diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/OpenIddict.Demo.Server.csproj b/modules/openiddict/app/OpenIddict.Demo.Server/OpenIddict.Demo.Server.csproj
index 09a185919a..2bc17b4f7c 100644
--- a/modules/openiddict/app/OpenIddict.Demo.Server/OpenIddict.Demo.Server.csproj
+++ b/modules/openiddict/app/OpenIddict.Demo.Server/OpenIddict.Demo.Server.csproj
@@ -68,6 +68,10 @@
runtime; build; native; contentfiles; analyzers
compile; contentFiles; build; buildMultitargeting; buildTransitive; analyzers; native
+
+ runtime; build; native; contentfiles; analyzers
+ compile; contentFiles; build; buildMultitargeting; buildTransitive; analyzers; native
+
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 3a9c8109fc..48496874d0 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
@@ -25,9 +25,16 @@ public class AbpOpenIddictAspNetCoreModule : AbpModule
Configure(options =>
{
+ options.ClaimsPrincipalHandlers.Add();
options.ClaimsPrincipalHandlers.Add();
});
+ var preActions = context.Services.GetPreConfigureActions();
+ Configure(options =>
+ {
+ preActions.Configure(options);
+ });
+
Configure(options =>
{
options.ViewLocationFormats.Add("/Volo/Abp/OpenIddict/Views/{1}/{0}.cshtml");
diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictOptions.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictOptions.cs
index 3339b6d376..9675d7ed6c 100644
--- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictOptions.cs
+++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictOptions.cs
@@ -25,4 +25,28 @@ public class AbpOpenIddictAspNetCoreOptions
/// Set the url of the select account page.
///
public string SelectAccountPage { get; set; } = "~/Account/SelectAccount";
+
+ ///
+ /// When set to true, the access token issued for the client_credentials grant
+ /// automatically includes the scopes configured on the client application (permissions
+ /// prefixed with oi_scp:) when the client does not explicitly request any scope.
+ /// Default: false.
+ ///
+ public bool UseDefaultScopesForClientCredentials { get; set; }
+
+ ///
+ /// When set to true, the access token issued for the password grant
+ /// automatically includes the scopes configured on the client application (permissions
+ /// prefixed with oi_scp:) when the client does not explicitly request any scope.
+ /// Default: false.
+ ///
+ public bool UseDefaultScopesForPassword { get; set; }
+
+ ///
+ /// When set to true, the access token issued for the urn:ietf:params:oauth:grant-type:token-exchange
+ /// grant automatically includes the scopes configured on the client application (permissions
+ /// prefixed with oi_scp:) when the client does not explicitly request any scope.
+ /// Default: false.
+ ///
+ public bool UseDefaultScopesForTokenExchange { get; set; }
}
diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Claims/AbpDefaultScopesHandler.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Claims/AbpDefaultScopesHandler.cs
new file mode 100644
index 0000000000..c047742e21
--- /dev/null
+++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Claims/AbpDefaultScopesHandler.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Extensions.Options;
+using OpenIddict.Abstractions;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.OpenIddict;
+
+public class AbpDefaultScopesHandler : IAbpOpenIddictClaimsPrincipalHandler, ITransientDependency
+{
+ public ILogger Logger { get; set; }
+ = NullLogger.Instance;
+
+ public async Task HandleAsync(AbpOpenIddictClaimsPrincipalHandlerContext context)
+ {
+ var options = context.ScopeServiceProvider
+ .GetRequiredService>().Value;
+
+ var request = context.OpenIddictRequest;
+ if (!IsDefaultScopesEnabled(request, options))
+ {
+ return;
+ }
+
+ if (!context.Principal.GetScopes().IsDefaultOrEmpty)
+ {
+ return;
+ }
+
+ var clientId = request.ClientId;
+ if (string.IsNullOrEmpty(clientId))
+ {
+ return;
+ }
+
+ var applicationManager = context.ScopeServiceProvider.GetRequiredService();
+ var scopeManager = context.ScopeServiceProvider.GetRequiredService();
+
+ var application = await applicationManager.FindByClientIdAsync(clientId);
+ if (application == null)
+ {
+ return;
+ }
+
+ var permissions = await applicationManager.GetPermissionsAsync(application);
+ var prefix = OpenIddictConstants.Permissions.Prefixes.Scope;
+
+ var scopes = permissions
+ .Where(p => p.StartsWith(prefix, StringComparison.Ordinal))
+ .Select(p => p[prefix.Length..])
+ .ToImmutableArray();
+
+ if (scopes.IsDefaultOrEmpty)
+ {
+ return;
+ }
+
+ Logger.LogDebug(
+ "Injecting default scopes for client {ClientId} (grant_type {GrantType}): {Scopes}",
+ clientId,
+ request.GrantType,
+ string.Join(", ", scopes));
+
+ context.Principal.SetScopes(scopes);
+
+ var resources = new List();
+ await foreach (var resource in scopeManager.ListResourcesAsync(scopes))
+ {
+ resources.Add(resource);
+ }
+
+ context.Principal.SetResources(resources);
+ }
+
+ protected virtual bool IsDefaultScopesEnabled(OpenIddictRequest request, AbpOpenIddictAspNetCoreOptions options)
+ {
+ if (request.IsClientCredentialsGrantType())
+ {
+ return options.UseDefaultScopesForClientCredentials;
+ }
+
+ if (request.IsPasswordGrantType())
+ {
+ return options.UseDefaultScopesForPassword;
+ }
+
+ if (request.IsTokenExchangeGrantType())
+ {
+ return options.UseDefaultScopesForTokenExchange;
+ }
+
+ return false;
+ }
+}