|
|
|
@ -38,6 +38,7 @@ namespace OpenIddict.Server |
|
|
|
NormalizeUserCode.Descriptor, |
|
|
|
ValidateReferenceTokenIdentifier.Descriptor, |
|
|
|
ValidateIdentityModelToken.Descriptor, |
|
|
|
MapInternalClaims.Descriptor, |
|
|
|
RestoreReferenceTokenProperties.Descriptor, |
|
|
|
ValidatePrincipal.Descriptor, |
|
|
|
ValidateTokenEntry.Descriptor, |
|
|
|
@ -496,26 +497,79 @@ namespace OpenIddict.Server |
|
|
|
}); |
|
|
|
|
|
|
|
// Restore the claim destinations from the special oi_cl_dstn claim (represented as a dictionary/JSON object).
|
|
|
|
if (token.TryGetPayloadValue(Claims.Private.ClaimDestinations, out ImmutableDictionary<string, string[]> destinations)) |
|
|
|
if (token.TryGetPayloadValue(Claims.Private.ClaimDestinationsMap, out ImmutableDictionary<string, string[]> destinations)) |
|
|
|
{ |
|
|
|
context.Principal.SetDestinations(destinations); |
|
|
|
} |
|
|
|
|
|
|
|
if (context.Principal.HasTokenType(TokenTypeHints.AccessToken)) |
|
|
|
context.Logger.LogTrace("The token '{Token}' was successfully validated and the following claims " + |
|
|
|
"could be extracted: {Claims}.", context.Token, context.Principal.Claims); |
|
|
|
|
|
|
|
return default; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Contains the logic responsible of mapping internal claims used by OpenIddict.
|
|
|
|
/// </summary>
|
|
|
|
public class MapInternalClaims : IOpenIddictServerHandler<ProcessAuthenticationContext> |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
|
|
/// </summary>
|
|
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; } |
|
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>() |
|
|
|
.UseSingletonHandler<MapInternalClaims>() |
|
|
|
.SetOrder(ValidateIdentityModelToken.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) |
|
|
|
{ |
|
|
|
// Map the standardized "azp" and "scope" claims to their "oi_" equivalent so that
|
|
|
|
// the ClaimsPrincipal extensions exposed by OpenIddict return consistent results.
|
|
|
|
context.Principal.SetPresenters(context.Principal.GetClaims(Claims.AuthorizedParty)); |
|
|
|
throw new ArgumentNullException(nameof(context)); |
|
|
|
} |
|
|
|
|
|
|
|
// Note: starting in OpenIddict 3.0, the public "scope" claim is formatted
|
|
|
|
// as a unique space-separated string containing all the granted scopes.
|
|
|
|
// Visit https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-03 for more information.
|
|
|
|
context.Principal.SetScopes(context.Principal.GetClaim(Claims.Scope) |
|
|
|
?.Split(Separators.Space, StringSplitOptions.RemoveEmptyEntries)); |
|
|
|
if (context.Principal == null) |
|
|
|
{ |
|
|
|
return default; |
|
|
|
} |
|
|
|
|
|
|
|
context.Logger.LogTrace("The token '{Token}' was successfully validated and the following claims " + |
|
|
|
"could be extracted: {Claims}.", context.Token, context.Principal.Claims); |
|
|
|
if (!context.Principal.HasTokenType(TokenTypeHints.AccessToken)) |
|
|
|
{ |
|
|
|
return default; |
|
|
|
} |
|
|
|
|
|
|
|
// Map the standardized "azp" and "scope" claims to their "oi_" equivalent so that
|
|
|
|
// the ClaimsPrincipal extensions exposed by OpenIddict return consistent results.
|
|
|
|
if (!context.Principal.HasPresenter()) |
|
|
|
{ |
|
|
|
context.Principal.SetPresenters(context.Principal.GetClaims(Claims.AuthorizedParty)); |
|
|
|
} |
|
|
|
|
|
|
|
// 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-03 for more information.
|
|
|
|
if (!context.Principal.HasScope()) |
|
|
|
{ |
|
|
|
var scopes = context.Principal.GetClaims(Claims.Scope); |
|
|
|
if (scopes.Length == 1) |
|
|
|
{ |
|
|
|
scopes = scopes[0].Split(Separators.Space, StringSplitOptions.RemoveEmptyEntries).ToImmutableArray(); |
|
|
|
} |
|
|
|
|
|
|
|
context.Principal.SetScopes(scopes); |
|
|
|
} |
|
|
|
|
|
|
|
return default; |
|
|
|
} |
|
|
|
@ -548,7 +602,7 @@ namespace OpenIddict.Server |
|
|
|
.AddFilter<RequireDegradedModeDisabled>() |
|
|
|
.AddFilter<RequireTokenStorageEnabled>() |
|
|
|
.UseScopedHandler<RestoreReferenceTokenProperties>() |
|
|
|
.SetOrder(ValidateIdentityModelToken.Descriptor.Order + 1_000) |
|
|
|
.SetOrder(MapInternalClaims.Descriptor.Order + 1_000) |
|
|
|
.Build(); |
|
|
|
|
|
|
|
public async ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context) |
|
|
|
@ -1378,7 +1432,7 @@ namespace OpenIddict.Server |
|
|
|
|
|
|
|
// When the request is a verification request, don't flow the copy from the user code.
|
|
|
|
if (context.EndpointType == OpenIddictServerEndpointType.Verification && |
|
|
|
string.Equals(claims.Key, Claims.Private.Scopes, StringComparison.OrdinalIgnoreCase)) |
|
|
|
string.Equals(claims.Key, Claims.Private.Scope, StringComparison.OrdinalIgnoreCase)) |
|
|
|
{ |
|
|
|
continue; |
|
|
|
} |
|
|
|
@ -1763,8 +1817,8 @@ namespace OpenIddict.Server |
|
|
|
} |
|
|
|
|
|
|
|
// Never exclude the presenters and scope private claims.
|
|
|
|
if (string.Equals(claim.Type, Claims.Private.Presenters, StringComparison.OrdinalIgnoreCase) || |
|
|
|
string.Equals(claim.Type, Claims.Private.Scopes, StringComparison.OrdinalIgnoreCase)) |
|
|
|
if (string.Equals(claim.Type, Claims.Private.Presenter, StringComparison.OrdinalIgnoreCase) || |
|
|
|
string.Equals(claim.Type, Claims.Private.Scope, StringComparison.OrdinalIgnoreCase)) |
|
|
|
{ |
|
|
|
return true; |
|
|
|
} |
|
|
|
@ -2708,9 +2762,9 @@ namespace OpenIddict.Server |
|
|
|
// that are manually mapped to public standard azp/scope JWT claims.
|
|
|
|
var principal = context.AccessTokenPrincipal.Clone(claim => claim.Type switch |
|
|
|
{ |
|
|
|
Claims.Private.Presenters => false, |
|
|
|
Claims.Private.Scopes => false, |
|
|
|
Claims.Private.TokenId => false, |
|
|
|
Claims.Private.Presenter => false, |
|
|
|
Claims.Private.Scope => false, |
|
|
|
Claims.Private.TokenId => false, |
|
|
|
|
|
|
|
_ => true |
|
|
|
}); |
|
|
|
@ -2913,7 +2967,7 @@ namespace OpenIddict.Server |
|
|
|
{ |
|
|
|
descriptor.Claims = new Dictionary<string, object>(StringComparer.Ordinal) |
|
|
|
{ |
|
|
|
[Claims.Private.ClaimDestinations] = destinations |
|
|
|
[Claims.Private.ClaimDestinationsMap] = destinations |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
@ -3092,7 +3146,7 @@ namespace OpenIddict.Server |
|
|
|
{ |
|
|
|
descriptor.Claims = new Dictionary<string, object>(StringComparer.Ordinal) |
|
|
|
{ |
|
|
|
[Claims.Private.ClaimDestinations] = destinations |
|
|
|
[Claims.Private.ClaimDestinationsMap] = destinations |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
@ -3372,7 +3426,7 @@ namespace OpenIddict.Server |
|
|
|
{ |
|
|
|
descriptor.Claims = new Dictionary<string, object>(StringComparer.Ordinal) |
|
|
|
{ |
|
|
|
[Claims.Private.ClaimDestinations] = destinations |
|
|
|
[Claims.Private.ClaimDestinationsMap] = destinations |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
|