|
|
|
@ -34,6 +34,7 @@ namespace OpenIddict.Validation |
|
|
|
ValidateReferenceTokenIdentifier.Descriptor, |
|
|
|
ValidateIdentityModelToken.Descriptor, |
|
|
|
IntrospectToken.Descriptor, |
|
|
|
NormalizeScopeClaims.Descriptor, |
|
|
|
MapInternalClaims.Descriptor, |
|
|
|
RestoreReferenceTokenProperties.Descriptor, |
|
|
|
ValidatePrincipal.Descriptor, |
|
|
|
@ -349,6 +350,56 @@ namespace OpenIddict.Validation |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Contains the logic responsible of normalizing the scope claims stored in the tokens.
|
|
|
|
/// </summary>
|
|
|
|
public class NormalizeScopeClaims : IOpenIddictValidationHandler<ProcessAuthenticationContext> |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
|
|
/// </summary>
|
|
|
|
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } |
|
|
|
= OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>() |
|
|
|
.UseSingletonHandler<NormalizeScopeClaims>() |
|
|
|
.SetOrder(IntrospectToken.Descriptor.Order + 1_000) |
|
|
|
.Build(); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Processes the event.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="context">The context associated with the event to process.</param>
|
|
|
|
/// <returns>
|
|
|
|
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
|
|
|
|
/// </returns>
|
|
|
|
public ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context) |
|
|
|
{ |
|
|
|
if (context == null) |
|
|
|
{ |
|
|
|
throw new ArgumentNullException(nameof(context)); |
|
|
|
} |
|
|
|
|
|
|
|
if (context.Principal == null) |
|
|
|
{ |
|
|
|
return default; |
|
|
|
} |
|
|
|
|
|
|
|
// Note: in previous OpenIddict versions, scopes were represented as a JSON array
|
|
|
|
// and deserialized as multiple claims. In OpenIddict 3.0, the public "scope" claim
|
|
|
|
// is formatted as a unique space-separated string containing all the granted scopes.
|
|
|
|
// To ensure access tokens generated by previous versions are still correctly handled,
|
|
|
|
// both formats (unique space-separated string or multiple scope claims) must be supported.
|
|
|
|
// To achieve that, all the "scope" claims are combined into a single one containg all the values.
|
|
|
|
// Visit https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-04 for more information.
|
|
|
|
var scopes = context.Principal.GetClaims(Claims.Scope); |
|
|
|
if (scopes.Length > 1) |
|
|
|
{ |
|
|
|
context.Principal.SetClaim(Claims.Scope, string.Join(" ", scopes)); |
|
|
|
} |
|
|
|
|
|
|
|
return default; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Contains the logic responsible of mapping internal claims used by OpenIddict.
|
|
|
|
/// </summary>
|
|
|
|
@ -360,7 +411,7 @@ namespace OpenIddict.Validation |
|
|
|
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } |
|
|
|
= OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>() |
|
|
|
.UseSingletonHandler<MapInternalClaims>() |
|
|
|
.SetOrder(IntrospectToken.Descriptor.Order + 1_000) |
|
|
|
.SetOrder(NormalizeScopeClaims.Descriptor.Order + 1_000) |
|
|
|
.Build(); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -442,24 +493,14 @@ namespace OpenIddict.Validation |
|
|
|
} |
|
|
|
|
|
|
|
// In OpenIddict 3.0, the scopes granted to an application are stored in "oi_scp".
|
|
|
|
//
|
|
|
|
// Note: in previous OpenIddict versions, scopes were represented as a JSON array
|
|
|
|
// and deserialized as multiple claims. In OpenIddict 3.0, the public "scope" claim
|
|
|
|
// is formatted as a unique space-separated string containing all the granted scopes.
|
|
|
|
// To ensure access tokens generated by previous versions are still correctly handled,
|
|
|
|
// both formats (unique space-separated string or multiple scope claims) must be supported.
|
|
|
|
// Visit https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-04 for more information.
|
|
|
|
// If no such claim exists, try to infer them from the standard "scope" JWT claim,
|
|
|
|
// which is guaranteed to be a unique space-separated claim containing all the values.
|
|
|
|
if (!context.Principal.HasScope()) |
|
|
|
{ |
|
|
|
var scopes = context.Principal.GetClaims(Claims.Scope); |
|
|
|
if (scopes.Length == 1) |
|
|
|
{ |
|
|
|
scopes = scopes[0].Split(Separators.Space, StringSplitOptions.RemoveEmptyEntries).ToImmutableArray(); |
|
|
|
} |
|
|
|
|
|
|
|
if (scopes.Any()) |
|
|
|
var scope = context.Principal.GetClaim(Claims.Scope); |
|
|
|
if (!string.IsNullOrEmpty(scope)) |
|
|
|
{ |
|
|
|
context.Principal.SetScopes(scopes); |
|
|
|
context.Principal.SetScopes(scope.Split(Separators.Space, StringSplitOptions.RemoveEmptyEntries)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|