diff --git a/Directory.Packages.props b/Directory.Packages.props index 1f627b183d..eb2b5fc708 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -128,11 +128,11 @@ - - - - - + + + + + diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor.cs b/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor.cs index ec1428e8aa..10506545dd 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor.cs +++ b/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor.cs @@ -42,10 +42,10 @@ public partial class EntityActions : ComponentBase { Actions.Add(action); } - - private bool DisabledOrNoActions() + + protected virtual bool DisabledOrNoActions() { - return Disabled || !Actions.Any(t => t is { Visible: true, HasPermission: true }); + return Disabled || (Actions.Any() && Actions.All(t => !t.Visible || !t.HasPermission)); } protected override void OnInitialized() diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs index 333ae59e3f..4b8d71214b 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs @@ -47,7 +47,7 @@ public class AuthService : IAuthService, ITransientDependency return null; } - var url = $"{CliUrls.WwwAbpIo}api/license/login-info"; + var url = $"{CliUrls.AccountAbpIo}api/license/login-info"; var client = CliHttpClientFactory.CreateClient(); @@ -130,7 +130,7 @@ public class AuthService : IAuthService, ITransientDependency public async Task CheckMultipleOrganizationsAsync(string username) { - var url = $"{CliUrls.WwwAbpIo}api/license/check-multiple-organizations?username={username}"; + var url = $"{CliUrls.AccountAbpIo}api/license/check-multiple-organizations?username={username}"; var client = CliHttpClientFactory.CreateClient(); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliConsts.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliConsts.cs index d968ae6b12..78a36fe329 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliConsts.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliConsts.cs @@ -12,7 +12,7 @@ public static class CliConsts public static string GithubHttpClientName = "GithubHttpClient"; - public static string LogoutUrl = CliUrls.WwwAbpIo + "api/license/logout"; + public static string LogoutUrl = CliUrls.AccountAbpIo + "api/license/logout"; public static string LicenseCodePlaceHolder = @""; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/AbpIoApiKeyService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/AbpIoApiKeyService.cs index ffd0dfdde2..4cade89493 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/AbpIoApiKeyService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/AbpIoApiKeyService.cs @@ -51,7 +51,7 @@ public class AbpIoApiKeyService : IApiKeyService, ITransientDependency return _apiKeyResult; } - var url = $"{CliUrls.WwwAbpIo}api/license/api-key"; + var url = $"{CliUrls.AccountAbpIo}api/license/api-key"; var client = _cliHttpClientFactory.CreateClient(); using (var response = await client.GetHttpResponseMessageWithRetryAsync(url, CancellationTokenProvider.Token, _logger)) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs index f59c373984..7094604049 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs @@ -88,7 +88,7 @@ public class TemplateInfoProvider : ITemplateInfoProvider, ITransientDependency try { - var url = $"{CliUrls.WwwAbpIo}api/license/check-user"; + var url = $"{CliUrls.AccountAbpIo}api/license/check-user"; var client = _cliHttpClientFactory.CreateClient(); using (var response = await client.GetHttpResponseMessageWithRetryAsync(url, CancellationTokenProvider.Token, Logger)) diff --git a/latest-versions.json b/latest-versions.json index 21fbe41bf4..1d49e265f6 100644 --- a/latest-versions.json +++ b/latest-versions.json @@ -1,4 +1,13 @@ [ + { + "version": "9.2.2", + "releaseDate": "", + "type": "stable", + "message": "", + "leptonx": { + "version": "4.2.2" + } + }, { "version": "9.2.1", "releaseDate": "", diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogFeature.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogFeature.cs index 5571e776b1..2ef2e6a4e5 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogFeature.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogFeature.cs @@ -7,11 +7,11 @@ namespace Volo.CmsKit.Blogs; public class BlogFeature : FullAuditedAggregateRoot { - public Guid BlogId { get; protected set; } + public virtual Guid BlogId { get; protected set; } - public string FeatureName { get; protected set; } + public virtual string FeatureName { get; protected set; } - public bool IsEnabled { get; protected internal set; } + public virtual bool IsEnabled { get; protected internal set; } protected BlogFeature() { diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MarkedItems/UserMarkedItem.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MarkedItems/UserMarkedItem.cs index 24fddd1382..5fcdca4008 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MarkedItems/UserMarkedItem.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MarkedItems/UserMarkedItem.cs @@ -15,8 +15,8 @@ public class UserMarkedItem : BasicAggregateRoot, IHasCreationTime, IMustH public virtual DateTime CreationTime { get; set; } - public string EntityId { get; protected set; } - public string EntityType { get; protected set; } + public virtual string EntityId { get; protected set; } + public virtual string EntityType { get; protected set; } protected UserMarkedItem() { } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptor.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptor.cs index 6bbbdff675..d52a0b9355 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptor.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptor.cs @@ -7,15 +7,15 @@ namespace Volo.CmsKit.MediaDescriptors; public class MediaDescriptor : FullAuditedAggregateRoot, IMultiTenant { - public Guid? TenantId { get; protected set; } + public virtual Guid? TenantId { get; protected set; } - public string EntityType { get; protected set; } + public virtual string EntityType { get; protected set; } - public string Name { get; protected set; } + public virtual string Name { get; protected set; } - public string MimeType { get; protected set; } + public virtual string MimeType { get; protected set; } - public long Size { get; protected set; } + public virtual long Size { get; protected set; } protected MediaDescriptor() { diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Menus/MenuItem.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Menus/MenuItem.cs index 418bb19408..57faabbdec 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Menus/MenuItem.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Menus/MenuItem.cs @@ -17,31 +17,31 @@ public class MenuItem : AuditedAggregateRoot, IMultiTenant /// Presents another Id. /// If it's , then it's a root menu item. /// - public Guid? ParentId { get; set; } + public virtual Guid? ParentId { get; set; } [NotNull] - public string DisplayName { get; protected set; } + public virtual string DisplayName { get; protected set; } - public bool IsActive { get; set; } + public virtual bool IsActive { get; set; } [NotNull] - public string Url { get; protected set; } + public virtual string Url { get; protected set; } - public string Icon { get; set; } + public virtual string Icon { get; set; } - public int Order { get; set; } + public virtual int Order { get; set; } - public string Target { get; set; } + public virtual string Target { get; set; } - public string ElementId { get; set; } + public virtual string ElementId { get; set; } - public string CssClass { get; set; } + public virtual string CssClass { get; set; } - public Guid? PageId { get; protected set; } + public virtual Guid? PageId { get; protected set; } - public Guid? TenantId { get; protected set; } - - public string RequiredPermissionName { get; set; } + public virtual Guid? TenantId { get; protected set; } + + public virtual string RequiredPermissionName { get; set; } protected MenuItem() { diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/DocumentContributor.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/DocumentContributor.cs index 876686b5ff..2fb555fb50 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/DocumentContributor.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/DocumentContributor.cs @@ -5,15 +5,15 @@ namespace Volo.Docs.Documents { public class DocumentContributor : Entity { - public Guid DocumentId { get; set; } + public virtual Guid DocumentId { get; set; } - public string Username { get; set; } + public virtual string Username { get; set; } - public int CommitCount { get; set; } + public virtual int CommitCount { get; set; } - public string UserProfileUrl { get; set; } + public virtual string UserProfileUrl { get; set; } - public string AvatarUrl { get; set; } + public virtual string AvatarUrl { get; set; } protected DocumentContributor() { diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDefinitionRecord.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDefinitionRecord.cs index 20ba414b08..8a2fff0846 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDefinitionRecord.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDefinitionRecord.cs @@ -7,31 +7,31 @@ namespace Volo.Abp.FeatureManagement; public class FeatureDefinitionRecord : BasicAggregateRoot, IHasExtraProperties { - public string GroupName { get; set; } + public virtual string GroupName { get; set; } - public string Name { get; set; } + public virtual string Name { get; set; } - public string ParentName { get; set; } + public virtual string ParentName { get; set; } - public string DisplayName { get; set; } + public virtual string DisplayName { get; set; } - public string Description { get; set; } + public virtual string Description { get; set; } - public string DefaultValue { get; set; } + public virtual string DefaultValue { get; set; } - public bool IsVisibleToClients { get; set; } + public virtual bool IsVisibleToClients { get; set; } - public bool IsAvailableToHost { get; set; } + public virtual bool IsAvailableToHost { get; set; } /// /// Comma separated list of provider names. /// - public string AllowedProviders { get; set; } + public virtual string AllowedProviders { get; set; } /// /// Serialized string to store info about the ValueType. /// - public string ValueType { get; set; } // ToggleStringValueType + public virtual string ValueType { get; set; } // ToggleStringValueType public ExtraPropertyDictionary ExtraProperties { get; protected set; } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureGroupDefinitionRecord.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureGroupDefinitionRecord.cs index 61c7e60473..e50bd5e66d 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureGroupDefinitionRecord.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureGroupDefinitionRecord.cs @@ -6,11 +6,11 @@ namespace Volo.Abp.FeatureManagement; public class FeatureGroupDefinitionRecord : BasicAggregateRoot, IHasExtraProperties { - public string Name { get; set; } + public virtual string Name { get; set; } - public string DisplayName { get; set; } + public virtual string DisplayName { get; set; } - public ExtraPropertyDictionary ExtraProperties { get; protected set; } + public virtual ExtraPropertyDictionary ExtraProperties { get; protected set; } public FeatureGroupDefinitionRecord() { diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLog.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLog.cs index c801c263e6..5c0c08fae9 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLog.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLog.cs @@ -8,29 +8,29 @@ namespace Volo.Abp.Identity; public class IdentitySecurityLog : AggregateRoot, IMultiTenant { - public Guid? TenantId { get; protected set; } + public virtual Guid? TenantId { get; protected set; } - public string ApplicationName { get; protected set; } + public virtual string ApplicationName { get; protected set; } - public string Identity { get; protected set; } + public virtual string Identity { get; protected set; } - public string Action { get; protected set; } + public virtual string Action { get; protected set; } - public Guid? UserId { get; protected set; } + public virtual Guid? UserId { get; protected set; } - public string UserName { get; protected set; } + public virtual string UserName { get; protected set; } - public string TenantName { get; protected set; } + public virtual string TenantName { get; protected set; } - public string ClientId { get; protected set; } + public virtual string ClientId { get; protected set; } - public string CorrelationId { get; protected set; } + public virtual string CorrelationId { get; protected set; } - public string ClientIpAddress { get; protected set; } + public virtual string ClientIpAddress { get; protected set; } - public string BrowserInfo { get; protected set; } + public virtual string BrowserInfo { get; protected set; } - public DateTime CreationTime { get; protected set; } + public virtual DateTime CreationTime { get; protected set; } protected IdentitySecurityLog() { diff --git a/modules/openiddict/app/OpenIddict.Demo.Client.Console/Program.cs b/modules/openiddict/app/OpenIddict.Demo.Client.Console/Program.cs index 2e55cc24b1..87948a4cdc 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Client.Console/Program.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Client.Console/Program.cs @@ -75,6 +75,41 @@ Console.WriteLine("UserInfo: {0}", JsonSerializer.Serialize(JsonDocument.Parse(u })); Console.WriteLine(); +var tokenExchangeResponse = await client.RequestTokenExchangeTokenAsync(new TokenExchangeTokenRequest() +{ + Address = configuration.TokenEndpoint, + ClientId = clientId, + ClientSecret = clientSecret, + SubjectToken = refreshTokenResponse.AccessToken!, + SubjectTokenType = "urn:ietf:params:oauth:token-type:access_token", + Scope = "AbpAPI profile roles email phone offline_access", +}); + +if (tokenExchangeResponse.IsError) +{ + throw new Exception(tokenExchangeResponse.Error); +} + +Console.WriteLine("Token Exchange token: {0}", tokenExchangeResponse.AccessToken); +Console.WriteLine(); +Console.WriteLine("Token Exchange token: {0}", tokenExchangeResponse.RefreshToken); +Console.WriteLine(); + +userinfo = await client.GetUserInfoAsync(new UserInfoRequest() +{ + Address = configuration.UserInfoEndpoint, + Token = tokenExchangeResponse.AccessToken +}); +if (userinfo.IsError) +{ + throw new Exception(userinfo.Error); +} + +Console.WriteLine("Token Exchange UserInfo: {0}", JsonSerializer.Serialize(JsonDocument.Parse(userinfo.Raw), new JsonSerializerOptions +{ + WriteIndented = true +})); +Console.WriteLine(); var introspectionResponse = await client.IntrospectTokenAsync(new TokenIntrospectionRequest() { diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/EntityFrameworkCore/ServerDataSeedContributor.cs b/modules/openiddict/app/OpenIddict.Demo.Server/EntityFrameworkCore/ServerDataSeedContributor.cs index c67b2977ef..e1fd97136b 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/EntityFrameworkCore/ServerDataSeedContributor.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/EntityFrameworkCore/ServerDataSeedContributor.cs @@ -79,6 +79,7 @@ public class ServerDataSeedContributor : IDataSeedContributor, ITransientDepende OpenIddictConstants.Permissions.GrantTypes.RefreshToken, OpenIddictConstants.Permissions.GrantTypes.DeviceCode, OpenIddictConstants.Permissions.GrantTypes.ClientCredentials, + OpenIddictConstants.Permissions.GrantTypes.TokenExchange, OpenIddictConstants.Permissions.Prefixes.GrantType + MyTokenExtensionGrant.ExtensionGrantName, OpenIddictConstants.Permissions.ResponseTypes.Code, diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.Designer.cs b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250710090114_Initial.Designer.cs similarity index 99% rename from modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.Designer.cs rename to modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250710090114_Initial.Designer.cs index 5c4b03ed99..1ba6189674 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.Designer.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250710090114_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace OpenIddict.Demo.Server.Migrations { [DbContext(typeof(ServerDbContext))] - [Migration("20250630055813_Initial")] + [Migration("20250710090114_Initial")] partial class Initial { /// @@ -1164,8 +1164,8 @@ namespace OpenIddict.Demo.Server.Migrations .HasColumnType("nvarchar(400)"); b.Property("Type") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); b.HasKey("Id"); diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.cs b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250710090114_Initial.cs similarity index 99% rename from modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.cs rename to modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250710090114_Initial.cs index 77775a3324..a992bf78c2 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250710090114_Initial.cs @@ -645,7 +645,7 @@ namespace OpenIddict.Demo.Server.Migrations ReferenceId = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), Status = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), Subject = table.Column(type: "nvarchar(400)", maxLength: 400, nullable: true), - Type = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + Type = table.Column(type: "nvarchar(150)", maxLength: 150, nullable: true), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false) }, diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs index ff0eaf970c..b1caafb242 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs @@ -1161,8 +1161,8 @@ namespace OpenIddict.Demo.Server.Migrations .HasColumnType("nvarchar(400)"); b.Property("Type") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); b.HasKey("Id"); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs index dddc89d515..3a9c8109fc 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs @@ -79,7 +79,8 @@ public class AbpOpenIddictAspNetCoreModule : AbpModule .AllowClientCredentialsFlow() .AllowRefreshTokenFlow() .AllowDeviceAuthorizationFlow() - .AllowNoneFlow(); + .AllowNoneFlow() + .AllowTokenExchangeFlow(); builder.RegisterScopes(new[] { diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictErrors.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictErrors.cs new file mode 100644 index 0000000000..45422ad210 --- /dev/null +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictErrors.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.OpenIddict; + +public static class AbpOpenIddictErrors +{ + public const string AccountLocked = "account_locked"; + + public const string AccountInactive = "account_inactive"; +} diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs index a8350e22e5..6216571168 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs @@ -44,7 +44,7 @@ public class AuthorizeController : AbpOpenIdDictControllerBase var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme); if (result is not { Succeeded: true } || ((request.HasPromptValue(OpenIddictConstants.PromptValues.Login) || request.MaxAge is 0 || - (request.MaxAge != null && result.Properties?.IssuedUtc != null && + (request.MaxAge is not null && result.Properties?.IssuedUtc is not null && TimeProvider.System.GetUtcNow() - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) && TempData["IgnoreAuthenticationChallenge"] is null or false)) { diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs index 33a1a37845..5059c7c5ca 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs @@ -107,10 +107,13 @@ public partial class TokenController ClientId = request.ClientId }); + var errorCode = OpenIddictConstants.Errors.InvalidGrant; string errorDescription; + if (result.IsLockedOut) { Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", request.Username); + errorCode = AbpOpenIddictErrors.AccountLocked; errorDescription = "The user account has been locked out due to invalid login attempts. Please wait a while and try again."; } else if (result.IsNotAllowed) @@ -139,7 +142,8 @@ public partial class TokenController return await HandleConfirmUserAsync(request, user); } - errorDescription = "You are not allowed to login! Your account is inactive."; + errorCode = AbpOpenIddictErrors.AccountInactive; + errorDescription = "You are not allowed to login! Your account is inactive or needs to confirm your email/phone number."; } } else @@ -150,7 +154,7 @@ public partial class TokenController var properties = new AuthenticationProperties(new Dictionary { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, + [OpenIddictServerAspNetCoreConstants.Properties.Error] = errorCode, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = errorDescription }); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.TokenExchange.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.TokenExchange.cs new file mode 100644 index 0000000000..8f17a34be0 --- /dev/null +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.TokenExchange.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Security.Claims; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Mvc; +using Microsoft.IdentityModel.Tokens; +using OpenIddict.Abstractions; +using OpenIddict.Server.AspNetCore; + +namespace Volo.Abp.OpenIddict.Controllers; + +public partial class TokenController +{ + protected virtual async Task HandleTokenExchangeGrantTypeAsync(OpenIddictRequest request) + { + // Retrieve the claims principal stored in the subject token. + // + // Note: the principal may not represent a user (e.g if the token was issued during a client credentials token + // request and represents a client application): developers are strongly encouraged to ensure that the user + // and client identifiers are randomly generated so that a malicious client cannot impersonate a legit user. + // + // See https://datatracker.ietf.org/doc/html/rfc9068#SecurityConsiderations for more information. + var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + + // If available, retrieve the claims principal stored in the actor token. + var actor = result.Properties?.GetParameter(OpenIddictServerAspNetCoreConstants.Properties.ActorTokenPrincipal); + + // Retrieve the user profile corresponding to the subject token. + var user = await UserManager.FindByIdAsync(result.Principal!.GetClaim(OpenIddictConstants.Claims.Subject)!); + if (user is null) + { + return Forbid( + authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, + properties: new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The token is no longer valid." + })); + } + + // Ensure the user is still allowed to sign in. + if (!await PreSignInCheckAsync(user)) + { + return Forbid( + authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, + properties: new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in." + })); + } + + // Note: whether the identity represents a delegated or impersonated access (or any other + // model) is entirely up to the implementer: to support all scenarios, OpenIddict doesn't + // enforce any specific constraint on the identity used for the sign-in operation and only + // requires that the standard "act" and "may_act" claims be valid JSON objects if present. + + // Clear the dynamic claims cache. + await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId); + + // Create a new ClaimsPrincipal containing the claims that + // will be used to create an id_token, a token or a code. + var principal = await SignInManager.CreateUserPrincipalAsync(user); + + // Note: IdentityModel doesn't support serializing ClaimsIdentity.Actor to the + // standard "act" claim yet, which requires adding the "act" claim manually. + // + // For more information, see + // https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3219. + if (!string.IsNullOrEmpty(actor?.GetClaim(OpenIddictConstants.Claims.Subject)) && + !string.Equals(principal.GetClaim(OpenIddictConstants.Claims.Subject), actor.GetClaim(OpenIddictConstants.Claims.Subject), StringComparison.Ordinal)) + { + principal.SetClaim(OpenIddictConstants.Claims.Actor, new JsonObject + { + [OpenIddictConstants.Claims.Subject] = actor.GetClaim(OpenIddictConstants.Claims.Subject) + }); + } + + // Note: in this sample, the granted scopes match the requested scope + // but you may want to allow the user to uncheck specific scopes. + // For that, simply restrict the list of scopes before calling SetScopes. + principal.SetScopes(request.GetScopes()); + principal.SetResources(await GetResourcesAsync(request.GetScopes())); + + await OpenIddictClaimsPrincipalManager.HandleAsync(request, principal); + + // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. + return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } +} diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.cs index e3b3d10c39..e348ffe007 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.cs @@ -42,6 +42,11 @@ public partial class TokenController : AbpOpenIdDictControllerBase return await HandleClientCredentialsAsync(request); } + if (request.IsTokenExchangeGrantType()) + { + return await HandleTokenExchangeGrantTypeAsync(request); + } + var extensionGrantsOptions = HttpContext.RequestServices.GetRequiredService>(); var extensionTokenGrant = extensionGrantsOptions.Value.Find(request.GrantType); if (extensionTokenGrant != null) diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Tokens/OpenIddictTokenConsts.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Tokens/OpenIddictTokenConsts.cs index c0e2674911..847cf0ef7a 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Tokens/OpenIddictTokenConsts.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Tokens/OpenIddictTokenConsts.cs @@ -8,5 +8,5 @@ public class OpenIddictTokenConsts public static int SubjectMaxLength { get; set; } = 400; - public static int TypeMaxLength { get; set; } = 50; + public static int TypeMaxLength { get; set; } = 150; } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/AbpOpenIddictDomainModule.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/AbpOpenIddictDomainModule.cs index 1312ad5d8c..fd20f71012 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/AbpOpenIddictDomainModule.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/AbpOpenIddictDomainModule.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using OpenIddict.Abstractions; +using OpenIddict.Core; using Volo.Abp.BackgroundWorkers; using Volo.Abp.Caching; using Volo.Abp.DistributedLocking; @@ -65,15 +66,15 @@ public class AbpOpenIddictDomainModule : AbpModule .SetDefaultTokenEntity(); builder - .AddApplicationStore() - .AddAuthorizationStore() - .AddScopeStore() - .AddTokenStore(); - - builder.ReplaceApplicationManager(typeof(AbpApplicationManager)); - builder.ReplaceAuthorizationManager(typeof(AbpAuthorizationManager)); - builder.ReplaceScopeManager(typeof(AbpScopeManager)); - builder.ReplaceTokenManager(typeof(AbpTokenManager)); + .ReplaceApplicationStore() + .ReplaceAuthorizationStore() + .ReplaceScopeStore() + .ReplaceTokenStore(); + + builder.ReplaceApplicationManager(); + builder.ReplaceAuthorizationManager(); + builder.ReplaceScopeManager(); + builder.ReplaceTokenManager(); builder.Services.TryAddScoped(provider => (IAbpApplicationManager)provider.GetRequiredService()); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationManager.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationManager.cs index 99b44e4293..ea2283ea25 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationManager.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationManager.cs @@ -17,7 +17,7 @@ public class AbpApplicationManager : OpenIddictApplicationManager cache, [NotNull] ILogger logger, [NotNull] IOptionsMonitor options, - [NotNull] IOpenIddictApplicationStoreResolver resolver, + [NotNull] IOpenIddictApplicationStore resolver, AbpOpenIddictIdentifierConverter identifierConverter) : base(cache, logger, options, resolver) { diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Authorizations/AbpAuthorizationManager.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Authorizations/AbpAuthorizationManager.cs index d190192058..50ec2866ec 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Authorizations/AbpAuthorizationManager.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Authorizations/AbpAuthorizationManager.cs @@ -16,7 +16,7 @@ public class AbpAuthorizationManager : OpenIddictAuthorizationManager cache, [NotNull] [ItemNotNull] ILogger> logger, [NotNull] [ItemNotNull] IOptionsMonitor options, - [NotNull] IOpenIddictAuthorizationStoreResolver resolver, + [NotNull] IOpenIddictAuthorizationStore resolver, AbpOpenIddictIdentifierConverter identifierConverter) : base(cache, logger, options, resolver) { diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Scopes/AbpScopeManager.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Scopes/AbpScopeManager.cs index 76596c159b..0f89463733 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Scopes/AbpScopeManager.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Scopes/AbpScopeManager.cs @@ -16,7 +16,7 @@ public class AbpScopeManager : OpenIddictScopeManager [NotNull] [ItemNotNull] IOpenIddictScopeCache cache, [NotNull] [ItemNotNull] ILogger> logger, [NotNull] [ItemNotNull] IOptionsMonitor options, - [NotNull] IOpenIddictScopeStoreResolver resolver, + [NotNull] IOpenIddictScopeStore resolver, AbpOpenIddictIdentifierConverter identifierConverter) : base(cache, logger, options, resolver) { diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Tokens/AbpTokenManager.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Tokens/AbpTokenManager.cs index 7f4f967124..879d5b3f4f 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Tokens/AbpTokenManager.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Tokens/AbpTokenManager.cs @@ -16,7 +16,7 @@ public class AbpTokenManager : OpenIddictTokenManager [NotNull] [ItemNotNull] IOpenIddictTokenCache cache, [NotNull] [ItemNotNull] ILogger> logger, [NotNull] [ItemNotNull] IOptionsMonitor options, - [NotNull] IOpenIddictTokenStoreResolver resolver, + [NotNull] IOpenIddictTokenStore resolver, AbpOpenIddictIdentifierConverter identifierConverter) : base(cache, logger, options, resolver) {