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; + } +}