diff --git a/src/OpenIddict.Abstractions/OpenIddictConstants.cs b/src/OpenIddict.Abstractions/OpenIddictConstants.cs index 08fa8207..b64994f9 100644 --- a/src/OpenIddict.Abstractions/OpenIddictConstants.cs +++ b/src/OpenIddict.Abstractions/OpenIddictConstants.cs @@ -69,7 +69,6 @@ namespace OpenIddict.Abstractions public const string PreferredUsername = "preferred_username"; public const string Profile = "profile"; public const string Region = "region"; - public const string Resource = "resource"; public const string Role = "role"; public const string Scope = "scope"; public const string StreetAddress = "street_address"; @@ -97,6 +96,7 @@ namespace OpenIddict.Abstractions public const string Nonce = "oi_nce"; public const string RedirectUri = "oi_reduri"; public const string RefreshTokenLifetime = "oi_reft_lft"; + public const string Resource = "oi_rsrc"; public const string TokenUsage = "oi_tkn_use"; } } diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs index 1a9e6261..f53c040d 100644 --- a/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs +++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs @@ -1110,7 +1110,7 @@ namespace OpenIddict.Abstractions throw new ArgumentNullException(nameof(principal)); } - return ImmutableHashSet.CreateRange(StringComparer.Ordinal, principal.GetClaims(Claims.Resource)); + return ImmutableHashSet.CreateRange(StringComparer.Ordinal, principal.GetClaims(Claims.Private.Resource)); } /// @@ -1423,7 +1423,7 @@ namespace OpenIddict.Abstractions throw new ArgumentNullException(nameof(principal)); } - return principal.FindAll(Claims.Resource).Any(); + return principal.FindAll(Claims.Private.Resource).Any(); } /// @@ -1611,7 +1611,7 @@ namespace OpenIddict.Abstractions throw new ArgumentNullException(nameof(principal)); } - return principal.SetClaims(Claims.Resource, resources.Distinct(StringComparer.Ordinal)); + return principal.SetClaims(Claims.Private.Resource, resources.Distinct(StringComparer.Ordinal)); } /// diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs index a66120b8..fabd75b0 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs @@ -43,6 +43,7 @@ namespace OpenIddict.Server ValidateSigninResponse.Descriptor, AttachDefaultScopes.Descriptor, AttachDefaultPresenters.Descriptor, + InferResources.Descriptor, EvaluateReturnedTokens.Descriptor, AttachAccessToken.Descriptor, AttachAuthorizationCode.Descriptor, @@ -300,6 +301,47 @@ namespace OpenIddict.Server } } + /// + /// Contains the logic responsible of inferring resources from the audience claims if necessary. + /// + public class InferResources : IOpenIddictServerHandler + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictServerHandlerDescriptor Descriptor { get; } + = OpenIddictServerHandlerDescriptor.CreateBuilder() + .UseSingletonHandler() + .SetOrder(AttachDefaultPresenters.Descriptor.Order + 1_000) + .Build(); + + /// + /// Processes the event. + /// + /// The context associated with the event to process. + /// + /// A that can be used to monitor the asynchronous operation. + /// + public ValueTask HandleAsync([NotNull] ProcessSigninContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + // When a "resources" property cannot be found in the ticket, infer it from the "audiences" property. + if (context.Principal.HasAudience() && !context.Principal.HasResource()) + { + context.Principal.SetResources(context.Principal.GetAudiences()); + } + + // Reset the audiences collection, as it's later set, based on the token type. + context.Principal.SetAudiences(Array.Empty()); + + return default; + } + } + /// /// Contains the logic responsible of selecting the token types returned to the client application. /// @@ -311,7 +353,7 @@ namespace OpenIddict.Server public static OpenIddictServerHandlerDescriptor Descriptor { get; } = OpenIddictServerHandlerDescriptor.CreateBuilder() .UseSingletonHandler() - .SetOrder(AttachDefaultPresenters.Descriptor.Order + 1_000) + .SetOrder(InferResources.Descriptor.Order + 1_000) .Build(); /// @@ -448,6 +490,12 @@ namespace OpenIddict.Server return true; }); + // Remove the destinations from the claim properties. + foreach (var claim in principal.Claims) + { + claim.Properties.Remove(OpenIddictConstants.Properties.Destinations); + } + principal.SetTokenId(Guid.NewGuid().ToString()).SetCreationDate(DateTimeOffset.UtcNow); var lifetime = context.Principal.GetAccessTokenLifetime() ?? context.Options.AccessTokenLifetime; @@ -456,11 +504,8 @@ namespace OpenIddict.Server principal.SetExpirationDate(principal.GetCreationDate() + lifetime.Value); } - // Remove the destinations from the claim properties. - foreach (var claim in principal.Claims) - { - claim.Properties.Remove(OpenIddictConstants.Properties.Destinations); - } + // Set the audiences collection using the private resource claims stored in the principal. + principal.SetAudiences(context.Principal.GetResources()); // When receiving a grant_type=refresh_token request, determine whether the client application // requests a limited set of scopes and immediately replace the scopes collection if necessary. @@ -702,14 +747,14 @@ namespace OpenIddict.Server return true; }); - principal.SetTokenId(Guid.NewGuid().ToString()).SetCreationDate(DateTimeOffset.UtcNow); - // Remove the destinations from the claim properties. foreach (var claim in principal.Claims) { claim.Properties.Remove(OpenIddictConstants.Properties.Destinations); } + principal.SetTokenId(Guid.NewGuid().ToString()).SetCreationDate(DateTimeOffset.UtcNow); + var lifetime = context.Principal.GetIdentityTokenLifetime() ?? context.Options.IdentityTokenLifetime; if (lifetime.HasValue) {