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)
{