From 6d81e93ccb5743cdecdfab61f6da46b7bbf8bf29 Mon Sep 17 00:00:00 2001 From: Ali Hassan <97635301+alihdev@users.noreply.github.com> Date: Tue, 3 Sep 2024 13:39:57 +0300 Subject: [PATCH 01/10] Add specific error codes for Authentication failures in OpenIddict TokenController.Password --- .../Abp/OpenIddict/Controllers/TokenController.Password.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 470debf115..40747a2fcc 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 @@ -108,10 +108,13 @@ public partial class TokenController }); string errorDescription; + string errorCode; + if (result.IsLockedOut) { Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", request.Username); errorDescription = "The user account has been locked out due to invalid login attempts. Please wait a while and try again."; + errorCode = "account_locked"; } else if (result.IsNotAllowed) { @@ -128,16 +131,18 @@ public partial class TokenController } errorDescription = "You are not allowed to login! Your account is inactive or needs to confirm your email/phone number."; + errorCode = "account_inactive"; } else { Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", request.Username); errorDescription = "Invalid username or password!"; + errorCode = OpenIddictConstants.Errors.InvalidGrant; } var properties = new AuthenticationProperties(new Dictionary { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, + [OpenIddictServerAspNetCoreConstants.Properties.Error] = errorCode, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = errorDescription }); From 09229dfb7060f7725228bf8213c75d444acf6f3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Ali=20=C3=96ZKAYA?= Date: Mon, 30 Jun 2025 18:05:19 +0300 Subject: [PATCH 02/10] Update login API URLs to use `AccountAbpIo` --- .../src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs | 4 ++-- framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliConsts.cs | 2 +- .../Volo/Abp/Cli/Licensing/AbpIoApiKeyService.cs | 2 +- .../Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) 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)) From 85392e10c7fc53b283cff4fe93aef541da00a6fb Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 10 Jul 2025 17:02:29 +0800 Subject: [PATCH 03/10] Update `OpenIddict` to 7.0.0. --- Directory.Packages.props | 10 +++++----- ....cs => 20250710090114_Initial.Designer.cs} | 6 +++--- ...3_Initial.cs => 20250710090114_Initial.cs} | 2 +- .../ServerDbContextModelSnapshot.cs | 4 ++-- .../Tokens/OpenIddictTokenConsts.cs | 2 +- .../OpenIddict/AbpOpenIddictDomainModule.cs | 19 ++++++++++--------- .../Applications/AbpApplicationManager.cs | 2 +- .../Authorizations/AbpAuthorizationManager.cs | 2 +- .../Abp/OpenIddict/Scopes/AbpScopeManager.cs | 2 +- .../Abp/OpenIddict/Tokens/AbpTokenManager.cs | 2 +- 10 files changed, 26 insertions(+), 25 deletions(-) rename modules/openiddict/app/OpenIddict.Demo.Server/Migrations/{20250630055813_Initial.Designer.cs => 20250710090114_Initial.Designer.cs} (99%) rename modules/openiddict/app/OpenIddict.Demo.Server/Migrations/{20250630055813_Initial.cs => 20250710090114_Initial.cs} (99%) diff --git a/Directory.Packages.props b/Directory.Packages.props index 5d9885ddbd..afcb39eb77 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -128,11 +128,11 @@ - - - - - + + + + + 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.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) { From e489ced990a437085615f059d1ed1ec6f252e64d Mon Sep 17 00:00:00 2001 From: selman koc <64414348+skoc10@users.noreply.github.com> Date: Thu, 10 Jul 2025 17:32:39 +0300 Subject: [PATCH 04/10] Update latest-versions.json --- latest-versions.json | 9 +++++++++ 1 file changed, 9 insertions(+) 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": "", From 85913991af5a9c95413af3518e4355453df827f5 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 11 Jul 2025 09:57:43 +0800 Subject: [PATCH 05/10] Improve EntityActions initialization and action check https://abp.io/support/questions/9597/ --- .../Components/EntityActions.razor.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor.cs b/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor.cs index ec1428e8aa..d4ec097192 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor.cs +++ b/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor.cs @@ -38,20 +38,23 @@ public partial class EntityActions : ComponentBase [Inject] public IStringLocalizer UiLocalizer { get; set; } = default!; + public bool Initialized { get; set; } + internal void AddAction(EntityAction action) { Actions.Add(action); } - - private bool DisabledOrNoActions() + + protected virtual bool DisabledOrNoActions() { - return Disabled || !Actions.Any(t => t is { Visible: true, HasPermission: true }); + return !Initialized && (Disabled || Actions.All(t => !t.Visible || !t.HasPermission)); } protected override void OnInitialized() { base.OnInitialized(); ToggleText = UiLocalizer["Actions"]; + Initialized = true; } protected async override Task OnAfterRenderAsync(bool firstRender) From ab1d5ec8fa0fc9faefd69200787728df6ec2b48a Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 11 Jul 2025 10:18:29 +0800 Subject: [PATCH 06/10] Refactor EntityActions initialization and disabled logic --- .../Volo.Abp.BlazoriseUI/Components/EntityActions.razor.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor.cs b/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor.cs index d4ec097192..10506545dd 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor.cs +++ b/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor.cs @@ -38,8 +38,6 @@ public partial class EntityActions : ComponentBase [Inject] public IStringLocalizer UiLocalizer { get; set; } = default!; - public bool Initialized { get; set; } - internal void AddAction(EntityAction action) { Actions.Add(action); @@ -47,14 +45,13 @@ public partial class EntityActions : ComponentBase protected virtual bool DisabledOrNoActions() { - return !Initialized && (Disabled || Actions.All(t => !t.Visible || !t.HasPermission)); + return Disabled || (Actions.Any() && Actions.All(t => !t.Visible || !t.HasPermission)); } protected override void OnInitialized() { base.OnInitialized(); ToggleText = UiLocalizer["Actions"]; - Initialized = true; } protected async override Task OnAfterRenderAsync(bool firstRender) From 6ced2e8a348e8b926a5f5a15f3001c089c8ed4d4 Mon Sep 17 00:00:00 2001 From: maliming Date: Sat, 12 Jul 2025 11:13:59 +0800 Subject: [PATCH 07/10] Implement Token Exchange Grant Type support --- .../OpenIddict.Demo.Client.Console/Program.cs | 35 +++++++ .../ServerDataSeedContributor.cs | 1 + .../AbpOpenIddictAspNetCoreModule.cs | 3 +- .../Controllers/AuthorizeController.cs | 2 +- .../TokenController.TokenExchange.cs | 92 +++++++++++++++++++ .../OpenIddict/Controllers/TokenController.cs | 5 + 6 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.TokenExchange.cs 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/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/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.TokenExchange.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.TokenExchange.cs new file mode 100644 index 0000000000..5ad1752d73 --- /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 SignInManager.CanSignInAsync(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) From 7c5dc4858825badfafc545f1f5350f71df5a64a1 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 13 Jul 2025 10:03:44 +0800 Subject: [PATCH 08/10] Use PreSignInCheckAsync in token exchange flow --- .../Abp/OpenIddict/Controllers/TokenController.TokenExchange.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 5ad1752d73..8f17a34be0 100644 --- 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 @@ -41,7 +41,7 @@ public partial class TokenController } // Ensure the user is still allowed to sign in. - if (!await SignInManager.CanSignInAsync(user)) + if (!await PreSignInCheckAsync(user)) { return Forbid( authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, From 8def47c444af9e6ff49fcd27f2997a2859cbb55f Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 14 Jul 2025 15:20:57 +0800 Subject: [PATCH 09/10] refactor: update all entity properties to `virtual` --- .../Volo/CmsKit/Blogs/BlogFeature.cs | 6 ++--- .../Volo/CmsKit/MarkedItems/UserMarkedItem.cs | 4 +-- .../MediaDescriptors/MediaDescriptor.cs | 10 +++---- .../Volo/CmsKit/Menus/MenuItem.cs | 26 +++++++++---------- .../Docs/Documents/DocumentContributor.cs | 10 +++---- .../FeatureDefinitionRecord.cs | 20 +++++++------- .../FeatureGroupDefinitionRecord.cs | 6 ++--- .../Volo/Abp/Identity/IdentitySecurityLog.cs | 24 ++++++++--------- 8 files changed, 53 insertions(+), 53 deletions(-) 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() { From aecc051fcbfffbb53c03c1def79bcf291947d19f Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 15 Jul 2025 16:30:08 +0800 Subject: [PATCH 10/10] Add custom error codes for account lock and inactivity --- .../Volo/Abp/OpenIddict/AbpOpenIddictErrors.cs | 8 ++++++++ .../OpenIddict/Controllers/TokenController.Password.cs | 10 ++++------ 2 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictErrors.cs 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/TokenController.Password.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs index 91cb57cdb4..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,14 +107,14 @@ public partial class TokenController ClientId = request.ClientId }); + var errorCode = OpenIddictConstants.Errors.InvalidGrant; string errorDescription; - string errorCode; - + 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."; - errorCode = "account_locked"; } else if (result.IsNotAllowed) { @@ -127,7 +127,6 @@ public partial class TokenController { Logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", request.Username); - if (user.ShouldChangePasswordOnNextLogin) { return await HandleShouldChangePasswordOnNextLoginAsync(request, user, request.Password); @@ -143,15 +142,14 @@ public partial class TokenController return await HandleConfirmUserAsync(request, user); } + errorCode = AbpOpenIddictErrors.AccountInactive; errorDescription = "You are not allowed to login! Your account is inactive or needs to confirm your email/phone number."; - errorCode = "account_inactive"; } } else { Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", request.Username); errorDescription = "Invalid username or password!"; - errorCode = OpenIddictConstants.Errors.InvalidGrant; } var properties = new AuthenticationProperties(new Dictionary