diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs index 3132ad2863..4f413738c6 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs @@ -527,6 +527,28 @@ public virtual TokenValidationResult ValidateToken(string token, TokenValidation /// if the validationParameters.TokenReader delegate is not able to parse/read the token as a valid , /// public override async Task ValidateTokenAsync(string token, TokenValidationParameters validationParameters) + { + return await ValidateTokenAsync(token, validationParameters, CancellationToken.None).ConfigureAwait(false); + } + + /// + /// Validates a token. + /// On a validation failure, no exception will be thrown; instead, the exception will be set in the returned TokenValidationResult.Exception property. + /// Callers should always check the TokenValidationResult.IsValid property to verify the validity of the result. + /// + /// The token to be validated. + /// A required for validation. + /// Propagates notification that operations should be canceled. + /// A + /// + /// TokenValidationResult.Exception will be set to one of the following exceptions if the is invalid. + /// if is null or empty. + /// if is null. + /// 'token.Length' is greater than . + /// if is not a valid , + /// if the validationParameters.TokenReader delegate is not able to parse/read the token as a valid , + /// + internal override async Task ValidateTokenAsync(string token, TokenValidationParameters validationParameters, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(token)) return new TokenValidationResult { Exception = LogHelper.LogArgumentNullException(nameof(token)), IsValid = false }; @@ -537,11 +559,12 @@ public override async Task ValidateTokenAsync(string toke if (token.Length > MaximumTokenSizeInBytes) return new TokenValidationResult { Exception = LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(TokenLogMessages.IDX10209, LogHelper.MarkAsNonPII(token.Length), LogHelper.MarkAsNonPII(MaximumTokenSizeInBytes)))), IsValid = false }; +#pragma warning disable CA1031 try { TokenValidationResult result = ReadToken(token, validationParameters); if (result.IsValid) - return await ValidateTokenAsync(result.SecurityToken, validationParameters).ConfigureAwait(false); + return await ValidateTokenAsync(result.SecurityToken, validationParameters, cancellationToken).ConfigureAwait(false); return result; } @@ -553,10 +576,17 @@ public override async Task ValidateTokenAsync(string toke IsValid = false }; } +#pragma warning restore CA1031 } /// public override async Task ValidateTokenAsync(SecurityToken token, TokenValidationParameters validationParameters) + { + return await ValidateTokenAsync(token, validationParameters, CancellationToken.None).ConfigureAwait(false); + } + + /// + internal override async Task ValidateTokenAsync(SecurityToken token, TokenValidationParameters validationParameters, CancellationToken cancellationToken) { if (token == null) throw LogHelper.LogArgumentNullException(nameof(token)); @@ -570,8 +600,9 @@ public override async Task ValidateTokenAsync(SecurityTok try { - return await ValidateTokenAsync(jwt, validationParameters).ConfigureAwait(false); + return await ValidateTokenAsync(jwt, validationParameters, cancellationToken).ConfigureAwait(false); } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) { return new TokenValidationResult @@ -580,6 +611,7 @@ public override async Task ValidateTokenAsync(SecurityTok IsValid = false }; } +#pragma warning restore CA1031 // Do not catch general exception types } /// @@ -635,15 +667,16 @@ private static TokenValidationResult ReadToken(string token, TokenValidationPara /// /// The JWT token /// The to be used for validation. + /// Propagates notification that operations should be canceled. /// - private async ValueTask ValidateTokenAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters) + private async ValueTask ValidateTokenAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, CancellationToken cancellationToken) { BaseConfiguration currentConfiguration = null; if (validationParameters.ConfigurationManager != null) { try { - currentConfiguration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false); + currentConfiguration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(cancellationToken).ConfigureAwait(false); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) @@ -656,7 +689,7 @@ private async ValueTask ValidateTokenAsync(JsonWebToken j } } - TokenValidationResult tokenValidationResult = await ValidateTokenAsync(jsonWebToken, validationParameters, currentConfiguration).ConfigureAwait(false); + TokenValidationResult tokenValidationResult = await ValidateTokenAsync(jsonWebToken, validationParameters, currentConfiguration, cancellationToken).ConfigureAwait(false); if (validationParameters.ConfigurationManager != null) { if (tokenValidationResult.IsValid) @@ -678,12 +711,12 @@ private async ValueTask ValidateTokenAsync(JsonWebToken j validationParameters.ConfigurationManager.RequestRefresh(); validationParameters.RefreshBeforeValidation = true; var lastConfig = currentConfiguration; - currentConfiguration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false); + currentConfiguration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(cancellationToken).ConfigureAwait(false); // Only try to re-validate using the newly obtained config if it doesn't reference equal the previously used configuration. if (lastConfig != currentConfiguration) { - tokenValidationResult = await ValidateTokenAsync(jsonWebToken, validationParameters, currentConfiguration).ConfigureAwait(false); + tokenValidationResult = await ValidateTokenAsync(jsonWebToken, validationParameters, currentConfiguration, cancellationToken).ConfigureAwait(false); if (tokenValidationResult.IsValid) { @@ -703,7 +736,7 @@ private async ValueTask ValidateTokenAsync(JsonWebToken j { if (!lkgConfiguration.Equals(currentConfiguration) && TokenUtilities.IsRecoverableConfiguration(jsonWebToken.Kid, currentConfiguration, lkgConfiguration, recoverableException)) { - tokenValidationResult = await ValidateTokenAsync(jsonWebToken, validationParameters, lkgConfiguration).ConfigureAwait(false); + tokenValidationResult = await ValidateTokenAsync(jsonWebToken, validationParameters, lkgConfiguration, cancellationToken).ConfigureAwait(false); if (tokenValidationResult.IsValid) return tokenValidationResult; @@ -716,14 +749,14 @@ private async ValueTask ValidateTokenAsync(JsonWebToken j return tokenValidationResult; } - private ValueTask ValidateTokenAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + private ValueTask ValidateTokenAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration, CancellationToken cancellationToken) { return jsonWebToken.IsEncrypted ? - ValidateJWEAsync(jsonWebToken, validationParameters, configuration) : - ValidateJWSAsync(jsonWebToken, validationParameters, configuration); + ValidateJWEAsync(jsonWebToken, validationParameters, configuration, cancellationToken) : + ValidateJWSAsync(jsonWebToken, validationParameters, configuration, cancellationToken); } - private async ValueTask ValidateJWSAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + private async ValueTask ValidateJWSAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration, CancellationToken cancellationToken) { try { @@ -734,21 +767,21 @@ private async ValueTask ValidateJWSAsync(JsonWebToken jso if (validationParameters.SignatureValidator != null || validationParameters.SignatureValidatorUsingConfiguration != null) { var validatedToken = ValidateSignatureUsingDelegates(jsonWebToken, validationParameters, configuration); - tokenValidationResult = await ValidateTokenPayloadAsync(validatedToken, validationParameters, configuration).ConfigureAwait(false); + tokenValidationResult = await ValidateTokenPayloadAsync(validatedToken, validationParameters, configuration, cancellationToken).ConfigureAwait(false); Validators.ValidateIssuerSecurityKey(validatedToken.SigningKey, validatedToken, validationParameters, configuration); } else { if (validationParameters.ValidateSignatureLast) { - tokenValidationResult = await ValidateTokenPayloadAsync(jsonWebToken, validationParameters, configuration).ConfigureAwait(false); + tokenValidationResult = await ValidateTokenPayloadAsync(jsonWebToken, validationParameters, configuration, cancellationToken).ConfigureAwait(false); if (tokenValidationResult.IsValid) tokenValidationResult.SecurityToken = ValidateSignatureAndIssuerSecurityKey(jsonWebToken, validationParameters, configuration); } else { var validatedToken = ValidateSignatureAndIssuerSecurityKey(jsonWebToken, validationParameters, configuration); - tokenValidationResult = await ValidateTokenPayloadAsync(validatedToken, validationParameters, configuration).ConfigureAwait(false); + tokenValidationResult = await ValidateTokenPayloadAsync(validatedToken, validationParameters, configuration, cancellationToken).ConfigureAwait(false); } } @@ -767,7 +800,7 @@ private async ValueTask ValidateJWSAsync(JsonWebToken jso } } - private async ValueTask ValidateJWEAsync(JsonWebToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + private async ValueTask ValidateJWEAsync(JsonWebToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration, CancellationToken cancellationToken) { try { @@ -775,7 +808,7 @@ private async ValueTask ValidateJWEAsync(JsonWebToken jwt if (!tokenValidationResult.IsValid) return tokenValidationResult; - tokenValidationResult = await ValidateJWSAsync(tokenValidationResult.SecurityToken as JsonWebToken, validationParameters, configuration).ConfigureAwait(false); + tokenValidationResult = await ValidateJWSAsync(tokenValidationResult.SecurityToken as JsonWebToken, validationParameters, configuration, cancellationToken).ConfigureAwait(false); if (!tokenValidationResult.IsValid) return tokenValidationResult; @@ -838,7 +871,7 @@ private static JsonWebToken ValidateSignatureAndIssuerSecurityKey(JsonWebToken j return validatedToken; } - private async ValueTask ValidateTokenPayloadAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + private async ValueTask ValidateTokenPayloadAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration, CancellationToken cancellationToken) { var expires = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Exp) ? (DateTime?)jsonWebToken.ValidTo : null; var notBefore = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Nbf) ? (DateTime?)jsonWebToken.ValidFrom : null; @@ -857,7 +890,7 @@ private async ValueTask ValidateTokenPayloadAsync(JsonWeb // NOTE: More than one nested actor token should not be considered a valid token, but if we somehow encounter one, // this code will still work properly. TokenValidationResult tokenValidationResult = - await ValidateTokenAsync(jsonWebToken.Actor, validationParameters.ActorValidationParameters ?? validationParameters).ConfigureAwait(false); + await ValidateTokenAsync(jsonWebToken.Actor, validationParameters.ActorValidationParameters ?? validationParameters, cancellationToken).ConfigureAwait(false); if (!tokenValidationResult.IsValid) return tokenValidationResult; diff --git a/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs b/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs index bba4f7c69b..633e8b3fd9 100644 --- a/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs +++ b/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs @@ -5,6 +5,7 @@ using System; using System.ComponentModel; using System.Security.Claims; +using System.Threading; using System.Threading.Tasks; using static Microsoft.IdentityModel.Logging.LogHelper; @@ -65,6 +66,17 @@ public int TokenLifetimeInMinutes /// A public virtual Task ValidateTokenAsync(string token, TokenValidationParameters validationParameters) => throw new NotImplementedException(); + /// + /// Validates a token. + /// On a validation failure, no exception will be thrown; instead, the exception will be set in the returned TokenValidationResult.Exception property. + /// Callers should always check the TokenValidationResult.IsValid property to verify the validity of the result. + /// + /// The token to be validated. + /// A required for validation. + /// Propagates notification that operations should be canceled. + /// A + internal virtual Task ValidateTokenAsync(string token, TokenValidationParameters validationParameters, CancellationToken cancellationToken) => throw new NotImplementedException(); + /// /// Validates a token. /// On a validation failure, no exception will be thrown; instead, the exception will be set in the returned TokenValidationResult.Exception property. @@ -75,6 +87,17 @@ public int TokenLifetimeInMinutes /// A public virtual Task ValidateTokenAsync(SecurityToken token, TokenValidationParameters validationParameters) => throw new NotImplementedException(); + /// + /// Validates a token. + /// On a validation failure, no exception will be thrown; instead, the exception will be set in the returned TokenValidationResult.Exception property. + /// Callers should always check the TokenValidationResult.IsValid property to verify the validity of the result. + /// + /// The to be validated. + /// A required for validation. + /// Propagates notification that operations should be canceled. + /// A + internal virtual Task ValidateTokenAsync(SecurityToken token, TokenValidationParameters validationParameters, CancellationToken cancellationToken) => throw new NotImplementedException(); + /// /// Converts a string into an instance of . ///