|
|
|
@ -188,6 +188,8 @@ namespace OpenIddict.Server |
|
|
|
OpenIddictServerEndpointType.Authorization => (context.Request.IdTokenHint, TokenUsages.IdToken), |
|
|
|
OpenIddictServerEndpointType.Logout => (context.Request.IdTokenHint, TokenUsages.IdToken), |
|
|
|
|
|
|
|
// Generic tokens received by the introspection and revocation can be of any type.
|
|
|
|
// Additional token type filtering is made by the endpoint themselves, if needed.
|
|
|
|
OpenIddictServerEndpointType.Introspection => (context.Request.Token, null), |
|
|
|
OpenIddictServerEndpointType.Revocation => (context.Request.Token, null), |
|
|
|
|
|
|
|
@ -334,6 +336,7 @@ namespace OpenIddict.Server |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// If the type associated with the token entry doesn't match the expected type, return an error.
|
|
|
|
if (!string.IsNullOrEmpty(context.TokenType) && |
|
|
|
!string.Equals(context.TokenType, await _tokenManager.GetTypeAsync(token))) |
|
|
|
{ |
|
|
|
@ -416,41 +419,18 @@ namespace OpenIddict.Server |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// If the token cannot be validated, don't return an error to allow another handle to validate it.
|
|
|
|
var result = !string.IsNullOrEmpty(context.TokenType) ? |
|
|
|
await ValidateTokenAsync(context.Token, context.TokenType) : |
|
|
|
await ValidateAnyTokenAsync(context.Token); |
|
|
|
if (result.ClaimsIdentity == null) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
var parameters = context.Options.TokenValidationParameters.Clone(); |
|
|
|
parameters.ValidIssuer = context.Issuer?.AbsoluteUri; |
|
|
|
parameters.IssuerSigningKeys = context.Options.SigningCredentials.Select(credentials => credentials.Key); |
|
|
|
parameters.TokenDecryptionKeys = context.Options.EncryptionCredentials.Select(credentials => credentials.Key); |
|
|
|
|
|
|
|
// Attach the principal extracted from the token to the parent event context.
|
|
|
|
context.Principal = new ClaimsPrincipal(result.ClaimsIdentity); |
|
|
|
|
|
|
|
// Store the token type as a special private claim.
|
|
|
|
context.Principal.SetClaim(Claims.Private.TokenUsage, ((JsonWebToken) result.SecurityToken).Typ switch |
|
|
|
// If a specific token type is expected, override the default valid types to reject
|
|
|
|
// security tokens whose "typ" header doesn't match the expected token type.
|
|
|
|
if (!string.IsNullOrEmpty(context.TokenType)) |
|
|
|
{ |
|
|
|
JsonWebTokenTypes.AccessToken => TokenUsages.AccessToken, |
|
|
|
JsonWebTokenTypes.IdentityToken => TokenUsages.IdToken, |
|
|
|
|
|
|
|
JsonWebTokenTypes.Private.AuthorizationCode => TokenUsages.AuthorizationCode, |
|
|
|
JsonWebTokenTypes.Private.DeviceCode => TokenUsages.DeviceCode, |
|
|
|
JsonWebTokenTypes.Private.RefreshToken => TokenUsages.RefreshToken, |
|
|
|
JsonWebTokenTypes.Private.UserCode => TokenUsages.UserCode, |
|
|
|
|
|
|
|
_ => throw new InvalidOperationException("The token type is not supported.") |
|
|
|
}); |
|
|
|
|
|
|
|
context.Logger.LogTrace("The token '{Token}' was successfully validated and the following claims " + |
|
|
|
"could be extracted: {Claims}.", context.Token, context.Principal.Claims); |
|
|
|
|
|
|
|
async ValueTask<TokenValidationResult> ValidateTokenAsync(string token, string type) |
|
|
|
{ |
|
|
|
var parameters = context.Options.TokenValidationParameters.Clone(); |
|
|
|
parameters.ValidTypes = new[] |
|
|
|
{ |
|
|
|
type switch |
|
|
|
context.TokenType switch |
|
|
|
{ |
|
|
|
TokenUsages.AccessToken => JsonWebTokenTypes.AccessToken, |
|
|
|
TokenUsages.IdToken => JsonWebTokenTypes.IdentityToken, |
|
|
|
@ -463,74 +443,36 @@ namespace OpenIddict.Server |
|
|
|
_ => throw new InvalidOperationException("The token type is not supported.") |
|
|
|
} |
|
|
|
}; |
|
|
|
parameters.ValidIssuer = context.Issuer?.AbsoluteUri; |
|
|
|
|
|
|
|
parameters.IssuerSigningKeys = type switch |
|
|
|
{ |
|
|
|
TokenUsages.AccessToken => context.Options.SigningCredentials.Select(credentials => credentials.Key), |
|
|
|
TokenUsages.AuthorizationCode => context.Options.SigningCredentials.Select(credentials => credentials.Key), |
|
|
|
TokenUsages.DeviceCode => context.Options.SigningCredentials.Select(credentials => credentials.Key), |
|
|
|
TokenUsages.RefreshToken => context.Options.SigningCredentials.Select(credentials => credentials.Key), |
|
|
|
TokenUsages.UserCode => context.Options.SigningCredentials.Select(credentials => credentials.Key), |
|
|
|
|
|
|
|
TokenUsages.IdToken => context.Options.SigningCredentials |
|
|
|
.Select(credentials => credentials.Key) |
|
|
|
.OfType<AsymmetricSecurityKey>(), |
|
|
|
|
|
|
|
_ => Array.Empty<SecurityKey>() |
|
|
|
}; |
|
|
|
|
|
|
|
parameters.TokenDecryptionKeys = type switch |
|
|
|
{ |
|
|
|
TokenUsages.AuthorizationCode => context.Options.EncryptionCredentials.Select(credentials => credentials.Key), |
|
|
|
TokenUsages.DeviceCode => context.Options.EncryptionCredentials.Select(credentials => credentials.Key), |
|
|
|
TokenUsages.RefreshToken => context.Options.EncryptionCredentials.Select(credentials => credentials.Key), |
|
|
|
TokenUsages.UserCode => context.Options.EncryptionCredentials.Select(credentials => credentials.Key), |
|
|
|
|
|
|
|
TokenUsages.AccessToken => context.Options.EncryptionCredentials |
|
|
|
.Select(credentials => credentials.Key) |
|
|
|
.Where(key => key is SymmetricSecurityKey), |
|
|
|
|
|
|
|
_ => Array.Empty<SecurityKey>() |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
var result = await context.Options.JsonWebTokenHandler.ValidateTokenStringAsync(token, parameters); |
|
|
|
if (!result.IsValid) |
|
|
|
{ |
|
|
|
context.Logger.LogTrace(result.Exception, "An error occurred while validating the token '{Token}'.", token); |
|
|
|
} |
|
|
|
// If the token cannot be validated, don't return an error to allow another handle to validate it.
|
|
|
|
var result = await context.Options.JsonWebTokenHandler.ValidateTokenStringAsync(context.Token, parameters); |
|
|
|
if (result.ClaimsIdentity == null || !result.IsValid) |
|
|
|
{ |
|
|
|
context.Logger.LogTrace(result.Exception, "An error occurred while validating the token '{Token}'.", context.Token); |
|
|
|
|
|
|
|
return result; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
async ValueTask<TokenValidationResult> ValidateAnyTokenAsync(string token) |
|
|
|
{ |
|
|
|
var result = await ValidateTokenAsync(token, TokenUsages.AccessToken); |
|
|
|
if (result.IsValid) |
|
|
|
{ |
|
|
|
return result; |
|
|
|
} |
|
|
|
// Attach the principal extracted from the token to the parent event context.
|
|
|
|
context.Principal = new ClaimsPrincipal(result.ClaimsIdentity); |
|
|
|
|
|
|
|
result = await ValidateTokenAsync(token, TokenUsages.RefreshToken); |
|
|
|
if (result.IsValid) |
|
|
|
{ |
|
|
|
return result; |
|
|
|
} |
|
|
|
// Store the token type as a special private claim.
|
|
|
|
context.Principal.SetClaim(Claims.Private.TokenUsage, ((JsonWebToken) result.SecurityToken).Typ switch |
|
|
|
{ |
|
|
|
JsonWebTokenTypes.AccessToken => TokenUsages.AccessToken, |
|
|
|
JsonWebTokenTypes.IdentityToken => TokenUsages.IdToken, |
|
|
|
|
|
|
|
result = await ValidateTokenAsync(token, TokenUsages.AuthorizationCode); |
|
|
|
if (result.IsValid) |
|
|
|
{ |
|
|
|
return result; |
|
|
|
} |
|
|
|
JsonWebTokenTypes.Private.AuthorizationCode => TokenUsages.AuthorizationCode, |
|
|
|
JsonWebTokenTypes.Private.DeviceCode => TokenUsages.DeviceCode, |
|
|
|
JsonWebTokenTypes.Private.RefreshToken => TokenUsages.RefreshToken, |
|
|
|
JsonWebTokenTypes.Private.UserCode => TokenUsages.UserCode, |
|
|
|
|
|
|
|
result = await ValidateTokenAsync(token, TokenUsages.IdToken); |
|
|
|
if (result.IsValid) |
|
|
|
{ |
|
|
|
return result; |
|
|
|
} |
|
|
|
_ => throw new InvalidOperationException("The token type is not supported.") |
|
|
|
}); |
|
|
|
|
|
|
|
return new TokenValidationResult { IsValid = false }; |
|
|
|
} |
|
|
|
context.Logger.LogTrace("The token '{Token}' was successfully validated and the following claims " + |
|
|
|
"could be extracted: {Claims}.", context.Token, context.Principal.Claims); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|