From ff6377892c6a126ea6207a28d5dd9779393152f7 Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Wed, 28 Aug 2024 20:04:29 +0100 Subject: [PATCH] ValidateTokenAsync - New Path: Refactor result types (#2794) * Adding benchmark for new ValidateTokenAsync model vs old. * Update benchmark/Microsoft.IdentityModel.Benchmarks/ValidateTokenAsyncWithVPTests.cs Co-authored-by: Keegan Caruso * Updated class naming to be more explicit and added benchmark using clone * Added benchmarks to compare performance on multiple validation scenarios * Removed StackFrame from ValidationResult Adding Result and Unit type. Experiment around allocations and performance. * Work in progress * Added project * Added missing await * Removed tags, added implicit initialisers for Result type. Updated Result return values to use implicit initialisation * Removed IdentityComparer methods relating to removed result types. Updated tests for Algorithm and Audience validation * Removed ITokenValidationError interface. Updated tests * Replaced TokenValidationError with ExceptionDetails. Added stack frames to failures, added initial cache experiment for stack frames * Removed optionality from CancellationToken parameter * Updated tests * Added ClaimsIdentity to ValidationResult * Updated benchmarks to match the result types and added CancellationToken * Removed test consoleapp, re-grouped benchmarks for better comparison * Removed unit type * Removed TokenValidationError since it is no longer used * Restored ExceptionType type name * Restored ExceptionFromType method in ExceptionDetail * Override Result's empty initialiser and annotate it to prevent wrong initialisation * Removed commented code. * Commented empty if statement * Added cached stack frames and nullability annotations for ValidateTokenAsync Changed return type to use Result and removed error information from ValidationResult Added method to add StackFrames to ExceptionDetail * Added stack frames for ReadToken and DecryptToken * Added ValidationFailureType to ExceptionDetail * Updated tests to use ValidationFailureType * Update src/Microsoft.IdentityModel.Abstractions/Result.cs Co-authored-by: Keegan Caruso * Moved Result to Tokens project, made it internal. Reverted TargetFrameworks change to LoggingExtensions * Addressed PR feedback around argument null exceptions * Addressed PR comments. Removed unused imports. Increased epsilon for datetime comparison in tests. --------- Co-authored-by: Franco Fung Co-authored-by: Franco Fung <38921563+FuPingFranco@users.noreply.github.com> Co-authored-by: Keegan Caruso --- .../Program.cs | 2 +- .../ValidateTokenAsyncTests.cs | 75 +- .../JsonWebTokenHandler.DecryptToken.cs | 108 +-- .../JsonWebTokenHandler.ReadToken.cs | 37 +- .../JsonWebTokenHandler.ValidateSignature.cs | 198 ++--- ...nWebTokenHandler.ValidateToken.Internal.cs | 330 +++++--- ...bTokenHandler.ValidateToken.StackFrames.cs | 55 ++ .../JwtTokenUtilities.DecryptTokenResult.cs | 77 +- .../JwtTokenUtilities.cs | 25 +- .../Delegates.cs | 3 +- .../LogMessages.cs | 1 + .../TokenUtilities.cs | 34 +- .../Results/AlgorithmValidationResult.cs | 70 -- .../Results/AudienceValidationResult.cs | 67 -- .../Results/Details/ExceptionDetail.cs | 120 ++- .../Results/Details/MessageDetail.cs | 3 + .../Results/InternalTokenValidationResult.cs | 146 ---- .../Results/IssuerValidationResult.cs | 83 -- .../Results/LifetimeValidationResult.cs | 78 -- .../Results/ReplayValidationResult.cs | 73 -- .../Validation/Results/Result.cs | 159 ++++ .../Results/SignatureValidationResult.cs | 76 -- .../Results/SigningKeyValidationResult.cs | 69 -- .../Results/TokenDecryptionResult.cs | 97 --- .../Validation/Results/TokenReadingResult.cs | 86 -- .../Results/TokenTypeValidationResult.cs | 70 -- .../Results/TokenValidationResult.cs | 68 +- .../Validation/Results/ValidationResult.cs | 175 ++-- .../Validation/ValidationFailureType.cs | 13 +- .../Validation/ValidationParameters.cs | 8 +- .../Validation/Validators.Algorithm.cs | 42 +- .../Validation/Validators.Audience.cs | 68 +- .../Validation/Validators.Issuer.cs | 129 ++- .../Validation/Validators.IssuerSigningKey.cs | 106 +-- .../Validation/Validators.Lifetime.cs | 97 +-- .../Validation/Validators.TokenReplay.cs | 82 +- .../Validation/Validators.TokenType.cs | 81 +- .../JsonWebTokenHandler.DecryptTokenTests.cs | 112 ++- .../JsonWebTokenHandler.ReadTokenTests.cs | 81 +- ...nWebTokenHandler.ValidateSignatureTests.cs | 138 ++-- .../IdentityComparer.cs | 773 +++--------------- .../InternalsVisibleTo.cs | 5 + .../AlgorithmValidationResultTests.cs | 71 +- .../Validation/AsyncValidatorsTests.cs | 58 -- .../AudienceValidationResultTests.cs | 514 +++++------- .../Validation/ExceptionDetailsTests.cs | 60 +- .../Validation/IssuerValidationResultTests.cs | 114 ++- .../LifetimeValidationResultTests.cs | 168 ++-- .../Validation/ReplayValidationResultTests.cs | 142 ++-- .../SigningKeyValidationResultTests.cs | 127 +-- .../TokenTypeValidationResultTests.cs | 110 +-- 51 files changed, 2064 insertions(+), 3420 deletions(-) create mode 100644 src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.StackFrames.cs delete mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/AlgorithmValidationResult.cs delete mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/AudienceValidationResult.cs delete mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/InternalTokenValidationResult.cs delete mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/IssuerValidationResult.cs delete mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/LifetimeValidationResult.cs delete mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/ReplayValidationResult.cs create mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/Result.cs delete mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/SignatureValidationResult.cs delete mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/SigningKeyValidationResult.cs delete mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenDecryptionResult.cs delete mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenReadingResult.cs delete mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenTypeValidationResult.cs create mode 100644 test/Microsoft.IdentityModel.TestUtils/InternalsVisibleTo.cs delete mode 100644 test/Microsoft.IdentityModel.Tokens.Tests/Validation/AsyncValidatorsTests.cs diff --git a/benchmark/Microsoft.IdentityModel.Benchmarks/Program.cs b/benchmark/Microsoft.IdentityModel.Benchmarks/Program.cs index 570a92b9af..5c33334a49 100644 --- a/benchmark/Microsoft.IdentityModel.Benchmarks/Program.cs +++ b/benchmark/Microsoft.IdentityModel.Benchmarks/Program.cs @@ -49,7 +49,7 @@ private static void DebugThroughTests() ValidateTokenAsyncTests validateTokenAsyncTests = new ValidateTokenAsyncTests(); validateTokenAsyncTests.Setup(); TokenValidationResult tokenValidationResult = validateTokenAsyncTests.JsonWebTokenHandler_ValidateTokenAsyncWithTVP().Result; - TokenValidationResult validationResult = validateTokenAsyncTests.JsonWebTokenHandler_ValidateTokenAsyncWithVP().Result; + bool validationResult = validateTokenAsyncTests.JsonWebTokenHandler_ValidateTokenAsyncWithVP().Result; var claims = validateTokenAsyncTests.JsonWebTokenHandler_ValidateTokenAsyncWithTVP_CreateClaims(); ValidateSignedHttpRequestAsyncTests validateSignedHttpRequestAsyncTests = new ValidateSignedHttpRequestAsyncTests(); diff --git a/benchmark/Microsoft.IdentityModel.Benchmarks/ValidateTokenAsyncTests.cs b/benchmark/Microsoft.IdentityModel.Benchmarks/ValidateTokenAsyncTests.cs index 8724b49637..b174a691dd 100644 --- a/benchmark/Microsoft.IdentityModel.Benchmarks/ValidateTokenAsyncTests.cs +++ b/benchmark/Microsoft.IdentityModel.Benchmarks/ValidateTokenAsyncTests.cs @@ -5,6 +5,7 @@ using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; +using System.Threading; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; @@ -82,13 +83,13 @@ public void Setup() _callContext = new CallContext(); } - [BenchmarkCategory("ValidateTokenAsyncWithTokenValidationParameters"), Benchmark] - public async Task JwtSecurityTokenHandler_ValidateTokenAsync() => await _jwtSecurityTokenHandler.ValidateTokenAsync(_jws, _tokenValidationParameters).ConfigureAwait(false); + [BenchmarkCategory("ValidateTokenAsync_Success"), Benchmark] + public async Task JwtSecurityTokenHandler_ValidateTokenAsync() => await _jwtSecurityTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _tokenValidationParameters).ConfigureAwait(false); - [BenchmarkCategory("ValidateTokenAsyncWithTokenValidationParameters"), Benchmark(Baseline = true)] - public async Task JsonWebTokenHandler_ValidateTokenAsyncWithTVP() => await _jsonWebTokenHandler.ValidateTokenAsync(_jws, _tokenValidationParameters).ConfigureAwait(false); + [BenchmarkCategory("ValidateTokenAsync_Success"), Benchmark(Baseline = true)] + public async Task JsonWebTokenHandler_ValidateTokenAsyncWithTVP() => await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _tokenValidationParameters).ConfigureAwait(false); - [BenchmarkCategory("ValidateTokenAsyncWithTokenValidationParameters"), Benchmark] + [BenchmarkCategory("ValidateTokenAsync_Success"), Benchmark] public async Task JsonWebTokenHandler_ValidateTokenAsyncWithTVPUsingModifiedClone() { var tokenValidationParameters = _tokenValidationParameters.Clone(); @@ -98,7 +99,16 @@ public async Task JsonWebTokenHandler_ValidateTokenAsyncW return await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, tokenValidationParameters).ConfigureAwait(false); } - [BenchmarkCategory("ValidateTokenAsyncWithTokenValidationParameters"), Benchmark] + [BenchmarkCategory("ValidateTokenAsync_Success"), Benchmark] + public async Task JsonWebTokenHandler_ValidateTokenAsyncWithVP() + { + // Because ValidationResult is an internal type, we cannot return it in the benchmark. + // We return a boolean instead until the type is made public. + Result result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _validationParameters, _callContext, CancellationToken.None).ConfigureAwait(false); + return result.IsSuccess; + } + + [BenchmarkCategory("ValidateTokenAsync_FailTwiceBeforeSuccess"), Benchmark(Baseline = true)] public async Task JsonWebTokenHandler_ValidateTokenAsyncWithTVP_SucceedOnThirdAttempt() { TokenValidationResult result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidTokenValidationParameters).ConfigureAwait(false); @@ -108,7 +118,7 @@ public async Task JsonWebTokenHandler_ValidateTokenAsyncW return result; } - [BenchmarkCategory("ValidateTokenAsyncWithTokenValidationParameters"), Benchmark] + [BenchmarkCategory("ValidateTokenAsync_FailTwiceBeforeSuccess"), Benchmark] public async Task JsonWebTokenHandler_ValidateTokenAsyncWithTVPUsingClone_SucceedOnThirdAttempt() { TokenValidationResult result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidTokenValidationParameters.Clone()).ConfigureAwait(false); @@ -118,7 +128,17 @@ public async Task JsonWebTokenHandler_ValidateTokenAsyncW return result; } - [BenchmarkCategory("ValidateTokenAsyncWithTokenValidationParameters"), Benchmark] + [BenchmarkCategory("ValidateTokenAsync_FailTwiceBeforeSuccess"), Benchmark] + public async Task JsonWebTokenHandler_ValidateTokenAsyncWithVP_SucceedOnThirdAttempt() + { + Result result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidValidationParameters, _callContext, CancellationToken.None).ConfigureAwait(false); + result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidValidationParameters, _callContext, CancellationToken.None).ConfigureAwait(false); + result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _validationParameters, _callContext, CancellationToken.None).ConfigureAwait(false); + + return result.IsSuccess; + } + + [BenchmarkCategory("ValidateTokenAsync_FailFourTimesBeforeSuccess"), Benchmark(Baseline = true)] public async Task JsonWebTokenHandler_ValidateTokenAsyncWithTVP_SucceedOnFifthAttempt() { TokenValidationResult result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidTokenValidationParameters).ConfigureAwait(false); @@ -130,7 +150,7 @@ public async Task JsonWebTokenHandler_ValidateTokenAsyncW return result; } - [BenchmarkCategory("ValidateTokenAsyncWithTokenValidationParameters"), Benchmark] + [BenchmarkCategory("ValidateTokenAsync_FailFourTimesBeforeSuccess"), Benchmark] public async Task JsonWebTokenHandler_ValidateTokenAsyncWithTVPUsingClone_SucceedOnFifthAttempt() { TokenValidationResult result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidTokenValidationParameters.Clone()).ConfigureAwait(false); @@ -142,41 +162,32 @@ public async Task JsonWebTokenHandler_ValidateTokenAsyncW return result; } - [BenchmarkCategory("ValidateTokenAsyncWithValidationParameters"), Benchmark(Baseline = true)] - public async Task JsonWebTokenHandler_ValidateTokenAsyncWithVP() => await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _validationParameters, _callContext, null).ConfigureAwait(false); - - [BenchmarkCategory("ValidateTokenAsyncWithValidationParameters"), Benchmark] - public async Task JsonWebTokenHandler_ValidateTokenAsyncWithVP_SucceedOnThirdAttempt() + [BenchmarkCategory("ValidateTokenAsync_FailFourTimesBeforeSuccess"), Benchmark] + public async Task JsonWebTokenHandler_ValidateTokenAsyncWithVP_SucceedOnFifthAttempt() { - var result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidValidationParameters, _callContext, null).ConfigureAwait(false); - result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidValidationParameters, _callContext, null).ConfigureAwait(false); - return await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _validationParameters, _callContext, null).ConfigureAwait(false); - } + Result result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidValidationParameters, _callContext, CancellationToken.None).ConfigureAwait(false); + result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidValidationParameters, _callContext, CancellationToken.None).ConfigureAwait(false); + result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidValidationParameters, _callContext, CancellationToken.None).ConfigureAwait(false); + result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidValidationParameters, _callContext, CancellationToken.None).ConfigureAwait(false); + result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _validationParameters, _callContext, CancellationToken.None).ConfigureAwait(false); - [BenchmarkCategory("ValidateTokenAsyncWithValidationParameters"), Benchmark] - public async Task JsonWebTokenHandler_ValidateTokenAsyncWithVP_SucceedOnFifthAttempt() - { - var result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidValidationParameters, _callContext, null).ConfigureAwait(false); - result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidValidationParameters, _callContext, null).ConfigureAwait(false); - result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidValidationParameters, _callContext, null).ConfigureAwait(false); - result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _invalidValidationParameters, _callContext, null).ConfigureAwait(false); - return await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _validationParameters, _callContext, null).ConfigureAwait(false); + return result.IsSuccess; } - [BenchmarkCategory("ValidateTokenAsyncClaimAccess"), Benchmark] - public async Task> JsonWebTokenHandler_ValidateTokenAsyncWithVP_CreateClaims() + [BenchmarkCategory("ValidateTokenAsyncClaimAccess"), Benchmark(Baseline = true)] + public async Task> JsonWebTokenHandler_ValidateTokenAsyncWithTVP_CreateClaims() { - var result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _validationParameters, _callContext, null).ConfigureAwait(false); + var result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _tokenValidationParameters).ConfigureAwait(false); var claimsIdentity = result.ClaimsIdentity; var claims = claimsIdentity.Claims; return claims.ToList(); } [BenchmarkCategory("ValidateTokenAsyncClaimAccess"), Benchmark] - public async Task> JsonWebTokenHandler_ValidateTokenAsyncWithTVP_CreateClaims() + public async Task> JsonWebTokenHandler_ValidateTokenAsyncWithVP_CreateClaims() { - var result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _tokenValidationParameters).ConfigureAwait(false); - var claimsIdentity = result.ClaimsIdentity; + Result result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _validationParameters, _callContext, CancellationToken.None).ConfigureAwait(false); + var claimsIdentity = result.UnwrapResult().ClaimsIdentity; var claims = claimsIdentity.Claims; return claims.ToList(); } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.DecryptToken.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.DecryptToken.cs index 437d0d692a..5e12c33897 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.DecryptToken.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.DecryptToken.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; -using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; @@ -23,51 +23,58 @@ public partial class JsonWebTokenHandler : TokenHandler /// The to be used for validating the token. /// /// The decoded / cleartext contents of the JWE. - /// Returned inside if is null. - /// Returned inside if is null. - /// Returned inside if is null or empty. - /// Returned inside if the decompression failed. - /// Returned inside if is not null AND the decryption fails. - /// Returned inside if the JWE was not able to be decrypted. - internal TokenDecryptionResult DecryptToken( + internal Result DecryptToken( JsonWebToken jwtToken, ValidationParameters validationParameters, - BaseConfiguration configuration, + BaseConfiguration? configuration, CallContext? callContext) { if (jwtToken == null) - return TokenDecryptionResult.NullParameterFailure(jwtToken, nameof(jwtToken)); + { + StackFrame tokenNullStackFrame = StackFrames.DecryptionTokenNull ??= new StackFrame(true); + return ExceptionDetail.NullParameter( + nameof(jwtToken), + tokenNullStackFrame); + } if (validationParameters == null) - return TokenDecryptionResult.NullParameterFailure(jwtToken, nameof(validationParameters)); + { + StackFrame validationParametersNullStackFrame = StackFrames.DecryptionValidationParametersNull ??= new StackFrame(true); + return ExceptionDetail.NullParameter( + nameof(validationParameters), + validationParametersNullStackFrame); + } if (string.IsNullOrEmpty(jwtToken.Enc)) - return new TokenDecryptionResult( - jwtToken, - ValidationFailureType.TokenDecryptionFailed, - new ExceptionDetail( - new MessageDetail(TokenLogMessages.IDX10612), - ExceptionDetail.ExceptionType.SecurityToken, - new System.Diagnostics.StackFrame())); - - var keysOrExceptionDetail = GetContentEncryptionKeys(jwtToken, validationParameters, configuration, callContext); - if (keysOrExceptionDetail.Item2 != null) // ExceptionDetail returned - return new TokenDecryptionResult( - jwtToken, + { + StackFrame headerMissingStackFrame = StackFrames.DecryptionHeaderMissing ??= new StackFrame(true); + return new ExceptionDetail( + new MessageDetail(TokenLogMessages.IDX10612), ValidationFailureType.TokenDecryptionFailed, - keysOrExceptionDetail.Item2); + ExceptionType.SecurityToken, + headerMissingStackFrame); + } - var keys = keysOrExceptionDetail.Item1; - if (keys == null) - return new TokenDecryptionResult( - jwtToken, + (IList? contentEncryptionKeys, ExceptionDetail? exceptionDetail) result = + GetContentEncryptionKeys(jwtToken, validationParameters, configuration, callContext); + + if (result.exceptionDetail != null) + { + StackFrame decryptionGetKeysStackFrame = StackFrames.DecryptionGetEncryptionKeys ??= new StackFrame(true); + return result.exceptionDetail.AddStackFrame(decryptionGetKeysStackFrame); + } + + if (result.contentEncryptionKeys == null) + { + StackFrame noKeysTriedStackFrame = StackFrames.DecryptionNoKeysTried ??= new StackFrame(true); + return new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10609, + LogHelper.MarkAsSecurityArtifact(jwtToken, JwtTokenUtilities.SafeLogJwtToken)), ValidationFailureType.TokenDecryptionFailed, - new ExceptionDetail( - new MessageDetail( - TokenLogMessages.IDX10609, - LogHelper.MarkAsSecurityArtifact(jwtToken, JwtTokenUtilities.SafeLogJwtToken)), - ExceptionDetail.ExceptionType.SecurityTokenDecryptionFailed, - new System.Diagnostics.StackFrame())); + ExceptionType.SecurityTokenDecryptionFailed, + noKeysTriedStackFrame); + } return JwtTokenUtilities.DecryptJwtToken( jwtToken, @@ -75,13 +82,13 @@ internal TokenDecryptionResult DecryptToken( new JwtTokenDecryptionParameters { DecompressionFunction = JwtTokenUtilities.DecompressToken, - Keys = keys, + Keys = result.contentEncryptionKeys, MaximumDeflateSize = MaximumTokenSizeInBytes }, callContext); } - internal (IList?, ExceptionDetail?) GetContentEncryptionKeys(JsonWebToken jwtToken, ValidationParameters validationParameters, BaseConfiguration configuration, CallContext? callContext) + internal (IList?, ExceptionDetail?) GetContentEncryptionKeys(JsonWebToken jwtToken, ValidationParameters validationParameters, BaseConfiguration? configuration, CallContext? callContext) { IList? keys = null; @@ -95,19 +102,22 @@ internal TokenDecryptionResult DecryptToken( else { var key = ResolveTokenDecryptionKey(jwtToken.EncodedToken, jwtToken, validationParameters, callContext); - if (key != null) - { - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(TokenLogMessages.IDX10904, key); - } - else if (configuration != null) + //if (key is not null) + //{ + // TODO: Move to CallContext or return decryption key source as part of result + //if (LogHelper.IsEnabled(EventLogLevel.Informational)) + // LogHelper.LogInformation(TokenLogMessages.IDX10904, key); + //} + //else + if (key is null && configuration is not null) { key = ResolveTokenDecryptionKeyFromConfig(jwtToken, configuration); - if (key != null && LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(TokenLogMessages.IDX10905, key); + // TODO: Move to CallContext or return decryption key source as part of result + //if (key != null && LogHelper.IsEnabled(EventLogLevel.Informational)) + // LogHelper.LogInformation(TokenLogMessages.IDX10905, key); } - if (key != null) + if (key is not null) keys = [key]; } @@ -196,14 +206,18 @@ internal TokenDecryptionResult DecryptToken( return (unwrappedKeys, null); else { + StackFrame decryptionKeyUnwrapFailedStackFrame = StackFrames.DecryptionKeyUnwrapFailed ??= new StackFrame(true); ExceptionDetail exceptionDetail = new( new MessageDetail( TokenLogMessages.IDX10618, keysAttempted?.ToString() ?? "", exceptionStrings?.ToString() ?? "", LogHelper.MarkAsSecurityArtifact(jwtToken, JwtTokenUtilities.SafeLogJwtToken)), - ExceptionDetail.ExceptionType.SecurityTokenKeyWrap, - new System.Diagnostics.StackFrame()); + ValidationFailureType.TokenDecryptionFailed, + ExceptionType.SecurityTokenKeyWrap, + decryptionKeyUnwrapFailedStackFrame, + null); + return (null, exceptionDetail); } } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ReadToken.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ReadToken.cs index f18ce31dcf..a37d327fcd 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ReadToken.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ReadToken.cs @@ -2,9 +2,8 @@ // Licensed under the MIT License. using System; -using Microsoft.IdentityModel.Logging; +using System.Diagnostics; using Microsoft.IdentityModel.Tokens; -using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; namespace Microsoft.IdentityModel.JsonWebTokens { @@ -13,15 +12,15 @@ public partial class JsonWebTokenHandler : TokenHandler { #nullable enable /// - /// Converts a string into an instance of , returned inside of a . + /// Converts a string into an instance of , returned inside of a . /// /// A JSON Web Token (JWT) in JWS or JWE Compact Serialization format. /// - /// A with the if valid, or an Exception. + /// A with the if valid, or an error. /// returned if is null or empty. /// returned if the validationParameters.TokenReader delegate is not able to parse/read the token as a valid . /// returned if is not a valid JWT, . - internal static TokenReadingResult ReadToken( + internal static Result ReadToken( string token, #pragma warning disable CA1801 // TODO: remove pragma disable once callContext is used for logging CallContext? callContext) @@ -29,34 +28,28 @@ internal static TokenReadingResult ReadToken( { if (String.IsNullOrEmpty(token)) { - return new TokenReadingResult( - token, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - TokenLogMessages.IDX10000, - LogHelper.MarkAsNonPII(nameof(token))), - ExceptionDetail.ExceptionType.ArgumentNull, - new System.Diagnostics.StackFrame())); + StackFrame nullTokenStackFrame = StackFrames.ReadTokenNullOrEmpty ?? new StackFrame(true); + return ExceptionDetail.NullParameter( + nameof(token), + nullTokenStackFrame); } try { JsonWebToken jsonWebToken = new JsonWebToken(token); - return new TokenReadingResult(jsonWebToken, token); + return jsonWebToken; } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { - return new TokenReadingResult( - token, + StackFrame malformedTokenStackFrame = StackFrames.ReadTokenMalformed ?? new StackFrame(true); + return new ExceptionDetail( + new MessageDetail(LogMessages.IDX14107), ValidationFailureType.TokenReadingFailed, - new ExceptionDetail( - new MessageDetail(LogMessages.IDX14107), - ExceptionDetail.ExceptionType.SecurityTokenMalformed, - new System.Diagnostics.StackFrame(), - ex)); + ExceptionType.SecurityTokenMalformed, + malformedTokenStackFrame, + ex); } } } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateSignature.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateSignature.cs index 6b5f36436e..65c48fdfaa 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateSignature.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateSignature.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using System.Linq; using System.Text; -using Microsoft.IdentityModel.JsonWebTokens.Results; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; @@ -17,6 +16,11 @@ namespace Microsoft.IdentityModel.JsonWebTokens /// This partial class contains methods and logic related to the validation of tokens' signatures. public partial class JsonWebTokenHandler : TokenHandler { + static internal class SignatureStackFrames + { + // Test StackFrame to validate caching solution. Need to add all the possible stack frames. + static internal StackFrame? NoKeysProvided; + } /// /// Validates the JWT signature. /// @@ -28,17 +32,21 @@ public partial class JsonWebTokenHandler : TokenHandler /// Returned by the default implementation if the token is not signed, or if the validation fails. /// Returned if the algorithm is not supported by the key. /// Returned if the key cannot be resolved. - internal static SignatureValidationResult ValidateSignature( + internal static Result ValidateSignature( JsonWebToken jwtToken, ValidationParameters validationParameters, BaseConfiguration? configuration, CallContext callContext) { if (jwtToken is null) - return SignatureValidationResult.NullParameterFailure(nameof(jwtToken)); + return ExceptionDetail.NullParameter( + nameof(jwtToken), + new StackFrame(true)); if (validationParameters is null) - return SignatureValidationResult.NullParameterFailure(nameof(validationParameters)); + return ExceptionDetail.NullParameter( + nameof(validationParameters), + new StackFrame(true)); // Delegate is set by the user, we call it and return the result. if (validationParameters.SignatureValidator is not null) @@ -46,17 +54,15 @@ internal static SignatureValidationResult ValidateSignature( // If the user wants to accept unsigned tokens, they must implement the delegate. if (!jwtToken.IsSigned) - return new SignatureValidationResult( + return new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10504, + LogHelper.MarkAsSecurityArtifact( + jwtToken.EncodedToken, + JwtTokenUtilities.SafeLogJwtToken)), ValidationFailureType.SignatureValidationFailed, - new ExceptionDetail( - new MessageDetail( - TokenLogMessages.IDX10504, - LogHelper.MarkAsSecurityArtifact( - jwtToken.EncodedToken, - JwtTokenUtilities.SafeLogJwtToken) - ), - ExceptionDetail.ExceptionType.SecurityTokenInvalidSignature, - new StackFrame())); + ExceptionType.SecurityTokenInvalidSignature, + new StackFrame(true)); SecurityKey? key = null; if (validationParameters.IssuerSigningKeyResolver is not null) @@ -89,15 +95,17 @@ internal static SignatureValidationResult ValidateSignature( if (validationParameters.TryAllIssuerSigningKeys) return ValidateSignatureUsingAllKeys(jwtToken, validationParameters, configuration, callContext); else - return new SignatureValidationResult( + { + StackFrame stackFrame = SignatureStackFrames.NoKeysProvided ??= new StackFrame(true); + return new ExceptionDetail( + new MessageDetail(TokenLogMessages.IDX10500), ValidationFailureType.SignatureValidationFailed, - new ExceptionDetail( - new MessageDetail(TokenLogMessages.IDX10500), - ExceptionDetail.ExceptionType.SecurityTokenSignatureKeyNotFound, - new StackFrame())); + ExceptionType.SecurityTokenSignatureKeyNotFound, + stackFrame); + } } - private static SignatureValidationResult ValidateSignatureUsingAllKeys( + private static Result ValidateSignatureUsingAllKeys( JsonWebToken jwtToken, ValidationParameters validationParameters, BaseConfiguration? configuration, @@ -107,31 +115,30 @@ private static SignatureValidationResult ValidateSignatureUsingAllKeys( // 1. User specified delegate: IssuerSigningKeyResolver returned null // 2. ResolveIssuerSigningKey returned null // Try all the keys. This is the degenerate case, not concerned about perf. - (SignatureValidationResult? configResult, bool configKidMatched, KeyMatchFailedResult? configFailedResult) = ValidateUsingKeys( + (Result? configResult, bool configKidMatched, KeyMatchFailedResult? configFailedResult) = ValidateUsingKeys( jwtToken, validationParameters, configuration?.SigningKeys, callContext); - if (configResult is not null) - return configResult; + if (configResult is Result unwrappedConfigResult) + return unwrappedConfigResult; - (SignatureValidationResult? vpResult, bool vpKidMatched, KeyMatchFailedResult? vpFailedResult) = ValidateUsingKeys( + (Result? vpResult, bool vpKidMatched, KeyMatchFailedResult? vpFailedResult) = ValidateUsingKeys( jwtToken, validationParameters, validationParameters.IssuerSigningKeys, callContext); - if (vpResult is not null) - return vpResult; + if (vpResult is Result unwrappedVpResult) + return unwrappedVpResult; if (vpFailedResult is null && configFailedResult is null) // No keys were attempted - return new SignatureValidationResult( + return new ExceptionDetail( + new MessageDetail(TokenLogMessages.IDX10500), ValidationFailureType.SignatureValidationFailed, - new ExceptionDetail( - new MessageDetail(TokenLogMessages.IDX10500), - ExceptionDetail.ExceptionType.SecurityTokenSignatureKeyNotFound, - new StackFrame())); + ExceptionType.SecurityTokenSignatureKeyNotFound, + new StackFrame(true)); StringBuilder exceptionStrings = new(); StringBuilder keysAttempted = new(); @@ -143,19 +150,17 @@ private static SignatureValidationResult ValidateSignatureUsingAllKeys( bool kidMatched = configKidMatched || vpKidMatched; // No valid signature found. Return the exception details. - return new SignatureValidationResult( - ValidationFailureType.SignatureValidationFailed, - GetSignatureValidationFailureExceptionDetails( - jwtToken, - validationParameters, - configuration, - exceptionStrings, - keysAttempted, - kidExists, - kidMatched)); + return GetSignatureValidationError( + jwtToken, + validationParameters, + configuration, + exceptionStrings, + keysAttempted, + kidExists, + kidMatched); } - private static (SignatureValidationResult? validResult, bool KidMatched, KeyMatchFailedResult? failedResult) ValidateUsingKeys( + private static (Result? validResult, bool KidMatched, KeyMatchFailedResult? failedResult) ValidateUsingKeys( JsonWebToken jwtToken, ValidationParameters validationParameters, ICollection? keys, @@ -170,36 +175,36 @@ private static (SignatureValidationResult? validResult, bool KidMatched, KeyMatc bool kidExists = !string.IsNullOrEmpty(jwtToken.Kid); bool kidMatched = false; IList? keysAttempted = null; - IList? results = null; + IList? errors = null; for (int i = 0; i < keysList.Count; i++) { SecurityKey key = keysList[i]; - SignatureValidationResult result = ValidateSignatureWithKey(jwtToken, key, validationParameters, callContext); - if (result.IsValid) + Result result = ValidateSignatureWithKey(jwtToken, key, validationParameters, callContext); + if (result.IsSuccess) { jwtToken.SigningKey = key; return (result, true, null); } keysAttempted ??= []; - results ??= []; + errors ??= []; - results.Add(result); + errors.Add(result.UnwrapError()); keysAttempted.Add(key); if (kidExists && !kidMatched && key.KeyId is not null) kidMatched = jwtToken.Kid.Equals(key.KeyId, key is X509SecurityKey ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); } - if (results is not null && results.Count > 0 && keysAttempted is not null && keysAttempted.Count > 0) - return (null, kidMatched, new KeyMatchFailedResult(results, keysAttempted)); + if (errors is not null && errors.Count > 0 && keysAttempted is not null && keysAttempted.Count > 0) + return (null, kidMatched, new KeyMatchFailedResult(errors, keysAttempted)); // No keys were attempted. return (null, kidMatched, null); } - private static SignatureValidationResult ValidateSignatureWithKey( + private static Result ValidateSignatureWithKey( JsonWebToken jsonWebToken, SecurityKey key, ValidationParameters validationParameters, @@ -208,40 +213,38 @@ private static SignatureValidationResult ValidateSignatureWithKey( CryptoProviderFactory cryptoProviderFactory = validationParameters.CryptoProviderFactory ?? key.CryptoProviderFactory; if (!cryptoProviderFactory.IsSupportedAlgorithm(jsonWebToken.Alg, key)) { - return new SignatureValidationResult( + return new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10400, + LogHelper.MarkAsNonPII(jsonWebToken.Alg), + key), ValidationFailureType.SignatureValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX14000, - LogHelper.MarkAsNonPII(jsonWebToken.Alg), - key), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAlgorithm, - new StackFrame())); + ExceptionType.SecurityTokenInvalidAlgorithm, + new StackFrame(true)); } - AlgorithmValidationResult result = validationParameters.AlgorithmValidator( + Result result = validationParameters.AlgorithmValidator( jsonWebToken.Alg, key, jsonWebToken, validationParameters, callContext); - if (!result.IsValid) - return new SignatureValidationResult( - ValidationFailureType.SignatureValidationFailed, - result.ExceptionDetail); + + if (!result.IsSuccess) + return new(result.UnwrapError()); // Because we return an interface type, we need to explicitly create the Result. SignatureProvider signatureProvider = cryptoProviderFactory.CreateForVerifying(key, jsonWebToken.Alg); try { if (signatureProvider == null) - return new SignatureValidationResult( + return new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10636, + key?.ToString() ?? "Null", + LogHelper.MarkAsNonPII(jsonWebToken.Alg)), ValidationFailureType.SignatureValidationFailed, - new ExceptionDetail( - new MessageDetail(TokenLogMessages.IDX10636, - key?.ToString() ?? "Null", - LogHelper.MarkAsNonPII(jsonWebToken.Alg)), - ExceptionDetail.ExceptionType.InvalidOperation, - new StackFrame())); + ExceptionType.InvalidOperation, + new StackFrame(true)); bool valid = EncodingUtils.PerformEncodingDependentOperation( jsonWebToken.EncodedToken, @@ -254,26 +257,32 @@ private static SignatureValidationResult ValidateSignatureWithKey( ValidateSignature); if (valid) - return SignatureValidationResult.Success(); + return key; else - return new SignatureValidationResult( + return new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10504, + LogHelper.MarkAsSecurityArtifact( + jsonWebToken.EncodedToken, + JwtTokenUtilities.SafeLogJwtToken)), ValidationFailureType.SignatureValidationFailed, - new ExceptionDetail( - new MessageDetail(TokenLogMessages.IDX10504), - ExceptionDetail.ExceptionType.SecurityTokenInvalidSignature, - new StackFrame())); + ExceptionType.SecurityTokenInvalidSignature, + new StackFrame(true)); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { - return new SignatureValidationResult( + return new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10504, + LogHelper.MarkAsSecurityArtifact( + jsonWebToken.EncodedToken, + JwtTokenUtilities.SafeLogJwtToken)), ValidationFailureType.SignatureValidationFailed, - new ExceptionDetail( - new MessageDetail(TokenLogMessages.IDX10504, ex.ToString()), - ExceptionDetail.ExceptionType.SecurityTokenInvalidSignature, - new StackFrame(), - ex)); + ExceptionType.SecurityTokenInvalidSignature, + new StackFrame(true), + ex); } finally { @@ -281,7 +290,7 @@ private static SignatureValidationResult ValidateSignatureWithKey( } } - private static ExceptionDetail GetSignatureValidationFailureExceptionDetails( + private static ExceptionDetail GetSignatureValidationError( JsonWebToken jwtToken, ValidationParameters validationParameters, BaseConfiguration? configuration, @@ -311,22 +320,24 @@ private static ExceptionDetail GetSignatureValidationFailureExceptionDetails( LogHelper.MarkAsNonPII(jwtToken.Kid), exceptionStrings.ToString(), LogHelper.MarkAsSecurityArtifact(jwtToken.EncodedToken, JwtTokenUtilities.SafeLogJwtToken)), - ExceptionDetail.ExceptionType.SecurityTokenSignatureKeyNotFound, - new StackFrame()); + ValidationFailureType.SignatureValidationFailed, + ExceptionType.SecurityTokenSignatureKeyNotFound, + new StackFrame(true)); } if (kidExists) return new ExceptionDetail( new MessageDetail( - TokenLogMessages.IDX10503, // No match for kid found among the keys provided. + TokenLogMessages.IDX10503, LogHelper.MarkAsNonPII(jwtToken.Kid), LogHelper.MarkAsNonPII(keysAttempted.ToString()), LogHelper.MarkAsNonPII(numKeysInTokenValidationParameters), LogHelper.MarkAsNonPII(numKeysInConfiguration), exceptionStrings.ToString(), LogHelper.MarkAsSecurityArtifact(jwtToken.EncodedToken, JwtTokenUtilities.SafeLogJwtToken)), - ExceptionDetail.ExceptionType.SecurityTokenSignatureKeyNotFound, - new StackFrame()); + ValidationFailureType.SignatureValidationFailed, + ExceptionType.SecurityTokenSignatureKeyNotFound, + new StackFrame(true)); return new ExceptionDetail( new MessageDetail( @@ -336,8 +347,9 @@ private static ExceptionDetail GetSignatureValidationFailureExceptionDetails( LogHelper.MarkAsNonPII(numKeysInConfiguration), exceptionStrings.ToString(), LogHelper.MarkAsSecurityArtifact(jwtToken.EncodedToken, JwtTokenUtilities.SafeLogJwtToken)), - ExceptionDetail.ExceptionType.SecurityTokenSignatureKeyNotFound, - new StackFrame()); + ValidationFailureType.SignatureValidationFailed, + ExceptionType.SecurityTokenSignatureKeyNotFound, + new StackFrame(true)); } private static void PopulateFailedResults( @@ -349,17 +361,17 @@ private static void PopulateFailedResults( { for (int i = 0; i < result.KeysAttempted.Count; i++) { - exceptionStrings.AppendLine(result.FailedResults[i].ExceptionDetail?.MessageDetail.Message ?? "Null"); + exceptionStrings.AppendLine(result.FailedResults[i].MessageDetail.Message); keysAttempted.AppendLine(result.KeysAttempted[i].ToString()); } } } private struct KeyMatchFailedResult( - IList failedResults, + IList failedResults, IList keysAttempted) { - public IList FailedResults = failedResults; + public IList FailedResults = failedResults; public IList KeysAttempted = keysAttempted; } } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs index 675425fc7d..6986155744 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs @@ -3,14 +3,15 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Logging; using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; +#nullable enable namespace Microsoft.IdentityModel.JsonWebTokens { public partial class JsonWebTokenHandler : TokenHandler @@ -24,109 +25,132 @@ public partial class JsonWebTokenHandler : TokenHandler /// The to be used for validating the token. /// A that contains useful information for logging. /// A that can be used to request cancellation of the asynchronous operation. - /// A . + /// A with either a if the token was validated or an with the failure information and exception otherwise. /// - /// TokenValidationResult.Exception will be set to one of the following exceptions if the is invalid. + /// ExceptionDetail.GetException() will return one of the following exceptions if the is invalid. /// /// Returned if is null or empty. /// Returned if is null. /// Returned if 'token.Length' is greater than . /// Returned if is not a valid , /// Returned if the validationParameters.TokenReader delegate is not able to parse/read the token as a valid , - internal async Task ValidateTokenAsync( + internal async Task> ValidateTokenAsync( string token, ValidationParameters validationParameters, CallContext callContext, - CancellationToken? cancellationToken) + CancellationToken cancellationToken) { // These exceptions will be removed once we add ExceptionDetails to TokenValidationResult. if (string.IsNullOrEmpty(token)) - return new TokenValidationResult { Exception = LogHelper.LogArgumentNullException(nameof(token)), IsValid = false }; + { + StackFrame nullTokenStackFrame = StackFrames.TokenStringNull ??= new StackFrame(true); + return ExceptionDetail.NullParameter( + nameof(token), + nullTokenStackFrame); + } if (validationParameters is null) - return new TokenValidationResult { Exception = LogHelper.LogArgumentNullException(nameof(validationParameters)), IsValid = false }; + { + StackFrame nullValidationParametersStackFrame = StackFrames.TokenStringValidationParametersNull ??= new StackFrame(true); + return ExceptionDetail.NullParameter( + nameof(validationParameters), + nullValidationParametersStackFrame); + } 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 }; + { + StackFrame invalidTokenLengthStackFrame = StackFrames.InvalidTokenLength ??= new StackFrame(true); + return new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10209, + LogHelper.MarkAsNonPII(token.Length), + LogHelper.MarkAsNonPII(MaximumTokenSizeInBytes)), + ValidationFailureType.InvalidSecurityToken, + ExceptionType.InvalidArgument, + invalidTokenLengthStackFrame); + } - TokenReadingResult tokenReadingResult = ReadToken(token, callContext); - if (tokenReadingResult.IsValid) - return await ValidateTokenAsync( - tokenReadingResult.SecurityToken(), + Result readResult = ReadToken(token, callContext); + if (readResult.IsSuccess) + { + Result validationResult = await ValidateTokenAsync( + readResult.UnwrapResult(), validationParameters, callContext, cancellationToken) .ConfigureAwait(false); - return new TokenValidationResult - { - Exception = tokenReadingResult.Exception, - IsValid = false - }; + if (validationResult.IsSuccess) + return validationResult; // No need to unwrap and re-wrap the result. + + StackFrame validationFailureStackFrame = StackFrames.TokenStringValidationFailed ??= new StackFrame(true); + return validationResult.UnwrapError().AddStackFrame(validationFailureStackFrame); + } + + StackFrame readFailureStackFrame = StackFrames.TokenStringReadFailed ??= new StackFrame(true); + return readResult.UnwrapError().AddStackFrame(readFailureStackFrame); } /// - internal async Task ValidateTokenAsync( + internal async Task> ValidateTokenAsync( SecurityToken token, ValidationParameters validationParameters, CallContext callContext, - CancellationToken? cancellationToken) + CancellationToken cancellationToken) { - // These exceptions will be removed once we add ExceptionDetails to TokenValidationResult. if (token is null) - throw LogHelper.LogArgumentNullException(nameof(token)); + { + StackFrame nullTokenStackFrame = StackFrames.TokenNull ??= new StackFrame(true); + return ExceptionDetail.NullParameter( + nameof(token), + nullTokenStackFrame); + } if (validationParameters is null) - return new TokenValidationResult { Exception = LogHelper.LogArgumentNullException(nameof(validationParameters)), IsValid = false }; - - if (token is not JsonWebToken jwt) - return new TokenValidationResult { Exception = LogHelper.LogArgumentException(nameof(token), $"{nameof(token)} must be a {nameof(JsonWebToken)}."), IsValid = false }; + { + StackFrame nullValidationParametersStackFrame = StackFrames.TokenValidationParametersNull ??= new StackFrame(true); + return ExceptionDetail.NullParameter( + nameof(validationParameters), + nullValidationParametersStackFrame); + } - return await InternalValidateTokenAsync( - jwt, - validationParameters, - callContext, - cancellationToken) - .ConfigureAwait(false); - } + if (token is not JsonWebToken jsonWebToken) + { + StackFrame notJwtStackFrame = StackFrames.TokenNotJWT ??= new StackFrame(true); + return new ExceptionDetail( + new MessageDetail(TokenLogMessages.IDX10001, nameof(token), nameof(JsonWebToken)), + ValidationFailureType.InvalidSecurityToken, + ExceptionType.InvalidArgument, + notJwtStackFrame); + } - /// - /// Internal method for token validation, responsible for: - /// (1) Obtaining a configuration from the . - /// (2) Revalidating using the Last Known Good Configuration (if present), and obtaining a refreshed configuration (if necessary) and revalidating using it. - /// - /// The JWT token. - /// The to be used for validating the token. - /// A that contains useful information for logging. - /// A that can be used to request cancellation of the asynchronous operation. - /// - private async ValueTask InternalValidateTokenAsync( - JsonWebToken jsonWebToken, - ValidationParameters validationParameters, - CallContext callContext, - CancellationToken? cancellationToken) - { - BaseConfiguration currentConfiguration = + BaseConfiguration? currentConfiguration = await GetCurrentConfigurationAsync(validationParameters) .ConfigureAwait(false); - InternalTokenValidationResult result = jsonWebToken.IsEncrypted ? + Result result = jsonWebToken.IsEncrypted ? await ValidateJWEAsync(jsonWebToken, validationParameters, currentConfiguration, callContext, cancellationToken).ConfigureAwait(false) : await ValidateJWSAsync(jsonWebToken, validationParameters, currentConfiguration, callContext, cancellationToken).ConfigureAwait(false); if (validationParameters.ConfigurationManager is null) - return result.ToTokenValidationResult(); + { + if (result.IsSuccess) + return result; - if (result.IsValid) + StackFrame tokenValidationStackFrame = StackFrames.TokenValidationFailedNullConfigurationManager ??= new StackFrame(true); + return result.UnwrapError().AddStackFrame(tokenValidationStackFrame); + } + + if (result.IsSuccess) { // Set current configuration as LKG if it exists. if (currentConfiguration is not null) validationParameters.ConfigurationManager.LastKnownGoodConfiguration = currentConfiguration; - return result.ToTokenValidationResult(); + return result; } - if (TokenUtilities.IsRecoverableExceptionType(result.ExceptionDetail.Type)) + if (TokenUtilities.IsRecoverableErrorType(result.UnwrapError().Type)) { // If we were still unable to validate, attempt to refresh the configuration and validate using it // but ONLY if the currentConfiguration is not null. We want to avoid refreshing the configuration on @@ -146,10 +170,10 @@ await ValidateJWEAsync(jsonWebToken, validationParameters, currentConfiguration, await ValidateJWEAsync(jsonWebToken, validationParameters, currentConfiguration, callContext, cancellationToken).ConfigureAwait(false) : await ValidateJWSAsync(jsonWebToken, validationParameters, currentConfiguration, callContext, cancellationToken).ConfigureAwait(false); - if (result.IsValid) + if (result.IsSuccess) { validationParameters.ConfigurationManager.LastKnownGoodConfiguration = currentConfiguration; - return result.ToTokenValidationResult(); + return result; } } } @@ -158,7 +182,7 @@ await ValidateJWEAsync(jsonWebToken, validationParameters, currentConfiguration, { validationParameters.RefreshBeforeValidation = false; validationParameters.ValidateWithLKG = true; - ExceptionDetail.ExceptionType recoverableExceptionType = result.ExceptionDetail.Type; + ExceptionType recoverableExceptionType = result.UnwrapError().Type; BaseConfiguration[] validConfigurations = validationParameters.ConfigurationManager.GetValidLkgConfigurations(); for (int i = 0; i < validConfigurations.Length; i++) @@ -171,121 +195,177 @@ await ValidateJWEAsync(jsonWebToken, validationParameters, currentConfiguration, await ValidateJWEAsync(jsonWebToken, validationParameters, currentConfiguration, callContext, cancellationToken).ConfigureAwait(false) : await ValidateJWSAsync(jsonWebToken, validationParameters, currentConfiguration, callContext, cancellationToken).ConfigureAwait(false); - if (result.IsValid) - return result.ToTokenValidationResult(); + if (result.IsSuccess) + return result; } } } } - return result.ToTokenValidationResult(); + // If we reach this point, the token validation failed and we should return the error. + StackFrame stackFrame = StackFrames.TokenValidationFailed ??= new StackFrame(true); + return result.UnwrapError().AddStackFrame(stackFrame); } - private async ValueTask ValidateJWEAsync( + private async ValueTask> ValidateJWEAsync( JsonWebToken jwtToken, ValidationParameters validationParameters, - BaseConfiguration configuration, + BaseConfiguration? configuration, CallContext callContext, - CancellationToken? cancellationToken) + CancellationToken cancellationToken) { - InternalTokenValidationResult internalResult = new InternalTokenValidationResult(jwtToken, this); - - TokenDecryptionResult decryptionResult = DecryptToken(jwtToken, validationParameters, configuration, callContext); - if (!internalResult.AddResult(decryptionResult)) - return internalResult; - - TokenReadingResult readingResult = ReadToken(decryptionResult.DecryptedToken(), callContext); - if (!internalResult.AddResult(readingResult)) - return internalResult; + Result decryptionResult = DecryptToken( + jwtToken, validationParameters, configuration, callContext); + if (!decryptionResult.IsSuccess) + { + StackFrame decryptionFailureStackFrame = StackFrames.DecryptionFailed ??= new StackFrame(true); + return decryptionResult.UnwrapError().AddStackFrame(decryptionFailureStackFrame); + } - JsonWebToken decryptedToken = readingResult.SecurityToken() as JsonWebToken; + Result readResult = ReadToken(decryptionResult.UnwrapResult(), callContext); + if (!readResult.IsSuccess) + { + StackFrame readFailureStackFrame = StackFrames.DecryptedReadFailed ??= new StackFrame(true); + return readResult.UnwrapError().AddStackFrame(readFailureStackFrame); + } - InternalTokenValidationResult jwsResult = - await ValidateJWSAsync(decryptedToken, validationParameters, configuration, callContext, cancellationToken) + JsonWebToken decryptedToken = (readResult.UnwrapResult() as JsonWebToken)!; + Result validationResult = + await ValidateJWSAsync(decryptedToken!, validationParameters, configuration, callContext, cancellationToken) .ConfigureAwait(false); - if (!internalResult.Merge(jwsResult)) - return internalResult; + if (!validationResult.IsSuccess) + { + StackFrame validationFailureStackFrame = StackFrames.JWEValidationFailed ??= new StackFrame(true); + return validationResult.UnwrapError().AddStackFrame(validationFailureStackFrame); + } + + JsonWebToken jsonWebToken = (validationResult.UnwrapResult().SecurityToken as JsonWebToken)!; - jwtToken.InnerToken = internalResult.SecurityToken as JsonWebToken; - jwtToken.Payload = (internalResult.SecurityToken as JsonWebToken).Payload; + jwtToken.InnerToken = jsonWebToken; + jwtToken.Payload = jsonWebToken.Payload; - return internalResult; + return validationResult; } - private async ValueTask ValidateJWSAsync( + private async ValueTask> ValidateJWSAsync( JsonWebToken jsonWebToken, ValidationParameters validationParameters, - BaseConfiguration configuration, + BaseConfiguration? configuration, CallContext callContext, - CancellationToken? cancellationToken) + CancellationToken cancellationToken) { - if (validationParameters.TransformBeforeSignatureValidation is not null) - jsonWebToken = validationParameters.TransformBeforeSignatureValidation(jsonWebToken, validationParameters) as JsonWebToken; - - InternalTokenValidationResult internalResult = new InternalTokenValidationResult(jsonWebToken, this); - DateTime? expires = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Exp) ? jsonWebToken.ValidTo : null; DateTime? notBefore = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Nbf) ? jsonWebToken.ValidFrom : null; - if (!internalResult.AddResult(validationParameters.LifetimeValidator( - notBefore, expires, jsonWebToken, validationParameters, callContext))) - return internalResult; + Result lifetimeValidationResult = validationParameters.LifetimeValidator( + notBefore, expires, jsonWebToken, validationParameters, callContext); + + if (!lifetimeValidationResult.IsSuccess) + { + StackFrame lifetimeValidationFailureStackFrame = StackFrames.LifetimeValidationFailed ??= new StackFrame(true); + return lifetimeValidationResult.UnwrapError().AddStackFrame(lifetimeValidationFailureStackFrame); + } if (jsonWebToken.Audiences is not IList tokenAudiences) tokenAudiences = jsonWebToken.Audiences.ToList(); - if (!internalResult.AddResult(validationParameters.AudienceValidator( - tokenAudiences, jsonWebToken, validationParameters, callContext))) - return internalResult; + Result audienceValidationResult = validationParameters.AudienceValidator( + tokenAudiences, jsonWebToken, validationParameters, callContext); - if (!internalResult.AddResult(await validationParameters.IssuerValidatorAsync( + if (!audienceValidationResult.IsSuccess) + { + StackFrame audienceValidationFailureStackFrame = StackFrames.AudienceValidationFailed ??= new StackFrame(true); + return audienceValidationResult.UnwrapError().AddStackFrame(audienceValidationFailureStackFrame); + } + + Result issuerValidationResult = await validationParameters.IssuerValidatorAsync( jsonWebToken.Issuer, jsonWebToken, validationParameters, callContext, cancellationToken) - .ConfigureAwait(false))) - return internalResult; + .ConfigureAwait(false); - if (!internalResult.AddResult(validationParameters.TokenReplayValidator( - expires, jsonWebToken.EncodedToken, validationParameters, callContext))) - return internalResult; + if (!issuerValidationResult.IsSuccess) + { + StackFrame issuerValidationFailureStackFrame = StackFrames.IssuerValidationFailed ??= new StackFrame(true); + return issuerValidationResult.UnwrapError().AddStackFrame(issuerValidationFailureStackFrame); + } + Result replayValidationResult = validationParameters.TokenReplayValidator( + expires, jsonWebToken.EncodedToken, validationParameters, callContext); + + if (!replayValidationResult.IsSuccess) + { + StackFrame replayValidationFailureStackFrame = StackFrames.ReplayValidationFailed ??= new StackFrame(true); + return replayValidationResult.UnwrapError().AddStackFrame(replayValidationFailureStackFrame); + } + + Result? actorValidationResult = null; // actor validation if (validationParameters.ValidateActor && !string.IsNullOrWhiteSpace(jsonWebToken.Actor)) { - TokenReadingResult actorReadingResult = ReadToken(jsonWebToken.Actor, callContext); - if (!internalResult.AddResult(actorReadingResult)) - return internalResult; + Result actorReadingResult = ReadToken(jsonWebToken.Actor, callContext); + if (!actorReadingResult.IsSuccess) + { + StackFrame actorReadingFailureStackFrame = StackFrames.ActorReadFailed ??= new StackFrame(true); + return actorReadingResult.UnwrapError().AddStackFrame(actorReadingFailureStackFrame); + } - JsonWebToken actorToken = actorReadingResult.SecurityToken() as JsonWebToken; + JsonWebToken actorToken = (actorReadingResult.UnwrapResult() as JsonWebToken)!; ValidationParameters actorParameters = validationParameters.ActorValidationParameters; - InternalTokenValidationResult actorValidationResult = + Result innerActorValidationResult = await ValidateJWSAsync(actorToken, actorParameters, configuration, callContext, cancellationToken) .ConfigureAwait(false); - // Consider adding a new ValidationResult type for actor validation - // that wraps the actorValidationResult.ValidationResults - if (!internalResult.AddResults(actorValidationResult.ValidationResults)) - return internalResult; + if (!innerActorValidationResult.IsSuccess) + { + StackFrame actorValidationFailureStackFrame = StackFrames.ActorValidationFailed ??= new StackFrame(true); + return innerActorValidationResult.UnwrapError().AddStackFrame(actorValidationFailureStackFrame); + } + + actorValidationResult = innerActorValidationResult; } - if (!internalResult.AddResult(validationParameters.TypeValidator( - jsonWebToken.Typ, jsonWebToken, validationParameters, callContext))) - return internalResult; + Result typeValidationResult = validationParameters.TypeValidator( + jsonWebToken.Typ, jsonWebToken, validationParameters, callContext); + if (!typeValidationResult.IsSuccess) + { + StackFrame typeValidationFailureStackFrame = StackFrames.TypeValidationFailed ??= new StackFrame(true); + return typeValidationResult.UnwrapError().AddStackFrame(typeValidationFailureStackFrame); + } // The signature validation delegate is yet to be migrated to ValidationParameters. - if (!internalResult.AddResult(ValidateSignature( - jsonWebToken, validationParameters, configuration, callContext))) - return internalResult; + Result signatureValidationResult = ValidateSignature( + jsonWebToken, validationParameters, configuration, callContext); + if (!signatureValidationResult.IsSuccess) + { + StackFrame signatureValidationFailureStackFrame = StackFrames.SignatureValidationFailed ??= new StackFrame(true); + return signatureValidationResult.UnwrapError().AddStackFrame(signatureValidationFailureStackFrame); + } - if (!internalResult.AddResult(validationParameters.IssuerSigningKeyValidator( - jsonWebToken.SigningKey, jsonWebToken, validationParameters, configuration, callContext))) - return internalResult; + Result issuerSigningKeyValidationResult = + validationParameters.IssuerSigningKeyValidator( + signatureValidationResult.UnwrapResult(), jsonWebToken, validationParameters, configuration, callContext); + if (!issuerSigningKeyValidationResult.IsSuccess) + { + StackFrame issuerSigningKeyValidationFailureStackFrame = StackFrames.IssuerSigningKeyValidationFailed ??= new StackFrame(true); + return issuerSigningKeyValidationResult.UnwrapError().AddStackFrame(issuerSigningKeyValidationFailureStackFrame); + } - return internalResult; + return new ValidationResult(jsonWebToken, this, validationParameters) + { + ValidatedLifetime = lifetimeValidationResult.UnwrapResult(), + ValidatedAudience = audienceValidationResult.UnwrapResult(), + ValidatedIssuer = issuerValidationResult.UnwrapResult(), + ValidatedTokenReplayExpirationTime = replayValidationResult.UnwrapResult(), + ActorValidationResult = actorValidationResult?.UnwrapResult(), + ValidatedTokenType = typeValidationResult.UnwrapResult(), + ValidatedSigningKey = signatureValidationResult.UnwrapResult(), + ValidatedSigningKeyLifetime = issuerSigningKeyValidationResult.UnwrapResult() + }; } - private static async Task GetCurrentConfigurationAsync(ValidationParameters validationParameters) + private static async Task GetCurrentConfigurationAsync(ValidationParameters validationParameters) { - BaseConfiguration currentConfiguration = null; + BaseConfiguration? currentConfiguration = null; if (validationParameters.ConfigurationManager is not null) { try @@ -293,13 +373,14 @@ private static async Task GetCurrentConfigurationAsync(Valida currentConfiguration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false); } #pragma warning disable CA1031 // Do not catch general exception types - catch (Exception ex) + catch #pragma warning restore CA1031 // Do not catch general exception types { // The exception is tracked and dismissed as the ValidationParameters may have the issuer // and signing key set directly on them, allowing the library to continue with token validation. - if (LogHelper.IsEnabled(EventLogLevel.Warning)) - LogHelper.LogWarning(LogHelper.FormatInvariant(TokenLogMessages.IDX10261, validationParameters.ConfigurationManager.MetadataAddress, ex.ToString())); + // TODO: Move to CallContext. + //if (LogHelper.IsEnabled(EventLogLevel.Warning)) + // LogHelper.LogWarning(LogHelper.FormatInvariant(TokenLogMessages.IDX10261, validationParameters.ConfigurationManager.MetadataAddress, ex.ToString())); } } @@ -307,3 +388,4 @@ private static async Task GetCurrentConfigurationAsync(Valida } } } +#nullable restore diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.StackFrames.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.StackFrames.cs new file mode 100644 index 0000000000..9f6ff9324c --- /dev/null +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.StackFrames.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Diagnostics; +using Microsoft.IdentityModel.Tokens; + +#nullable enable + +namespace Microsoft.IdentityModel.JsonWebTokens +{ + public partial class JsonWebTokenHandler : TokenHandler + { + // Cached stack frames to build exceptions from validation errors + internal static class StackFrames + { + // ValidateTokenAsync from string + internal static StackFrame? TokenStringNull; + internal static StackFrame? TokenStringValidationParametersNull; + internal static StackFrame? InvalidTokenLength; + internal static StackFrame? TokenStringValidationFailed; + internal static StackFrame? TokenStringReadFailed; + // ValidateTokenAsync from SecurityToken + internal static StackFrame? TokenNull; + internal static StackFrame? TokenValidationParametersNull; + internal static StackFrame? TokenNotJWT; + internal static StackFrame? TokenValidationFailedNullConfigurationManager; + internal static StackFrame? TokenValidationFailed; + // ValidateJWEAsync + internal static StackFrame? DecryptionFailed; + internal static StackFrame? DecryptedReadFailed; + internal static StackFrame? JWEValidationFailed; + // ValidateJWSAsync + internal static StackFrame? LifetimeValidationFailed; + internal static StackFrame? AudienceValidationFailed; + internal static StackFrame? IssuerValidationFailed; + internal static StackFrame? ReplayValidationFailed; + internal static StackFrame? ActorReadFailed; + internal static StackFrame? ActorValidationFailed; + internal static StackFrame? TypeValidationFailed; + internal static StackFrame? SignatureValidationFailed; + internal static StackFrame? IssuerSigningKeyValidationFailed; + // DecryptToken + internal static StackFrame? DecryptionTokenNull; + internal static StackFrame? DecryptionValidationParametersNull; + internal static StackFrame? DecryptionHeaderMissing; + internal static StackFrame? DecryptionGetEncryptionKeys; + internal static StackFrame? DecryptionNoKeysTried; + internal static StackFrame? DecryptionKeyUnwrapFailed; + // ReadToken + internal static StackFrame? ReadTokenNullOrEmpty; + internal static StackFrame? ReadTokenMalformed; + } + } +} +#nullable restore diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.DecryptTokenResult.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.DecryptTokenResult.cs index 4b300e8aa2..330f18b3f3 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.DecryptTokenResult.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.DecryptTokenResult.cs @@ -2,12 +2,10 @@ // Licensed under the MIT License. using System; +using System.Diagnostics; using System.Text; using Microsoft.IdentityModel.Tokens; -using Microsoft.IdentityModel.Logging; -using Microsoft.IdentityModel.Abstractions; using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; -using System.Diagnostics; namespace Microsoft.IdentityModel.JsonWebTokens { @@ -21,33 +19,21 @@ public partial class JwtTokenUtilities /// The decryption parameters container. /// The call context used for logging. /// The decrypted, and if the 'zip' claim is set, decompressed string representation of the token. - internal static TokenDecryptionResult DecryptJwtToken( + internal static Result DecryptJwtToken( JsonWebToken jsonWebToken, ValidationParameters validationParameters, JwtTokenDecryptionParameters decryptionParameters, CallContext callContext) { if (validationParameters == null) - return new TokenDecryptionResult( - jsonWebToken, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - TokenLogMessages.IDX10000, - nameof(validationParameters)), - ExceptionDetail.ExceptionType.ArgumentNull, - new System.Diagnostics.StackFrame())); + return ExceptionDetail.NullParameter( + nameof(validationParameters), + new StackFrame(true)); if (decryptionParameters == null) - return new TokenDecryptionResult( - jsonWebToken, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - TokenLogMessages.IDX10000, - nameof(decryptionParameters)), - ExceptionDetail.ExceptionType.ArgumentNull, - new System.Diagnostics.StackFrame())); + return ExceptionDetail.NullParameter( + nameof(decryptionParameters), + new StackFrame(true)); bool decryptionSucceeded = false; bool algorithmNotSupportedByCryptoProvider = false; @@ -62,8 +48,9 @@ internal static TokenDecryptionResult DecryptJwtToken( var cryptoProviderFactory = validationParameters.CryptoProviderFactory ?? key.CryptoProviderFactory; if (cryptoProviderFactory == null) { - if (LogHelper.IsEnabled(EventLogLevel.Warning)) - LogHelper.LogWarning(TokenLogMessages.IDX10607, key); + // TODO: Move to CallContext + //if (LogHelper.IsEnabled(EventLogLevel.Warning)) + // LogHelper.LogWarning(TokenLogMessages.IDX10607, key); continue; } @@ -72,17 +59,18 @@ internal static TokenDecryptionResult DecryptJwtToken( { if (!cryptoProviderFactory.IsSupportedAlgorithm(jsonWebToken.Enc, key)) { - if (LogHelper.IsEnabled(EventLogLevel.Warning)) - LogHelper.LogWarning(TokenLogMessages.IDX10611, LogHelper.MarkAsNonPII(decryptionParameters.Enc), key); + //TODO: Move to CallContext + //if (LogHelper.IsEnabled(EventLogLevel.Warning)) + // LogHelper.LogWarning(TokenLogMessages.IDX10611, LogHelper.MarkAsNonPII(decryptionParameters.Enc), key); algorithmNotSupportedByCryptoProvider = true; continue; } - AlgorithmValidationResult result = validationParameters.AlgorithmValidator(zipAlgorithm, key, jsonWebToken, validationParameters, callContext); - if (!result.IsValid) + Result result = validationParameters.AlgorithmValidator(zipAlgorithm, key, jsonWebToken, validationParameters, callContext); + if (!result.IsSuccess) { - (exceptionStrings ??= new StringBuilder()).AppendLine(result.ExceptionDetail.MessageDetail.Message); + (exceptionStrings ??= new StringBuilder()).AppendLine(result.UnwrapError().MessageDetail.Message); continue; } @@ -111,15 +99,12 @@ internal static TokenDecryptionResult DecryptJwtToken( } if (!decryptionSucceeded) - return new TokenDecryptionResult( - jsonWebToken, - ValidationFailureType.TokenDecryptionFailed, - GetDecryptionExceptionDetail( - decryptionParameters, - algorithmNotSupportedByCryptoProvider, - exceptionStrings, - keysAttempted, - callContext)); + return GetDecryptionError( + decryptionParameters, + algorithmNotSupportedByCryptoProvider, + exceptionStrings, + keysAttempted, + callContext); try { @@ -129,22 +114,18 @@ internal static TokenDecryptionResult DecryptJwtToken( else decodedString = decryptionParameters.DecompressionFunction(decryptedTokenBytes, zipAlgorithm, decryptionParameters.MaximumDeflateSize); - return new TokenDecryptionResult(decodedString, jsonWebToken); + return decodedString; } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { - return new TokenDecryptionResult( - jsonWebToken, + return new ExceptionDetail( + new MessageDetail(TokenLogMessages.IDX10679, zipAlgorithm), ValidationFailureType.TokenDecryptionFailed, - new ExceptionDetail( - new MessageDetail( - TokenLogMessages.IDX10679, - zipAlgorithm), - ExceptionDetail.ExceptionType.SecurityTokenDecompressionFailed, - new StackFrame(), - ex)); + ExceptionType.SecurityTokenDecompressionFailed, + new StackFrame(true), + ex); } } } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs index dfcd751d5f..e92d0e1d48 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs @@ -331,12 +331,16 @@ internal static string DecryptJwtToken( } if (!decryptionSucceeded) - throw GetDecryptionExceptionDetail( + { + ExceptionDetail exceptionDetail = GetDecryptionError( decryptionParameters, algorithmNotSupportedByCryptoProvider, exceptionStrings, keysAttempted, - null).GetException(); + null); + + throw LogHelper.LogExceptionMessage(exceptionDetail.GetException()); + } try { @@ -351,7 +355,7 @@ internal static string DecryptJwtToken( } } - private static ExceptionDetail GetDecryptionExceptionDetail( + private static ExceptionDetail GetDecryptionError( JwtTokenDecryptionParameters decryptionParameters, bool algorithmNotSupportedByCryptoProvider, StringBuilder exceptionStrings, @@ -367,7 +371,8 @@ private static ExceptionDetail GetDecryptionExceptionDetail( keysAttempted.ToString(), exceptionStrings?.ToString() ?? string.Empty, LogHelper.MarkAsSecurityArtifact(decryptionParameters.EncodedToken, SafeLogJwtToken)), - ExceptionDetail.ExceptionType.SecurityTokenDecryptionFailed, + ValidationFailureType.TokenDecryptionFailed, + ExceptionType.SecurityTokenDecryptionFailed, new StackFrame(true), null); else if (algorithmNotSupportedByCryptoProvider) @@ -376,17 +381,17 @@ private static ExceptionDetail GetDecryptionExceptionDetail( TokenLogMessages.IDX10619, LogHelper.MarkAsNonPII(decryptionParameters.Alg), LogHelper.MarkAsNonPII(decryptionParameters.Enc)), - ExceptionDetail.ExceptionType.SecurityTokenDecryptionFailed, - new StackFrame(true), - null); + ValidationFailureType.TokenDecryptionFailed, + ExceptionType.SecurityTokenDecryptionFailed, + new StackFrame(true)); else return new ExceptionDetail( new MessageDetail( TokenLogMessages.IDX10609, LogHelper.MarkAsSecurityArtifact(decryptionParameters.EncodedToken, SafeLogJwtToken)), - ExceptionDetail.ExceptionType.SecurityTokenDecryptionFailed, - new StackFrame(true), - null); + ValidationFailureType.TokenDecryptionFailed, + ExceptionType.SecurityTokenDecryptionFailed, + new StackFrame(true)); } private static byte[] DecryptToken(CryptoProviderFactory cryptoProviderFactory, SecurityKey key, string encAlg, byte[] ciphertext, byte[] headerAscii, byte[] initializationVector, byte[] authenticationTag) diff --git a/src/Microsoft.IdentityModel.Tokens/Delegates.cs b/src/Microsoft.IdentityModel.Tokens/Delegates.cs index 0587164690..5bfcb566f8 100644 --- a/src/Microsoft.IdentityModel.Tokens/Delegates.cs +++ b/src/Microsoft.IdentityModel.Tokens/Delegates.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Microsoft.IdentityModel.JsonWebTokens.Results; namespace Microsoft.IdentityModel.Tokens { @@ -205,7 +204,7 @@ namespace Microsoft.IdentityModel.Tokens /// The to be used for logging. /// This method is not expected to throw. /// The validated . - internal delegate SignatureValidationResult SignatureValidatorDelegate(SecurityToken token, ValidationParameters validationParameters, BaseConfiguration? configuration, CallContext? callContext); + internal delegate Result SignatureValidatorDelegate(SecurityToken token, ValidationParameters validationParameters, BaseConfiguration? configuration, CallContext? callContext); /// /// Transforms the security token before signature validation. diff --git a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs index ec1fbdf345..460a9012d8 100644 --- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs @@ -14,6 +14,7 @@ internal static class LogMessages #pragma warning disable 1591 // general public const string IDX10000 = "IDX10000: The parameter '{0}' cannot be a 'null' or an empty object. "; + public const string IDX10001 = "IDX10001: Invalid argument '{0}'. Argument must be of type '{1}'."; // properties, configuration public const string IDX10101 = "IDX10101: MaximumTokenSizeInBytes must be greater than zero. value: '{0}'"; diff --git a/src/Microsoft.IdentityModel.Tokens/TokenUtilities.cs b/src/Microsoft.IdentityModel.Tokens/TokenUtilities.cs index 00eccab114..5047904a92 100644 --- a/src/Microsoft.IdentityModel.Tokens/TokenUtilities.cs +++ b/src/Microsoft.IdentityModel.Tokens/TokenUtilities.cs @@ -250,19 +250,21 @@ internal static IEnumerable MergeClaims(IEnumerable claims, IEnume /// true if the exception is certain types of exceptions otherwise, false. internal static bool IsRecoverableException(Exception exception) { - return IsRecoverableExceptionType(ExceptionTypeForException(exception)); + return IsRecoverableErrorType(ErrorTypeForException(exception)); } /// /// Check whether the given exception type is recoverable by LKG. /// - /// The exception type to check. + /// The exception type to check. /// true if the exception is certain types of exceptions otherwise, false. - internal static bool IsRecoverableExceptionType(ExceptionDetail.ExceptionType exceptionType) + internal static bool IsRecoverableErrorType(ExceptionType? errorType) { - return exceptionType == ExceptionDetail.ExceptionType.SecurityTokenInvalidSignature - || exceptionType == ExceptionDetail.ExceptionType.SecurityTokenInvalidIssuer - || exceptionType == ExceptionDetail.ExceptionType.SecurityTokenSignatureKeyNotFound; + ExceptionType typeToCheck = errorType ?? ExceptionType.Unknown; + + return typeToCheck == ExceptionType.SecurityTokenInvalidSignature + || typeToCheck == ExceptionType.SecurityTokenInvalidIssuer + || typeToCheck == ExceptionType.SecurityTokenSignatureKeyNotFound; } /// @@ -277,7 +279,7 @@ internal static bool IsRecoverableConfiguration( string kid, BaseConfiguration currentConfiguration, BaseConfiguration lkgConfiguration, Exception currentException) { return IsRecoverableConfigurationAndExceptionType( - kid, currentConfiguration, lkgConfiguration, ExceptionTypeForException(currentException)); + kid, currentConfiguration, lkgConfiguration, ErrorTypeForException(currentException)); } /// @@ -289,19 +291,19 @@ internal static bool IsRecoverableConfiguration( /// The exception type to check. /// true if the configuration is recoverable otherwise, false. internal static bool IsRecoverableConfigurationAndExceptionType( - string kid, BaseConfiguration currentConfiguration, BaseConfiguration lkgConfiguration, ExceptionDetail.ExceptionType currentExceptionType) + string kid, BaseConfiguration currentConfiguration, BaseConfiguration lkgConfiguration, ExceptionType currentExceptionType) { Lazy isRecoverableSigningKey = new(() => lkgConfiguration.SigningKeys.Any(signingKey => signingKey.KeyId == kid)); - if (currentExceptionType == ExceptionDetail.ExceptionType.SecurityTokenInvalidIssuer) + if (currentExceptionType == ExceptionType.SecurityTokenInvalidIssuer) { return currentConfiguration.Issuer != lkgConfiguration.Issuer; } - else if (currentExceptionType == ExceptionDetail.ExceptionType.SecurityTokenSignatureKeyNotFound) + else if (currentExceptionType == ExceptionType.SecurityTokenSignatureKeyNotFound) { return isRecoverableSigningKey.Value; } - else if (currentExceptionType == ExceptionDetail.ExceptionType.SecurityTokenInvalidSignature) + else if (currentExceptionType == ExceptionType.SecurityTokenInvalidSignature) { SecurityKey currentSigningKey = currentConfiguration.SigningKeys.FirstOrDefault(x => x.KeyId == kid); if (currentSigningKey == null) @@ -314,16 +316,16 @@ internal static bool IsRecoverableConfigurationAndExceptionType( return false; } - static ExceptionDetail.ExceptionType ExceptionTypeForException(Exception exception) + static ExceptionType ErrorTypeForException(Exception exception) { if (exception is SecurityTokenInvalidSignatureException) - return ExceptionDetail.ExceptionType.SecurityTokenInvalidSignature; + return ExceptionType.SecurityTokenInvalidSignature; else if (exception is SecurityTokenInvalidIssuerException) - return ExceptionDetail.ExceptionType.SecurityTokenInvalidIssuer; + return ExceptionType.SecurityTokenInvalidIssuer; else if (exception is SecurityTokenSignatureKeyNotFoundException) - return ExceptionDetail.ExceptionType.SecurityTokenSignatureKeyNotFound; + return ExceptionType.SecurityTokenSignatureKeyNotFound; else - return ExceptionDetail.ExceptionType.Unknown; + return ExceptionType.Unknown; } } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/AlgorithmValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/AlgorithmValidationResult.cs deleted file mode 100644 index 1946ee3b02..0000000000 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/AlgorithmValidationResult.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -#nullable enable -namespace Microsoft.IdentityModel.Tokens -{ - /// - /// Contains the result of validating the Algorithm of a . - /// The contains a collection of for each step in the token validation. - /// - internal class AlgorithmValidationResult : ValidationResult - { - private Exception? _exception; - private const string TokenSource = "Microsoft.IdentityModel.Tokens"; - - /// - /// Creates an instance of . - /// - /// The algorithm to be validated. - public AlgorithmValidationResult(string? algorithm) - : base(ValidationFailureType.ValidationSucceeded) - { - Algorithm = algorithm; - IsValid = true; - } - - /// - /// Creates an instance of - /// - /// The algorithm to be validated. - /// is the that occurred during validation. - /// is the that occurred during validation. - public AlgorithmValidationResult(string? algorithm, ValidationFailureType validationFailure, ExceptionDetail exceptionDetail) - : base(validationFailure, exceptionDetail) - { - Algorithm = algorithm; - IsValid = false; - } - - /// - /// Gets the that occurred during validation. - /// - public override Exception? Exception - { - get - { - if (_exception != null || ExceptionDetail == null) - return _exception; - - HasValidOrExceptionWasRead = true; - _exception = ExceptionDetail.GetException(); - if (_exception is SecurityTokenInvalidAlgorithmException securityTokenInvalidAlgorithmException) - { - securityTokenInvalidAlgorithmException.InvalidAlgorithm = Algorithm; - securityTokenInvalidAlgorithmException.Source = TokenSource; - } - - return _exception; - } - } - - /// - /// Gets the algorithm used to sign the token. - /// - public string? Algorithm { get; } - - } -} -#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/AudienceValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/AudienceValidationResult.cs deleted file mode 100644 index 024cca68ba..0000000000 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/AudienceValidationResult.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; - -namespace Microsoft.IdentityModel.Tokens -{ - /// - /// Contains the result of validating the audiences from a . - /// The contains a collection of for each step in the token validation. - /// - internal class AudienceValidationResult : ValidationResult - { - private Exception _exception; - - /// - /// Creates an instance of . - /// - /// is the audience that was validated successfully. - public AudienceValidationResult(string audience) : base(ValidationFailureType.ValidationSucceeded) - { - IsValid = true; - Audience = audience; - } - - /// - /// Creates an instance of - /// - /// is the audience that was intended to be validated. - /// is the that occurred during validation. - /// is the that occurred during validation. - public AudienceValidationResult(string audience, ValidationFailureType validationFailure, ExceptionDetail exceptionDetail) - : base(validationFailure, exceptionDetail) - { - IsValid = false; - Audience = audience; - } - - /// - /// Gets the that occurred during validation. - /// - public override Exception Exception - { - get - { - if (_exception != null || ExceptionDetail == null) - return _exception; - - HasValidOrExceptionWasRead = true; - _exception = ExceptionDetail.GetException(); - if (_exception is SecurityTokenInvalidAudienceException securityTokenInvalidAudienceException) - { - securityTokenInvalidAudienceException.InvalidAudience = Audience; - securityTokenInvalidAudienceException.ExceptionDetail = ExceptionDetail; - securityTokenInvalidAudienceException.Source = "Microsoft.IdentityModel.Tokens"; - } - - return _exception; - } - } - - /// - /// Gets the audience that was validated or intended to be validated. - /// - public string Audience { get; } - } -} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ExceptionDetail.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ExceptionDetail.cs index c2416d3be4..4e884a997b 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ExceptionDetail.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ExceptionDetail.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using Microsoft.IdentityModel.Logging; namespace Microsoft.IdentityModel.Tokens { @@ -16,41 +15,62 @@ internal class ExceptionDetail /// /// Creates an instance of /// - /// contains information about the exception that is used to generate the exception message. - /// is the type of exception that occurred. - /// contains information about the stack frame where the exception occurred. - public ExceptionDetail(MessageDetail messageDetail, ExceptionType exceptionType, StackFrame stackFrame) - : this(messageDetail, exceptionType, stackFrame, null) + /// contains information about the exception that is used to generate the exception message. + /// is the type of validation failure that occurred. + /// is the type of exception that occurred. + /// is the stack frame where the exception occurred. + public ExceptionDetail(MessageDetail MessageDetail, ValidationFailureType failureType, ExceptionType exceptionType, StackFrame stackFrame) + : this(MessageDetail, failureType, exceptionType, stackFrame, null) { } /// /// Creates an instance of /// - /// contains information about the exception that is used to generate the exception message. - /// is the type of exception that occurred. - /// contains information about the stack frame where the exception occurred. - /// is the inner exception that occurred. - public ExceptionDetail(MessageDetail messageDetail, ExceptionType exceptionType, StackFrame stackFrame, Exception innerException) + /// contains information about the exception that is used to generate the exception message. + /// is the type of validation failure that occurred. + /// is the type of exception that occurred. + /// is the stack frame where the exception occurred. + /// is the inner exception that occurred. + public ExceptionDetail( + MessageDetail messageDetail, + ValidationFailureType failureType, + ExceptionType exceptionType, + StackFrame stackFrame, + Exception innerException) { Type = exceptionType; InnerException = innerException; MessageDetail = messageDetail; - StackFrames.Add(stackFrame); + FailureType = failureType; + StackFrames = new List(4) + { + stackFrame + }; } /// /// Creates an instance of an using /// /// An instantance of an Exception. - public Exception GetException() => ExceptionFromType(Type, InnerException); + public Exception GetException() + { + Exception exception = ExceptionFromType(Type, InnerException); + if (exception is SecurityTokenException securityTokenException) + securityTokenException.ExceptionDetail = this; - internal static ExceptionDetail NullParameter(string parameterName) => new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII(parameterName)), - ExceptionType.ArgumentNull, - new StackFrame()); + return exception; + } + + internal static ExceptionDetail NullParameter(string parameterName, StackFrame stackFrame) => new ExceptionDetail( + MessageDetail.NullParameter(parameterName), + ValidationFailureType.NullArgument, + ExceptionType.ArgumentNull, stackFrame); + + /// + /// Gets the type of validation failure that occurred. + /// + public ValidationFailureType FailureType { get; } /// /// Gets the type of exception that occurred. @@ -70,32 +90,17 @@ public ExceptionDetail(MessageDetail messageDetail, ExceptionType exceptionType, /// /// Gets the stack frames where the exception occurred. /// - public IList StackFrames { get; } = []; + public IList StackFrames { get; } - public enum ExceptionType + /// + /// Adds a stack frame to the list of stack frames and returns the updated object. + /// + /// The to be added. + /// + public ExceptionDetail AddStackFrame(StackFrame stackFrame) { - Unknown = -1, - ArgumentNull, - InvalidOperation, - SecurityToken, - SecurityTokenDecompressionFailed, - SecurityTokenDecryptionFailed, - SecurityTokenExpired, - SecurityTokenInvalidAudience, - SecurityTokenInvalidAlgorithm, - SecurityTokenInvalidIssuer, - SecurityTokenInvalidLifetime, - SecurityTokenInvalidSigningKey, - SecurityTokenInvalidSignature, - SecurityTokenInvalidType, - SecurityTokenKeyWrap, - SecurityTokenMalformed, - SecurityTokenNoExpiration, - SecurityTokenNotYetValid, - SecurityTokenReplayDetected, - SecurityTokenReplayAddFailed, - SecurityTokenSignatureKeyNotFound, - ExceptionTypeCount + StackFrames.Add(stackFrame); + return this; } private Exception ExceptionFromType(ExceptionType exceptionType, Exception innerException) @@ -104,6 +109,8 @@ private Exception ExceptionFromType(ExceptionType exceptionType, Exception inner { case ExceptionType.ArgumentNull: return new ArgumentNullException(MessageDetail.Message, innerException); + case ExceptionType.InvalidArgument: + return new ArgumentException(MessageDetail.Message, innerException); case ExceptionType.InvalidOperation: return new InvalidOperationException(MessageDetail.Message, innerException); case ExceptionType.SecurityToken: @@ -147,4 +154,31 @@ private Exception ExceptionFromType(ExceptionType exceptionType, Exception inner } } } + + internal enum ExceptionType + { + Unknown = -1, + ArgumentNull, + InvalidArgument, + InvalidOperation, + SecurityToken, + SecurityTokenDecompressionFailed, + SecurityTokenDecryptionFailed, + SecurityTokenExpired, + SecurityTokenInvalidAudience, + SecurityTokenInvalidAlgorithm, + SecurityTokenInvalidIssuer, + SecurityTokenInvalidLifetime, + SecurityTokenInvalidSigningKey, + SecurityTokenInvalidSignature, + SecurityTokenInvalidType, + SecurityTokenKeyWrap, + SecurityTokenMalformed, + SecurityTokenNoExpiration, + SecurityTokenNotYetValid, + SecurityTokenReplayDetected, + SecurityTokenReplayAddFailed, + SecurityTokenSignatureKeyNotFound, + ExceptionTypeCount + } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/MessageDetail.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/MessageDetail.cs index 0282247ec3..188e3a51b8 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/MessageDetail.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/MessageDetail.cs @@ -26,6 +26,9 @@ public MessageDetail(string formatString, params object[] parameters) Parameters = parameters; } + public static MessageDetail NullParameter(string parameterName) + => new MessageDetail(LogMessages.IDX10000, LogHelper.MarkAsNonPII(parameterName)); + /// /// Gets the formatted message. /// diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/InternalTokenValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/InternalTokenValidationResult.cs deleted file mode 100644 index c4efc371c5..0000000000 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/InternalTokenValidationResult.cs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; - -namespace Microsoft.IdentityModel.Tokens -{ -#nullable enable - /// - /// Internal class used to track the results of token validation and to provide a way to merge results. - /// Once all validation is complete, the results can be converted to a TokenValidationResult. - /// - internal class InternalTokenValidationResult - { - private bool _isValid; - private SecurityToken? _securityToken; - private TokenHandler _tokenHandler; - private List _validationResults = []; - - /// - /// Creates a new instance of to aggregate validation results. - /// - /// The being validated. - /// The performing the validation. - /// - public InternalTokenValidationResult(SecurityToken? securityToken, TokenHandler tokenHandler) - { - _securityToken = securityToken; - _tokenHandler = tokenHandler ?? throw new ArgumentNullException(nameof(tokenHandler)); - _isValid = true; - } - - /// - /// Adds a to the aggregated list of validation results. - /// - /// The to store. - /// The current IsValid value for the validation. - /// - public bool AddResult(ValidationResult validationResult) - { - if (validationResult == null) - throw new ArgumentNullException(nameof(validationResult)); - - _validationResults.Add(validationResult); - _isValid = _isValid && validationResult.IsValid; - - return IsValid; - } - - /// - /// Adds a list of to the aggregated list of validation results. - /// - /// The list of to store. - /// The current IsValid value for the validation. - /// - public bool AddResults(IList validationResults) - { - if (validationResults == null) - throw new ArgumentNullException(nameof(validationResults)); - - foreach (var validationResult in validationResults) - { - _ = AddResult(validationResult); - } - - return IsValid; - } - - /// - /// Gets the for the first failed validation result. - /// - public ExceptionDetail? ExceptionDetail - { - get - { - if (ValidationResults.Count == 0) - return null; - - // Iterate in reverse since the failure should be the last result - for (int i = ValidationResults.Count - 1; i >= 0; i--) - { - ValidationResult validationResult = ValidationResults[i]; - if (validationResult.ExceptionDetail != null) - return validationResult.ExceptionDetail; - } - - return null; - } - } - - /// - /// Gets a value indicating whether the token is valid. - /// - public bool IsValid => _isValid; - - /// - /// Merges the results of another into this instance. - /// Updates the and in case they changed. - /// - /// The to be merged. - /// - public bool Merge(InternalTokenValidationResult other) - { - _securityToken = other._securityToken; - _tokenHandler = other._tokenHandler; - - return AddResults(other.ValidationResults); - } - - /// - /// Gets the being validated. - /// - public SecurityToken? SecurityToken => _securityToken; - - /// - /// Returns a based on the aggregated validation results. - /// - /// The containing the result of aggregating all the individual results. - public TokenValidationResult ToTokenValidationResult() - { - if (IsValid) - { - // TokenValidationResult uses TokenValidationParameters to create ClaimsIdentity. - // We need to figure the best way to refactor that, ideally without creating a new TokenValidationResult class. - return new TokenValidationResult( - _securityToken, _tokenHandler, new TokenValidationParameters(), "issuer", _validationResults) - { - IsValid = true - }; - } - - return new TokenValidationResult - { - IsValid = false, - Exception = ExceptionDetail?.GetException(), // Need to introduce ExceptionDetail to TokenValidationResult - }; - } - - /// - /// Gets the list of that were aggregated. - /// - public IList ValidationResults => _validationResults; - } -#nullable restore -} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/IssuerValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/IssuerValidationResult.cs deleted file mode 100644 index 292eacfab1..0000000000 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/IssuerValidationResult.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; - -namespace Microsoft.IdentityModel.Tokens -{ - /// - /// Contains the result of validating a issuer. - /// The contains a collection of for each step in the token validation. - /// - internal class IssuerValidationResult : ValidationResult - { - internal enum ValidationSource - { - NotValidated = 0, - IssuerIsConfigurationIssuer, - IssuerIsValidIssuer, - IssuerIsAmongValidIssuers - } - - private Exception _exception; - - /// - /// Creates an instance of - /// - /// is the issuer that was validated successfully. - /// is the indicating how this issuer was validated. - public IssuerValidationResult(string issuer, ValidationSource source = ValidationSource.NotValidated) - : base(ValidationFailureType.ValidationSucceeded) - { - Issuer = issuer; - IsValid = true; - Source = source; - } - - /// - /// Creates an instance of - /// - /// is the issuer that was intended to be validated. - /// is the that occurred during validation. - /// is the that occurred during validation. - /// is the indicating how this issuer was validated. - public IssuerValidationResult(string issuer, ValidationFailureType validationFailure, ExceptionDetail exceptionDetail, ValidationSource source = ValidationSource.NotValidated) - : base(validationFailure, exceptionDetail) - { - Issuer = issuer; - IsValid = false; - Source = source; - } - - /// - /// Gets the that occurred during validation. - /// - public override Exception Exception - { - get - { - if (_exception != null || ExceptionDetail == null) - return _exception; - - HasValidOrExceptionWasRead = true; - _exception = ExceptionDetail.GetException(); - SecurityTokenInvalidIssuerException securityTokenInvalidIssuerException = _exception as SecurityTokenInvalidIssuerException; - if (securityTokenInvalidIssuerException != null) - { - securityTokenInvalidIssuerException.InvalidIssuer = Issuer; - securityTokenInvalidIssuerException.ExceptionDetail = ExceptionDetail; - securityTokenInvalidIssuerException.Source = "Microsoft.IdentityModel.Tokens"; - } - - return _exception; - } - } - - /// - /// Gets the issuer that was validated or intended to be validated. - /// - public string Issuer { get; } - - public ValidationSource Source { get; } - } -} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/LifetimeValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/LifetimeValidationResult.cs deleted file mode 100644 index f40608a574..0000000000 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/LifetimeValidationResult.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -#nullable enable -namespace Microsoft.IdentityModel.Tokens -{ - /// - /// Contains the result of validating the lifetime of a . - /// The contains a collection of for each step in the token validation. - /// - internal class LifetimeValidationResult : ValidationResult - { - private Exception? _exception; - - /// - /// Creates an instance of - /// - /// is the date from which the token that was validated successfully is valid. - /// is the expiration date for the token that was validated successfully. - public LifetimeValidationResult(DateTime? notBefore, DateTime? expires) - : base(ValidationFailureType.ValidationSucceeded) - { - NotBefore = notBefore; - Expires = expires; - IsValid = true; - } - - /// - /// Creates an instance of - /// - /// is the date from which the token is valid. - /// is the expiration date for the token. - /// is the that occurred during validation. - /// is the that occurred during validation. - public LifetimeValidationResult(DateTime? notBefore, DateTime? expires, ValidationFailureType validationFailure, ExceptionDetail exceptionDetail) - : base(validationFailure, exceptionDetail) - { - NotBefore = notBefore; - Expires = expires; - IsValid = false; - } - - /// - /// Gets the that occurred during validation. - /// - public override Exception? Exception - { - get - { - if (_exception != null || ExceptionDetail == null) - return _exception; - - HasValidOrExceptionWasRead = true; - _exception = ExceptionDetail.GetException(); - if (_exception is SecurityTokenInvalidLifetimeException securityTokenInvalidLifetimeException) - { - securityTokenInvalidLifetimeException.NotBefore = NotBefore; - securityTokenInvalidLifetimeException.Expires = Expires; - securityTokenInvalidLifetimeException.Source = "Microsoft.IdentityModel.Tokens"; - } - - return _exception; - } - } - - /// - /// Gets the date from which the token is valid. - /// - public DateTime? NotBefore { get; } - - /// - /// Gets the expiration date for the token. - /// - public DateTime? Expires { get; } - } -} -#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ReplayValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ReplayValidationResult.cs deleted file mode 100644 index dfaa0439b6..0000000000 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ReplayValidationResult.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; - -#nullable enable -namespace Microsoft.IdentityModel.Tokens -{ - /// - /// Contains the result of validating that a has not been replayed. - /// The contains a collection of for each step in the token validation. - /// - internal class ReplayValidationResult : ValidationResult - { - private Exception? _exception; - - /// - /// Creates an instance of . - /// - /// is the expiration date against which the token was validated. - public ReplayValidationResult(DateTime? expirationTime) : base(ValidationFailureType.ValidationSucceeded) - { - IsValid = true; - ExpirationTime = expirationTime; - } - - /// - /// Creates an instance of - /// - /// is the expiration date against which the token was validated. - /// is the that occurred during validation. - /// is the that occurred during validation. - public ReplayValidationResult(DateTime? expirationTime, ValidationFailureType validationFailure, ExceptionDetail exceptionDetail) - : base(validationFailure, exceptionDetail) - { - IsValid = false; - ExpirationTime = expirationTime; - } - - /// - /// Gets the that occurred during validation. - /// - public override Exception? Exception - { - get - { - if (_exception != null || ExceptionDetail == null) - return _exception; - - HasValidOrExceptionWasRead = true; - _exception = ExceptionDetail.GetException(); - _exception.Source = "Microsoft.IdentityModel.Tokens"; - - if (_exception is SecurityTokenReplayDetectedException securityTokenReplayDetectedException) - { - securityTokenReplayDetectedException.ExceptionDetail = ExceptionDetail; - } - else if (_exception is SecurityTokenReplayAddFailedException securityTokenReplayAddFailedException) - { - securityTokenReplayAddFailedException.ExceptionDetail = ExceptionDetail; - } - - return _exception; - } - } - - /// - /// Gets the expiration date against which the token was validated. - /// - public DateTime? ExpirationTime { get; } - } -} -#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Result.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Result.cs new file mode 100644 index 0000000000..9bf349fa18 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Result.cs @@ -0,0 +1,159 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Represents a result that can be either successful or unsuccessful. + /// + /// + /// + internal readonly struct Result : IEquatable> + { + readonly TResult? _result; + readonly TError? _error; + + /// + /// Creates a successful result. + /// + /// The value associated with the success. + public Result(TResult result) + { + _result = result; + _error = default; + IsSuccess = true; + } + + /// + /// Creates an error result. + /// + /// The error associated with the failure. + public Result(TError error) + { + _result = default; + _error = error; + IsSuccess = false; + } + + /// + /// Empty constructor implementation to prevent creating an empty result. + /// + /// Throws an when called as this should never be used. Always initialize Result with either a value or error. + /// Thrown when called. + [Obsolete("Cannot create an empty result", true)] + public Result() => throw new InvalidOperationException("Cannot create an empty result"); + + /// + /// Creates a successful result implicitly from the value. + /// + /// The value to be stored in the result. + public static implicit operator Result(TResult result) => new(result); + + /// + /// Creates an error result implicitly from the error value. + /// + /// The error to be stored in the result. + public static implicit operator Result(TError error) => new(error); + + /// + /// Gets a value indicating whether the result is successful. + /// + public readonly bool IsSuccess { get; } + + /// + /// Unwraps the result. + /// + /// The wrapped result value. + /// This method is only valid if the result type is successful. + /// Thrown if attempted to unwrap the value from a failed result. + public TResult UnwrapResult() => IsSuccess ? _result! : throw new InvalidOperationException("Cannot unwrap error result"); + + /// + /// Unwraps the error. + /// + /// The wrapped error value. + /// This method is only valid if the result type is unsuccessful. + /// Thrown if attempted to unwrap an error from a successful result. + public TError UnwrapError() => IsSuccess ? throw new InvalidOperationException("Cannot unwrap success result") : _error!; + + /// + /// + /// + /// + /// + public override bool Equals(object? obj) + { + if (obj is Result other) + { + return Equals(other); + } + + return false; + } + + /// + /// + /// + /// + /// + public override int GetHashCode() + { + if (IsSuccess) + return _result!.GetHashCode(); + else + return _error!.GetHashCode(); + } + + /// + /// + /// + /// + /// + /// + public static bool operator ==(Result left, Result right) + { + return left.Equals(right); + } + + /// + /// + /// + /// + /// + /// + public static bool operator !=(Result left, Result right) + { + return !(left == right); + } + + /// + /// + /// + /// + /// + public bool Equals(Result other) + { + if (other.IsSuccess != IsSuccess) + return false; + + if (IsSuccess) + return _result!.Equals(other._result); + else + return _error!.Equals(other._error); + } + + /// + /// Casts the result to a . + /// # + /// Required for compatibility, see CA2225 for more information + /// The existing instance. + public Result ToResult() + { + return this; + } + } +} +#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/SignatureValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/SignatureValidationResult.cs deleted file mode 100644 index fc1c2ef3c2..0000000000 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/SignatureValidationResult.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using Microsoft.IdentityModel.Tokens; - -namespace Microsoft.IdentityModel.JsonWebTokens.Results -{ -#nullable enable - /// - /// Contains the result of validating a signature. - /// The contains a collection of for each step in the token validation. - /// - internal class SignatureValidationResult : ValidationResult - { - private Exception? _exception; - - /// - /// Creates an instance of representing the successful result of validating a signature. - /// - public SignatureValidationResult(bool isValid, ValidationFailureType validationFailureType) : base(validationFailureType) - { - IsValid = isValid; - } - - /// - /// Creates an instance of representing the failed result of validating a signature. - /// - /// is the that occurred while validating the signature. - /// contains the of the error that occurred while validating the signature. - public SignatureValidationResult(ValidationFailureType validationFailure, ExceptionDetail? exceptionDetail) - : base(validationFailure, exceptionDetail) - { - IsValid = false; - } - - /// - /// Creates an instance of representing a successful validation. - /// - internal static SignatureValidationResult Success() => - new SignatureValidationResult(true, ValidationFailureType.ValidationSucceeded); - - /// - /// Creates an instance of representing a failure due to a null parameter. - /// - /// The name of the null parameter. - internal static SignatureValidationResult NullParameterFailure(string parameterName) => - new SignatureValidationResult( - ValidationFailureType.SignatureValidationFailed, - ExceptionDetail.NullParameter(parameterName)); - - /// - /// Gets the that occurred while validating the signature. - /// - public override Exception? Exception - { - get - { - if (_exception != null || ExceptionDetail == null) - return _exception; - - HasValidOrExceptionWasRead = true; - _exception = ExceptionDetail.GetException(); - _exception.Source = "Microsoft.IdentityModel.JsonWebTokens"; - - if (_exception is SecurityTokenException securityTokenException) - { - securityTokenException.ExceptionDetail = ExceptionDetail; - } - - return _exception; - } - } - } -#nullable restore -} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/SigningKeyValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/SigningKeyValidationResult.cs deleted file mode 100644 index 6c3905c1cf..0000000000 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/SigningKeyValidationResult.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -#nullable enable -namespace Microsoft.IdentityModel.Tokens -{ - /// - /// Contains the result of validating the used to sign a . - /// The contains a collection of for each step in the token validation. - /// - internal class SigningKeyValidationResult : ValidationResult - { - private Exception? _exception; - - /// - /// Creates an instance of - /// - /// is the security key that was validated successfully. - public SigningKeyValidationResult(SecurityKey? signingKey) - : base(ValidationFailureType.ValidationSucceeded) - { - SigningKey = signingKey; - IsValid = true; - } - - /// - /// Creates an instance of - /// - /// is the security key that was intended to be validated. - /// is the that occurred during validation. - /// is the that occurred during validation. - public SigningKeyValidationResult(SecurityKey? signingKey, ValidationFailureType validationFailure, ExceptionDetail exceptionDetail) - : base(validationFailure, exceptionDetail) - { - SigningKey = signingKey; - IsValid = false; - } - - /// - /// Gets the that occurred during validation. - /// - public override Exception? Exception - { - get - { - if (_exception != null || ExceptionDetail == null) - return _exception; - - HasValidOrExceptionWasRead = true; - _exception = ExceptionDetail.GetException(); - if (_exception is SecurityTokenInvalidSigningKeyException securityTokenInvalidSigningKeyException) - { - securityTokenInvalidSigningKeyException.SigningKey = SigningKey; - securityTokenInvalidSigningKeyException.ExceptionDetail = ExceptionDetail; - securityTokenInvalidSigningKeyException.Source = "Microsoft.IdentityModel.Tokens"; - } - - return _exception; - } - } - - /// - /// Gets the security key that was validated or intended to be validated. - /// - public SecurityKey? SigningKey { get; } - } -} -#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenDecryptionResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenDecryptionResult.cs deleted file mode 100644 index 99488e75ba..0000000000 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenDecryptionResult.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; - -#nullable enable -namespace Microsoft.IdentityModel.Tokens -{ - /// - /// Contains the result of decrypting a securityToken in clear text. - /// The contains a collection of for each step in the token validation. - /// - internal class TokenDecryptionResult : ValidationResult - { - private Exception? _exception; - private string? _decryptedToken; - - /// - /// Creates an instance of containing the clear text result of decrypting a security token. - /// - /// The clear text result of decrypting the security token. - /// The SecurityToken that contains the cypher text. - public TokenDecryptionResult(string decryptedToken, SecurityToken securityToken) - : base(ValidationFailureType.ValidationSucceeded) - { - IsValid = true; - _decryptedToken = decryptedToken; - SecurityToken = securityToken; - } - - /// - /// Creates an instance of - /// - /// is the securityToken that could not be decrypted. - /// is the that occurred during reading. - /// is the that occurred during reading. - public TokenDecryptionResult(SecurityToken? securityToken, ValidationFailureType validationFailure, ExceptionDetail exceptionDetail) - : base(validationFailure, exceptionDetail) - { - SecurityToken = securityToken; - IsValid = false; - } - - /// - /// Creates an instance of representing a failure due to a null parameter. - /// - /// The securityToken that could not be decrypted. - /// The name of the null parameter. - internal static TokenDecryptionResult NullParameterFailure(SecurityToken? securityToken, string parameterName) => - new TokenDecryptionResult( - securityToken, - ValidationFailureType.TokenDecryptionFailed, - ExceptionDetail.NullParameter(parameterName)); - - /// - /// Gets the decoded contents of the SecurityToken. - /// - /// if the result is not valid, and the decrypted token is not available. - /// It is expected that this method will only be called if returns true. - public string DecryptedToken() - { - if (_decryptedToken is null) - throw new InvalidOperationException("Attempted to retrieve the DecryptedToken from a failed TokenDecrypting result."); - - return _decryptedToken; - } - - /// - /// Gets the that occurred during reading. - /// - public override Exception? Exception - { - get - { - if (_exception != null || ExceptionDetail == null) - return _exception; - - HasValidOrExceptionWasRead = true; - _exception = ExceptionDetail.GetException(); - - if (_exception is SecurityTokenException securityTokenException) - { - securityTokenException.Source = "Microsoft.IdentityModel.Tokens"; - securityTokenException.ExceptionDetail = ExceptionDetail; - } - - return _exception; - } - } - - /// - /// The on which decryption was attempted. - /// - public SecurityToken? SecurityToken { get; } - } -} -#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenReadingResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenReadingResult.cs deleted file mode 100644 index 38002bd071..0000000000 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenReadingResult.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; - -#nullable enable -namespace Microsoft.IdentityModel.Tokens -{ - /// - /// Contains the result of reading a . - /// The contains a collection of for each step in the token validation. - /// - internal class TokenReadingResult : ValidationResult - { - private Exception? _exception; - private SecurityToken? _securityToken; - - /// - /// Creates an instance of . - /// - /// is the string from which the was created. - /// is the that was created. - public TokenReadingResult(SecurityToken securityToken, string tokenInput) - : base(ValidationFailureType.ValidationSucceeded) - { - IsValid = true; - TokenInput = tokenInput; - _securityToken = securityToken; - } - - /// - /// Creates an instance of - /// - /// is the string that failed to create a . - /// is the that occurred during reading. - /// is the that occurred during reading. - public TokenReadingResult(string? tokenInput, ValidationFailureType validationFailure, ExceptionDetail exceptionDetail) - : base(validationFailure, exceptionDetail) - { - TokenInput = tokenInput; - IsValid = false; - } - - /// - /// Gets the that was read. - /// - /// if the is null. - /// It is expected that the caller would check returns true before accessing this. - public SecurityToken SecurityToken() - { - if (_securityToken is null) - throw new InvalidOperationException("Attempted to retrieve the SecurityToken from a failed TokenReading result."); - - return _securityToken; - } - - /// - /// Gets the that occurred during reading. - /// - public override Exception? Exception - { - get - { - if (_exception != null || ExceptionDetail == null) - return _exception; - - HasValidOrExceptionWasRead = true; - _exception = ExceptionDetail.GetException(); - - if (_exception is SecurityTokenException securityTokenException) - { - securityTokenException.Source = "Microsoft.IdentityModel.Tokens"; - securityTokenException.ExceptionDetail = ExceptionDetail; - } - - return _exception; - } - } - - /// - /// Gets the string from which the was read. - /// - public string? TokenInput { get; } - } -} -#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenTypeValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenTypeValidationResult.cs deleted file mode 100644 index e098694e71..0000000000 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenTypeValidationResult.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -#nullable enable -namespace Microsoft.IdentityModel.Tokens -{ - /// - /// Contains the result of validating the TokenType of a . - /// The contains a collection of for each step in the token validation. - /// - internal class TokenTypeValidationResult : ValidationResult - { - private Exception? _exception; - private const string TokenSource = "Microsoft.IdentityModel.Tokens"; - - /// - /// Creates an instance of . - /// - /// is the type against which the token was validated. - public TokenTypeValidationResult(string? type) - : base(ValidationFailureType.ValidationSucceeded) - { - Type = type; - IsValid = true; - } - - /// - /// Creates an instance of - /// - /// is the type against which the token was validated. - /// is the that occurred during validation. - /// is the that occurred during validation. - public TokenTypeValidationResult(string? type, ValidationFailureType validationFailure, ExceptionDetail exceptionDetail) - : base(validationFailure, exceptionDetail) - { - Type = type; - IsValid = false; - } - - /// - /// Gets the that occurred during validation. - /// - public override Exception? Exception - { - get - { - if (_exception != null || ExceptionDetail == null) - return _exception; - - HasValidOrExceptionWasRead = true; - _exception = ExceptionDetail.GetException(); - if (_exception is SecurityTokenInvalidTypeException securityTokenInvalidTypeException) - { - securityTokenInvalidTypeException.InvalidType = Type; - securityTokenInvalidTypeException.Source = TokenSource; - } - - return _exception; - } - } - - /// - /// Gets the security token type. - /// - public string? Type { get; } - - } -} -#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenValidationResult.cs index 9e435baea5..024f4230a7 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenValidationResult.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenValidationResult.cs @@ -37,6 +37,7 @@ public class TokenValidationResult // TODO - lazy creation of _validationResults private List _validationResults; + private ExceptionDetail _exceptionDetail; private Exception _exception; private bool _isValid; @@ -78,31 +79,39 @@ internal TokenValidationResult( /// /// /// + /// /// This constructor is used by JsonWebTokenHandler as part of delaying creation of ClaimsIdentity. internal TokenValidationResult( SecurityToken securityToken, TokenHandler tokenHandler, ValidationParameters validationParameters, string issuer, - List validationResults) + List validationResults, + ExceptionDetail exceptionDetail) { _validationParameters = validationParameters; _tokenHandler = tokenHandler; _validationResults = validationResults; Issuer = issuer; SecurityToken = securityToken; + _exceptionDetail = exceptionDetail; } /// - /// Adds a to the list of . + /// Initializes a new instance of using . /// - /// the associated with one of the validation steps. For example . - internal void AddValidationResult(ValidationResult validationResult) + /// + /// + /// + /// This constructor is used by JsonWebTokenHandler as part of delaying creation of ClaimsIdentity. + internal TokenValidationResult( + TokenHandler tokenHandler, + ValidationParameters validationParameters, + ExceptionDetail exceptionDetail) { - if (validationResult is null) - throw LogHelper.LogArgumentNullException(nameof(validationResult)); - - _validationResults.Add(validationResult); + _tokenHandler = tokenHandler; + _exceptionDetail = exceptionDetail; + _validationParameters = validationParameters; } /// @@ -211,11 +220,8 @@ public Exception Exception get { HasValidOrExceptionWasRead = true; - if (_exception is null) - { - if (ExceptionDetail is not null) - return ExceptionDetail.GetException(); - } + if (_exception is null && _exceptionDetail is not null) + return _exceptionDetail.GetException(); return _exception; } @@ -225,28 +231,6 @@ public Exception Exception } } - /// - /// Gets the for the first failed validation result. - /// - private ExceptionDetail ExceptionDetail - { - get - { - if (ValidationResults.Count == 0) - return null; - - // Iterate in reverse since the failure should be the last result - for (int i = ValidationResults.Count - 1; i >= 0; i--) - { - ValidationResult validationResult = ValidationResults[i]; - if (validationResult.ExceptionDetail != null) - return validationResult.ExceptionDetail; - } - - return null; - } - } - internal bool HasValidOrExceptionWasRead { get; set; } /// @@ -306,19 +290,5 @@ public bool IsValid /// (e.g for a JSON Web Token, from the "typ" header). /// public string TokenType { get; set; } - - /// - /// Gets the list of that contains the result of validating the token. - /// - internal IReadOnlyList ValidationResults - { - get - { - if (_validationResults is null) - Interlocked.CompareExchange(ref _validationResults, new List(), null); - - return _validationResults.AsReadOnly(); - } - } } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidationResult.cs index dd62bb549d..421c4a1a86 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidationResult.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidationResult.cs @@ -1,112 +1,167 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#nullable enable using System; using System.Collections.Generic; using System.Diagnostics; +using System.Security.Claims; +using System.Threading; -#nullable enable namespace Microsoft.IdentityModel.Tokens { /// /// Contains results of a single step in validating a . /// A maintains a list of for each step in the token validation. /// - internal abstract class ValidationResult + internal class ValidationResult { - private bool _isValid; - /// /// Creates an instance of /// - protected ValidationResult() + /// The that is being validated. + /// The that is being used to validate the token. + /// The to be used for validating the token. + internal ValidationResult( + SecurityToken securityToken, + TokenHandler tokenHandler, + ValidationParameters validationParameters) { - ValidationFailureType = ValidationFailureType.ValidationNotEvaluated; + TokenHandler = tokenHandler ?? throw new ArgumentNullException(nameof(tokenHandler)); + SecurityToken = securityToken ?? throw new ArgumentNullException(nameof(securityToken)); + ValidationParameters = validationParameters ?? throw new ArgumentNullException(nameof(validationParameters)); } /// - /// Creates an instance of + /// Logs the validation result. /// - /// The that occurred during validation. - protected ValidationResult(ValidationFailureType validationFailureType) +#pragma warning disable CA1822 // Mark members as static + public void Log() +#pragma warning restore CA1822 // Mark members as static { - ValidationFailureType = validationFailureType; + // TODO - Do we need this, how will it work? } + public SecurityToken SecurityToken { get; private set; } + + public TokenHandler TokenHandler { get; private set; } + + public ValidationParameters ValidationParameters { get; private set; } + + #region Validation Results + public ValidationResult? ActorValidationResult { get; internal set; } + public string? ValidatedAudience { get; internal set; } + public ValidatedIssuer? ValidatedIssuer { get; internal set; } + public ValidatedLifetime? ValidatedLifetime { get; internal set; } + public DateTime? ValidatedTokenReplayExpirationTime { get; internal set; } + public ValidatedTokenType? ValidatedTokenType { get; internal set; } + public SecurityKey? ValidatedSigningKey { get; internal set; } + public ValidatedSigningKeyLifetime? ValidatedSigningKeyLifetime { get; internal set; } + #endregion + + #region Claims + // Fields lazily initialized in a thread-safe manner. _claimsIdentity is protected by the _claimsIdentitySyncObj + // lock, and since null is a valid initialized value, _claimsIdentityInitialized tracks whether or not it's valid. + // _claims is constructed by reading the data from the ClaimsIdentity and is synchronized using Interlockeds + // to ensure only one dictionary is published in the face of concurrent access (but if there's a race condition, + // multiple dictionaries could be constructed, with only one published for all to see). Simiarly, _propertyBag + // is initalized with Interlocked to ensure only a single instance is published in the face of concurrent use. + // _claimsIdentityInitialized only ever transitions from false to true, and is volatile to reads/writes are not + // reordered relative to the other operations. The rest of the objects are not because the .NET memory model + // guarantees object writes are store releases and that reads won't be introduced. + private volatile bool _claimsIdentityInitialized; + private object? _claimsIdentitySyncObj; + private ClaimsIdentity? _claimsIdentity; + private Dictionary? _claims; + /// - /// Creates an instance of + /// The created from the validated security token. /// - /// The that occurred during validation. - /// The representing the that occurred during validation. - protected ValidationResult(ValidationFailureType validationFailureType, ExceptionDetail? exceptionDetail) + public IDictionary Claims { - ValidationFailureType = validationFailureType; - ExceptionDetail = exceptionDetail; + get + { + if (_claims is null) + { + Interlocked.CompareExchange(ref _claims, TokenUtilities.CreateDictionaryFromClaims(ClaimsIdentity.Claims), null); + } + + return _claims; + } } /// - /// Adds a new stack frame to the exception details. + /// The created from the validated security token. /// - /// - public void AddStackFrame(StackFrame stackFrame) + public ClaimsIdentity ClaimsIdentity { - ExceptionDetail?.StackFrames.Add(stackFrame); - } + get + { + if (!_claimsIdentityInitialized) + { + lock (ClaimsIdentitySyncObj) + { + return ClaimsIdentityNoLocking; + } + } - /// - /// Gets the that occurred during validation. - /// - public abstract Exception? Exception { get; } + return _claimsIdentity!; + } + set + { + if (value is null) + throw new ArgumentNullException(nameof(value), "ClaimsIdentity cannot be set as null."); - /// - /// Gets the that occurred during validation. - /// - public ExceptionDetail? ExceptionDetail { get; } + lock (ClaimsIdentitySyncObj) + { + ClaimsIdentityNoLocking = value; + } + } + } /// - /// True if the token was successfully validated, false otherwise. + /// Gets or sets the without synchronization. All accesses must either + /// be protected or used when the caller knows access is serialized. /// - public bool IsValid + internal ClaimsIdentity ClaimsIdentityNoLocking { get { - HasValidOrExceptionWasRead = true; - return _isValid; + if (!_claimsIdentityInitialized) + { + Debug.Assert(_claimsIdentity is null); + + _claimsIdentity = TokenHandler.CreateClaimsIdentityInternal(SecurityToken, ValidationParameters, ValidatedIssuer?.Issuer); + _claimsIdentityInitialized = true; + } + + return _claimsIdentity!; } set { - _isValid = value; + Debug.Assert(value is not null); + _claimsIdentity = value; + _claims = null; + _claimsIdentityInitialized = true; } } - // TODO - HasValidOrExceptionWasRead, IsValid, Exception are temporary and will be removed when TokenValidationResult derives from ValidationResult. - /// - /// Gets or sets a boolean recording if IsValid or Exception was called. - /// - protected bool HasValidOrExceptionWasRead { get; set; } - - /// - /// Logs the validation result. - /// -#pragma warning disable CA1822 // Mark members as static - public void Log() -#pragma warning restore CA1822 // Mark members as static + /// Gets the object to use in for double-checked locking. + private object ClaimsIdentitySyncObj { - // TODO - Do we need this, how will it work? - } - - /// - /// Contains any logs that would have been written. - /// - public IList LogDetails { get; } = new List(); + get + { + object? syncObj = _claimsIdentitySyncObj; + if (syncObj is null) + { + Interlocked.CompareExchange(ref _claimsIdentitySyncObj, new object(), null); + syncObj = _claimsIdentitySyncObj; + } - /// - /// Gets the indicating why the validation was not satisfied. - /// - public ValidationFailureType ValidationFailureType - { - get; - } = ValidationFailureType.ValidationNotEvaluated; + return syncObj; + } + } + #endregion } } #nullable disable diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs index e7f4ca7f40..20b4224cc5 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs @@ -88,16 +88,9 @@ private class TokenReadingFailure : ValidationFailureType { internal TokenReadin private class TokenDecryptionFailure : ValidationFailureType { internal TokenDecryptionFailure(string name) : base(name) { } } /// - /// Defines a type that represents that no evaluation has taken place. + /// Defines a type that represents that a token is invalid. /// - public static readonly ValidationFailureType ValidationNotEvaluated = new NotEvaluated("NotEvaluated"); - private class NotEvaluated : ValidationFailureType { internal NotEvaluated(string name) : base(name) { } } - - /// - /// Defines a type that represents that no evaluation has taken place. - /// - public static readonly ValidationFailureType ValidationSucceeded = new Succeeded("Succeeded"); - private class Succeeded : ValidationFailureType { internal Succeeded(string name) : base(name) { } } - + public static readonly ValidationFailureType InvalidSecurityToken = new InvalidSecurityTokenFailure("InvalidSecurityToken"); + private class InvalidSecurityTokenFailure : ValidationFailureType { internal InvalidSecurityTokenFailure(string name) : base(name) { } } } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs index 92f3929ca3..d05477bcab 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs @@ -6,7 +6,6 @@ using System.ComponentModel; using System.Security.Claims; using System.Threading; -using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; namespace Microsoft.IdentityModel.Tokens @@ -227,8 +226,9 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken, roleClaimType = RoleClaimType; } - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10245, securityToken); + // TODO: Add to CallContext + //if (LogHelper.IsEnabled(EventLogLevel.Informational)) + // LogHelper.LogInformation(LogMessages.IDX10245, securityToken); #pragma warning disable RS0030 // Do not use banned APIs return new ClaimsIdentity(authenticationType: AuthenticationType ?? DefaultAuthenticationType, nameType: nameClaimType ?? ClaimsIdentity.DefaultNameClaimType, roleType: roleClaimType ?? ClaimsIdentity.DefaultRoleClaimType); @@ -494,7 +494,7 @@ public TokenReplayValidatorDelegate TokenReplayValidator /// /// Allows overriding the delegate that will be used to validate the type of the token. - /// If the token type cannot be validated, a MUST be returned by the delegate. + /// If the token type cannot be validated, a MUST be returned by the delegate. /// Note: the 'type' parameter may be null if it couldn't be extracted from its usual location. /// Implementations that need to resolve it from a different location can use the 'token' parameter. /// diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs index 684d339c80..ac7330e331 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs @@ -17,9 +17,9 @@ namespace Microsoft.IdentityModel.Tokens /// The being validated. /// required for validation. /// - /// A that contains the results of validating the algorithm. + /// A that contains the results of validating the algorithm. /// This delegate is not expected to throw. - internal delegate AlgorithmValidationResult AlgorithmValidatorDelegate( + internal delegate Result AlgorithmValidatorDelegate( string algorithm, SecurityKey securityKey, SecurityToken securityToken, @@ -37,7 +37,7 @@ public static partial class Validators /// required for validation. /// #pragma warning disable CA1801 // TODO: remove pragma disable once callContext is used for logging - internal static AlgorithmValidationResult ValidateAlgorithm( + internal static Result ValidateAlgorithm( string algorithm, SecurityKey securityKey, SecurityToken securityToken, @@ -46,32 +46,22 @@ internal static AlgorithmValidationResult ValidateAlgorithm( #pragma warning restore CA1801 // TODO: remove pragma disable once callContext is used for logging { if (validationParameters == null) - { - return new AlgorithmValidationResult( - algorithm, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII(nameof(validationParameters))), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))); - } + return ExceptionDetail.NullParameter( + nameof(validationParameters), + new StackFrame(true)); - if (validationParameters.ValidAlgorithms != null && validationParameters.ValidAlgorithms.Count > 0 && !validationParameters.ValidAlgorithms.Contains(algorithm, StringComparer.Ordinal)) - { - return new AlgorithmValidationResult( - algorithm, + if (validationParameters.ValidAlgorithms != null && + validationParameters.ValidAlgorithms.Count > 0 && + !validationParameters.ValidAlgorithms.Contains(algorithm, StringComparer.Ordinal)) + return new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10696, + LogHelper.MarkAsNonPII(algorithm)), ValidationFailureType.AlgorithmValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10696, - LogHelper.MarkAsNonPII(algorithm)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAlgorithm, - new StackFrame(true))); - } + ExceptionType.SecurityTokenInvalidAlgorithm, + new StackFrame(true)); - return new AlgorithmValidationResult(algorithm); + return algorithm; } } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs index 26ee826da2..f9b5fccb2b 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs @@ -17,9 +17,9 @@ namespace Microsoft.IdentityModel.Tokens /// The that is being validated. /// The to be used for validating the token. /// - /// A that contains the results of validating the issuer. + /// A that contains the results of validating the issuer. /// This delegate is not expected to throw. - internal delegate AudienceValidationResult AudienceValidatorDelegate( + internal delegate Result AudienceValidatorDelegate( IList audiences, SecurityToken? securityToken, ValidationParameters validationParameters, @@ -43,56 +43,40 @@ public static partial class Validators /// If none of the 'audiences' matched either or one of . /// An EXACT match is required. #pragma warning disable CA1801 // TODO: remove pragma disable once callContext is used for logging - internal static AudienceValidationResult ValidateAudience(IList tokenAudiences, SecurityToken? securityToken, ValidationParameters validationParameters, CallContext callContext) + internal static Result ValidateAudience(IList tokenAudiences, SecurityToken? securityToken, ValidationParameters validationParameters, CallContext callContext) #pragma warning restore CA1801 { if (validationParameters == null) - return new AudienceValidationResult( - Utility.SerializeAsSingleCommaDelimitedString(tokenAudiences), - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII(nameof(validationParameters))), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))); + return ExceptionDetail.NullParameter( + nameof(validationParameters), + new StackFrame(true)); if (tokenAudiences == null) - return new AudienceValidationResult( - Utility.SerializeAsSingleCommaDelimitedString(tokenAudiences), - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10207, - null), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true))); + return new ExceptionDetail( + new MessageDetail(LogMessages.IDX10207), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + new StackFrame(true)); if (tokenAudiences.Count == 0) - return new AudienceValidationResult( - Utility.SerializeAsSingleCommaDelimitedString(tokenAudiences), - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10206, - null), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true))); + return new ExceptionDetail( + new MessageDetail(LogMessages.IDX10206), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + new StackFrame(true)); string? validAudience = ValidTokenAudience(tokenAudiences, validationParameters.ValidAudiences, validationParameters.IgnoreTrailingSlashWhenValidatingAudience); if (validAudience != null) - return new AudienceValidationResult(validAudience); - - return new AudienceValidationResult( - Utility.SerializeAsSingleCommaDelimitedString(tokenAudiences), - ValidationFailureType.AudienceValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(tokenAudiences)), - LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidAudiences))), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true))); + return validAudience; + + return new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(tokenAudiences)), + LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidAudiences))), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + new StackFrame(true)); } private static string? ValidTokenAudience(IList tokenAudiences, IList validAudiences, bool ignoreTrailingSlashWhenValidatingAudience) diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs index 87fca5f32a..0e2e32ed73 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs @@ -4,11 +4,20 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; namespace Microsoft.IdentityModel.Tokens { +#nullable enable + internal enum IssuerValidationSource + { + NotValidated = 0, + IssuerIsConfigurationIssuer, + IssuerIsAmongValidIssuers + } + + internal record struct ValidatedIssuer(string Issuer, IssuerValidationSource ValidationSource); + /// /// Definition for delegate that will validate the issuer value in a token. /// @@ -17,14 +26,14 @@ namespace Microsoft.IdentityModel.Tokens /// The to be used for validating the token. /// /// - /// An that contains the results of validating the issuer. + /// An that contains the results of validating the issuer. /// This delegate is not expected to throw. - internal delegate Task IssuerValidationDelegateAsync( + internal delegate Task> IssuerValidationDelegateAsync( string issuer, SecurityToken securityToken, ValidationParameters validationParameters, CallContext callContext, - CancellationToken? cancellationToken); + CancellationToken cancellationToken); /// /// IssuerValidation @@ -39,70 +48,47 @@ public static partial class Validators /// The to be used for validating the token. /// /// - /// An that contains the results of validating the issuer. + /// An that contains either the issuer that was validated or an error. /// An EXACT match is required. - internal static async Task ValidateIssuerAsync( + internal static async Task> ValidateIssuerAsync( string issuer, SecurityToken securityToken, ValidationParameters validationParameters, - CallContext callContext, - CancellationToken? cancellationToken) +#pragma warning disable CA1801 // Review unused parameters + CallContext? callContext, +#pragma warning restore CA1801 // Review unused parameters + CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(issuer)) { - return new IssuerValidationResult( - issuer, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10211, - null), - ExceptionDetail.ExceptionType.SecurityTokenInvalidIssuer, - new StackFrame(true), - null)); + return new ExceptionDetail( + new MessageDetail(LogMessages.IDX10211), + ValidationFailureType.IssuerValidationFailed, + ExceptionType.SecurityTokenInvalidIssuer, + new StackFrame(true)); } if (validationParameters == null) - return new IssuerValidationResult( - issuer, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII(nameof(validationParameters))), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true), - null)); + return ExceptionDetail.NullParameter( + nameof(validationParameters), + new StackFrame(true)); if (securityToken == null) - return new IssuerValidationResult( - issuer, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII(nameof(securityToken))), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true), - null)); - - BaseConfiguration configuration = null; + return ExceptionDetail.NullParameter( + nameof(securityToken), + new StackFrame(true)); + + BaseConfiguration? configuration = null; if (validationParameters.ConfigurationManager != null) - configuration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(cancellationToken ?? CancellationToken.None).ConfigureAwait(false); + configuration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(cancellationToken).ConfigureAwait(false); // Return failed IssuerValidationResult if all possible places to validate against are null or empty. if (validationParameters.ValidIssuers.Count == 0 && string.IsNullOrWhiteSpace(configuration?.Issuer)) - { - return new IssuerValidationResult( - issuer, + return new ExceptionDetail( + new MessageDetail(LogMessages.IDX10211), ValidationFailureType.IssuerValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10211, - null), - ExceptionDetail.ExceptionType.SecurityTokenInvalidIssuer, - new StackFrame(true))); - } + ExceptionType.SecurityTokenInvalidIssuer, + new StackFrame(true)); if (configuration != null) { @@ -111,11 +97,12 @@ internal static async Task ValidateIssuerAsync( // TODO - how and when to log // Logs will have to be passed back to Wilson // so that they can be written to the correct place and in the correct format respecting PII. - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer), callContext); + // Add to CallContext + //if (LogHelper.IsEnabled(EventLogLevel.Informational)) + // LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer), callContext); + - return new IssuerValidationResult(issuer, - IssuerValidationResult.ValidationSource.IssuerIsConfigurationIssuer); + return new ValidatedIssuer(issuer, IssuerValidationSource.IssuerIsConfigurationIssuer); } } @@ -125,34 +112,34 @@ internal static async Task ValidateIssuerAsync( { if (string.IsNullOrEmpty(validationParameters.ValidIssuers[i])) { - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10262); + // TODO: Add to CallContext + //if (LogHelper.IsEnabled(EventLogLevel.Informational)) + // LogHelper.LogInformation(LogMessages.IDX10262); continue; } if (string.Equals(validationParameters.ValidIssuers[i], issuer)) { - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer)); + // TODO: Add to CallContext + //if (LogHelper.IsEnabled(EventLogLevel.Informational)) + // LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer)); - return new IssuerValidationResult(issuer, - IssuerValidationResult.ValidationSource.IssuerIsAmongValidIssuers); + return new ValidatedIssuer(issuer, IssuerValidationSource.IssuerIsAmongValidIssuers); } } } - return new IssuerValidationResult( - issuer, + return new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10212, + LogHelper.MarkAsNonPII(issuer), + LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidIssuers)), + LogHelper.MarkAsNonPII(configuration?.Issuer)), ValidationFailureType.IssuerValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10212, - LogHelper.MarkAsNonPII(issuer), - LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidIssuers)), - LogHelper.MarkAsNonPII(configuration?.Issuer)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidIssuer, - new StackFrame(true))); + ExceptionType.SecurityTokenInvalidIssuer, + new StackFrame(true)); } } +#nullable restore } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs index ea688998e3..f618ec6756 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs @@ -4,12 +4,13 @@ using System; using System.Diagnostics; using System.Security.Cryptography.X509Certificates; -using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; #nullable enable namespace Microsoft.IdentityModel.Tokens { + internal record struct ValidatedSigningKeyLifetime(DateTime? ValidFrom, DateTime? ValidTo, DateTime? ValidationTime); + /// /// Definition for delegate that will validate the that signed a . /// @@ -18,9 +19,9 @@ namespace Microsoft.IdentityModel.Tokens /// The to be used for validating the token. /// The to be used for validation. /// The to be used for logging. - /// A that contains the results of validating the issuer. + /// A that contains the results of validating the issuer. /// This delegate is not expected to throw. - internal delegate SigningKeyValidationResult IssuerSigningKeyValidatorDelegate( + internal delegate Result IssuerSigningKeyValidatorDelegate( SecurityKey signingKey, SecurityToken securityToken, ValidationParameters validationParameters, @@ -44,7 +45,7 @@ public static partial class Validators /// if 'securityKey' is null and ValidateIssuerSigningKey is true. /// if 'securityToken' is null and ValidateIssuerSigningKey is true. /// if 'validationParameters' is null. - internal static SigningKeyValidationResult ValidateIssuerSigningKey( + internal static Result ValidateIssuerSigningKey( SecurityKey securityKey, SecurityToken securityToken, ValidationParameters validationParameters, @@ -54,39 +55,21 @@ internal static SigningKeyValidationResult ValidateIssuerSigningKey( CallContext? callContext) { if (validationParameters == null) - return new SigningKeyValidationResult( - securityKey, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII(nameof(validationParameters))), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))); + return ExceptionDetail.NullParameter( + nameof(validationParameters), + new StackFrame(true)); if (securityKey == null) - { - return new SigningKeyValidationResult( - securityKey, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10253, - LogHelper.MarkAsNonPII(nameof(securityKey))), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))); - } + return new ExceptionDetail( + new MessageDetail(LogMessages.IDX10253, nameof(securityKey)), + ValidationFailureType.SigningKeyValidationFailed, + ExceptionType.ArgumentNull, + new StackFrame(true)); if (securityToken == null) - return new SigningKeyValidationResult( - securityKey, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII(nameof(securityToken))), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))); + return ExceptionDetail.NullParameter( + nameof(securityToken), + new StackFrame(true)); return ValidateIssuerSigningKeyLifeTime(securityKey, validationParameters, callContext); } @@ -98,53 +81,52 @@ internal static SigningKeyValidationResult ValidateIssuerSigningKey( /// The to be used for validating the token. /// #pragma warning disable CA1801 // Review unused parameters - internal static SigningKeyValidationResult ValidateIssuerSigningKeyLifeTime( + internal static Result ValidateIssuerSigningKeyLifeTime( SecurityKey securityKey, ValidationParameters validationParameters, CallContext? callContext) #pragma warning restore CA1801 // Review unused parameters { + DateTime utcNow = DateTime.UtcNow; + DateTime? notBeforeUtc = null; + DateTime? notAfterUtc = null; X509SecurityKey? x509SecurityKey = securityKey as X509SecurityKey; + if (x509SecurityKey?.Certificate is X509Certificate2 cert) { - DateTime utcNow = DateTime.UtcNow; - var notBeforeUtc = cert.NotBefore.ToUniversalTime(); - var notAfterUtc = cert.NotAfter.ToUniversalTime(); + notBeforeUtc = cert.NotBefore.ToUniversalTime(); + notAfterUtc = cert.NotAfter.ToUniversalTime(); if (notBeforeUtc > DateTimeUtil.Add(utcNow, validationParameters.ClockSkew)) - return new SigningKeyValidationResult( - securityKey, + return new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10248, + LogHelper.MarkAsNonPII(notBeforeUtc), + LogHelper.MarkAsNonPII(utcNow)), ValidationFailureType.SigningKeyValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogHelper.FormatInvariant( - LogMessages.IDX10248, - LogHelper.MarkAsNonPII(notBeforeUtc), - LogHelper.MarkAsNonPII(utcNow))), - ExceptionDetail.ExceptionType.SecurityTokenInvalidSigningKey, - new StackFrame(true))); + ExceptionType.SecurityTokenInvalidSigningKey, + new StackFrame(true)); - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10250, LogHelper.MarkAsNonPII(notBeforeUtc), LogHelper.MarkAsNonPII(utcNow)); + //TODO: Move to CallContext + //if (LogHelper.IsEnabled(EventLogLevel.Informational)) + // LogHelper.LogInformation(LogMessages.IDX10250, LogHelper.MarkAsNonPII(notBeforeUtc), LogHelper.MarkAsNonPII(utcNow)); if (notAfterUtc < DateTimeUtil.Add(utcNow, validationParameters.ClockSkew.Negate())) - return new SigningKeyValidationResult( - securityKey, + return new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10249, + LogHelper.MarkAsNonPII(notAfterUtc), + LogHelper.MarkAsNonPII(utcNow)), ValidationFailureType.SigningKeyValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogHelper.FormatInvariant( - LogMessages.IDX10249, - LogHelper.MarkAsNonPII(notAfterUtc), - LogHelper.MarkAsNonPII(utcNow))), - ExceptionDetail.ExceptionType.SecurityTokenInvalidSigningKey, - new StackFrame(true))); + ExceptionType.SecurityTokenInvalidSigningKey, + new StackFrame(true)); - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10251, LogHelper.MarkAsNonPII(notAfterUtc), LogHelper.MarkAsNonPII(utcNow)); + // TODO: Move to CallContext + //if (LogHelper.IsEnabled(EventLogLevel.Informational)) + // LogHelper.LogInformation(LogMessages.IDX10251, LogHelper.MarkAsNonPII(notAfterUtc), LogHelper.MarkAsNonPII(utcNow)); } - return new SigningKeyValidationResult(securityKey); + return new ValidatedSigningKeyLifetime(notBeforeUtc, notAfterUtc, utcNow); } } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs index a56ee6bd9e..7e10402857 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs @@ -9,6 +9,8 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens { + internal record struct ValidatedLifetime(DateTime? NotBefore, DateTime? Expires); + /// /// Definition for delegate that will validate the lifetime of a . /// @@ -17,9 +19,9 @@ namespace Microsoft.IdentityModel.Tokens /// The that is being validated. /// The to be used for validating the token. /// - /// A that contains the results of validating the issuer. + /// A that contains the results of validating the issuer. /// This delegate is not expected to throw. - internal delegate LifetimeValidationResult LifetimeValidatorDelegate( + internal delegate Result LifetimeValidatorDelegate( DateTime? notBefore, DateTime? expires, SecurityToken? securityToken, @@ -39,87 +41,72 @@ public static partial class Validators /// The being validated. /// The to be used for validating the token. /// - /// A indicating whether validation was successful, and providing a if it was not. + /// A indicating whether validation was successful, and providing a if it was not. /// If 'validationParameters' is null. /// If 'expires.HasValue' is false. /// If 'notBefore' is > 'expires'. /// If 'notBefore' is > DateTime.UtcNow. /// If 'expires' is < DateTime.UtcNow. /// All time comparisons apply . - /// Exceptions are not thrown, but embedded in . #pragma warning disable CA1801 // TODO: remove pragma disable once callContext is used for logging - internal static LifetimeValidationResult ValidateLifetime(DateTime? notBefore, DateTime? expires, SecurityToken? securityToken, ValidationParameters validationParameters, CallContext callContext) + internal static Result ValidateLifetime( + DateTime? notBefore, + DateTime? expires, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) #pragma warning restore CA1801 { if (validationParameters == null) - return new LifetimeValidationResult( - notBefore, - expires, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII(nameof(validationParameters))), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))); + return ExceptionDetail.NullParameter( + nameof(validationParameters), + new StackFrame(true)); if (!expires.HasValue) - return new LifetimeValidationResult( - notBefore, - expires, + return new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10225, + LogHelper.MarkAsNonPII(securityToken == null ? "null" : securityToken.GetType().ToString())), ValidationFailureType.LifetimeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10225, - LogHelper.MarkAsNonPII(securityToken == null ? "null" : securityToken.GetType().ToString())), - ExceptionDetail.ExceptionType.SecurityTokenNoExpiration, - new StackFrame(true))); + ExceptionType.SecurityTokenNoExpiration, + new StackFrame(true)); if (notBefore.HasValue && expires.HasValue && (notBefore.Value > expires.Value)) - return new LifetimeValidationResult( - notBefore, - expires, + return new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10224, + LogHelper.MarkAsNonPII(notBefore.Value), + LogHelper.MarkAsNonPII(expires.Value)), ValidationFailureType.LifetimeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10224, - LogHelper.MarkAsNonPII(notBefore.Value), - LogHelper.MarkAsNonPII(expires.Value)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidLifetime, - new StackFrame(true))); + ExceptionType.SecurityTokenInvalidLifetime, + new StackFrame(true)); DateTime utcNow = DateTime.UtcNow; if (notBefore.HasValue && (notBefore.Value > DateTimeUtil.Add(utcNow, validationParameters.ClockSkew))) - return new LifetimeValidationResult( - notBefore, - expires, + return new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10222, + LogHelper.MarkAsNonPII(notBefore.Value), + LogHelper.MarkAsNonPII(utcNow)), ValidationFailureType.LifetimeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10222, - LogHelper.MarkAsNonPII(notBefore.Value), - LogHelper.MarkAsNonPII(utcNow)), - ExceptionDetail.ExceptionType.SecurityTokenNotYetValid, - new StackFrame(true))); + ExceptionType.SecurityTokenNotYetValid, + new StackFrame(true)); if (expires.HasValue && (expires.Value < DateTimeUtil.Add(utcNow, validationParameters.ClockSkew.Negate()))) - return new LifetimeValidationResult( - notBefore, - expires, + return new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10223, + LogHelper.MarkAsNonPII(expires.Value), + LogHelper.MarkAsNonPII(utcNow)), ValidationFailureType.LifetimeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10223, - LogHelper.MarkAsNonPII(expires.Value), - LogHelper.MarkAsNonPII(utcNow)), - ExceptionDetail.ExceptionType.SecurityTokenExpired, - new StackFrame(true))); + ExceptionType.SecurityTokenExpired, + new StackFrame(true)); // if it reaches here, that means lifetime of the token is valid if (LogHelper.IsEnabled(EventLogLevel.Informational)) LogHelper.LogInformation(LogMessages.IDX10239); - return new LifetimeValidationResult(notBefore, expires); + return new ValidatedLifetime(notBefore, expires); } } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs index 8d301ea6a8..a49cd85491 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs @@ -14,9 +14,9 @@ namespace Microsoft.IdentityModel.Tokens /// The security token that is being validated. /// The to be used for validating the token. /// - /// A that contains the results of validating the token. + /// A that contains the results of validating the token. /// This delegate is not expected to throw. - internal delegate ReplayValidationResult TokenReplayValidatorDelegate( + internal delegate Result TokenReplayValidatorDelegate( DateTime? expirationTime, string securityToken, ValidationParameters validationParameters, @@ -40,76 +40,54 @@ public static partial class Validators /// If the 'securityToken' is found in the cache. /// If the 'securityToken' could not be added to the . #pragma warning disable CA1801 // Review unused parameters - internal static ReplayValidationResult ValidateTokenReplay(DateTime? expirationTime, string securityToken, ValidationParameters validationParameters, CallContext callContext) + internal static Result ValidateTokenReplay(DateTime? expirationTime, string securityToken, ValidationParameters validationParameters, CallContext callContext) #pragma warning restore CA1801 // Review unused parameters { if (string.IsNullOrWhiteSpace(securityToken)) - return new ReplayValidationResult( - expirationTime, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII(nameof(securityToken))), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(), - null)); + return ExceptionDetail.NullParameter( + nameof(securityToken), + new StackFrame(true)); if (validationParameters == null) - return new ReplayValidationResult( - expirationTime, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII(nameof(validationParameters))), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(), - null)); + return ExceptionDetail.NullParameter( + nameof(validationParameters), + new StackFrame(true)); // check if token if replay cache is set, then there must be an expiration time. if (validationParameters.TokenReplayCache != null) { if (expirationTime == null) - return new ReplayValidationResult( - expirationTime, + return new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10227, + LogHelper.MarkAsUnsafeSecurityArtifact(securityToken, t => t.ToString())), ValidationFailureType.TokenReplayValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10227, - LogHelper.MarkAsUnsafeSecurityArtifact(securityToken, t => t.ToString())), - ExceptionDetail.ExceptionType.SecurityTokenReplayDetected, - new StackFrame(), - null)); + ExceptionType.SecurityTokenReplayDetected, + new StackFrame(true)); if (validationParameters.TokenReplayCache.TryFind(securityToken)) - return new ReplayValidationResult( - expirationTime, + return new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10228, + LogHelper.MarkAsUnsafeSecurityArtifact(securityToken, t => t.ToString())), ValidationFailureType.TokenReplayValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10228, - LogHelper.MarkAsUnsafeSecurityArtifact(securityToken, t => t.ToString())), - ExceptionDetail.ExceptionType.SecurityTokenReplayDetected, - new StackFrame(), - null)); + ExceptionType.SecurityTokenReplayDetected, + new StackFrame(true)); if (!validationParameters.TokenReplayCache.TryAdd(securityToken, expirationTime.Value)) - return new ReplayValidationResult( - expirationTime, + return new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10229, + LogHelper.MarkAsUnsafeSecurityArtifact(securityToken, t => t.ToString())), ValidationFailureType.TokenReplayValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10229, - LogHelper.MarkAsUnsafeSecurityArtifact(securityToken, t => t.ToString())), - ExceptionDetail.ExceptionType.SecurityTokenReplayAddFailed, - new StackFrame(), - null)); + ExceptionType.SecurityTokenReplayAddFailed, + new StackFrame(true)); } // if it reaches here, that means no token replay is detected. - LogHelper.LogInformation(LogMessages.IDX10240); - return new ReplayValidationResult(expirationTime); + // TODO: Move to CallContext + //LogHelper.LogInformation(LogMessages.IDX10240); + return expirationTime; } } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs index 07af5a4300..8982e9ed9f 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs @@ -4,12 +4,12 @@ using System; using System.Diagnostics; using System.Linq; -using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; #nullable enable namespace Microsoft.IdentityModel.Tokens { + internal record struct ValidatedTokenType(string Type, int ValidTypeCount); /// /// Definition for delegate that will validate the token type of a token. /// @@ -17,9 +17,9 @@ namespace Microsoft.IdentityModel.Tokens /// The that is being validated. /// required for validation. /// - /// A that contains the results of validating the token type. + /// A that contains the results of validating the token type. /// An EXACT match is required. (case sensitive) is used for comparing against . - internal delegate TokenTypeValidationResult TypeValidatorDelegate( + internal delegate Result TypeValidatorDelegate( string? type, SecurityToken? securityToken, ValidationParameters validationParameters, @@ -34,10 +34,10 @@ public static partial class Validators /// The that is being validated. /// required for validation. /// - /// A that contains the results of validating the token type. + /// A that contains the results of validating the token type. /// An EXACT match is required. (case sensitive) is used for comparing against . #pragma warning disable CA1801 // TODO: remove pragma disable once callContext is used for logging - internal static TokenTypeValidationResult ValidateTokenType( + internal static Result ValidateTokenType( string? type, SecurityToken? securityToken, ValidationParameters validationParameters, @@ -45,70 +45,45 @@ internal static TokenTypeValidationResult ValidateTokenType( #pragma warning restore CA1801 // TODO: remove pragma disable once callContext is used for logging { if (securityToken == null) - { - return new TokenTypeValidationResult( - type, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII(nameof(securityToken))), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))); - } + return ExceptionDetail.NullParameter( + nameof(securityToken), + new StackFrame(true)); if (validationParameters == null) - { - return new TokenTypeValidationResult( - type, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII(nameof(validationParameters))), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))); - } + return ExceptionDetail.NullParameter( + nameof(validationParameters), + new StackFrame(true)); if (validationParameters.ValidTypes.Count == 0) { LogHelper.LogVerbose(LogMessages.IDX10255); - return new TokenTypeValidationResult(type); + return new ValidatedTokenType(type ?? "null", validationParameters.ValidTypes.Count); } if (string.IsNullOrEmpty(type)) - { - return new TokenTypeValidationResult( - type, + return new ExceptionDetail( + new MessageDetail(LogMessages.IDX10256), ValidationFailureType.TokenTypeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10256, - LogHelper.MarkAsNonPII(nameof(type))), - ExceptionDetail.ExceptionType.SecurityTokenInvalidType, - new StackFrame(true))); - } + ExceptionType.SecurityTokenInvalidType, + new StackFrame(true)); if (!validationParameters.ValidTypes.Contains(type, StringComparer.Ordinal)) { - return new TokenTypeValidationResult( - type, - ValidationFailureType.TokenTypeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10257, - LogHelper.MarkAsNonPII(nameof(type)), - LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidTypes))), - ExceptionDetail.ExceptionType.SecurityTokenInvalidType, - new StackFrame(true))); + return new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10257, + LogHelper.MarkAsNonPII(type), + LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidTypes))), + ValidationFailureType.TokenTypeValidationFailed, + ExceptionType.SecurityTokenInvalidType, + new StackFrame(true)); } - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - { - LogHelper.LogInformation(LogMessages.IDX10258, LogHelper.MarkAsNonPII(type)); - } + // TODO: Move to CallContext + //if (LogHelper.IsEnabled(EventLogLevel.Informational)) + // LogHelper.LogInformation(LogMessages.IDX10258, LogHelper.MarkAsNonPII(type)); - return new TokenTypeValidationResult(type); + return new ValidatedTokenType(type!, validationParameters.ValidTypes.Count); } } } diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.DecryptTokenTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.DecryptTokenTests.cs index da65e0afad..9f3604a566 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.DecryptTokenTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.DecryptTokenTests.cs @@ -2,8 +2,8 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using System.IdentityModel.Tokens.Jwt.Tests; +using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.TestUtils; using Microsoft.IdentityModel.Tokens; @@ -30,29 +30,33 @@ public void DecryptToken(TokenDecryptingTheoryData theoryData) theoryData.Token = new JsonWebToken(tokenString); } - if (theoryData.TestId == "Invalid_NoKeysProvided") - { -#pragma warning disable CS0219 // Variable is assigned but its value is never used - var something = 0; -#pragma warning restore CS0219 // Variable is assigned but its value is never used - } - CompareContext context = TestUtilities.WriteHeader($"{this}.JsonWebTokenHandlerDecryptTokenTests", theoryData); - TokenDecryptionResult tokenDecryptionResult = jsonWebTokenHandler.DecryptToken( + Result result = jsonWebTokenHandler.DecryptToken( theoryData.Token, theoryData.ValidationParameters, theoryData.Configuration, new CallContext()); - if (tokenDecryptionResult.Exception != null) - theoryData.ExpectedException.ProcessException(tokenDecryptionResult.Exception); + if (result.IsSuccess) + { + IdentityComparer.AreStringsEqual( + result.UnwrapResult(), + theoryData.Result.UnwrapResult(), + context); + + theoryData.ExpectedException.ProcessNoException(context); + } else - theoryData.ExpectedException.ProcessNoException(); + { + ExceptionDetail exceptionDetail = result.UnwrapError(); + IdentityComparer.AreStringsEqual( + exceptionDetail.FailureType.Name, + theoryData.Result.UnwrapError().FailureType.Name, + context); - IdentityComparer.AreTokenDecryptingResultsEqual( - tokenDecryptionResult, - theoryData.TokenDecryptionResult, - context); + Exception exception = exceptionDetail.GetException(); + theoryData.ExpectedException.ProcessException(exception, context); + } TestUtilities.AssertFailIfErrors(context); } @@ -61,13 +65,13 @@ public void DecryptToken(TokenDecryptingTheoryData theoryData) public void DecryptToken_ThrowsIfAccessingSecurityTokenOnFailedRead() { JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); - TokenDecryptionResult tokenDecryptionResult = jsonWebTokenHandler.DecryptToken( + Result tokenDecryptionResult = jsonWebTokenHandler.DecryptToken( null, null, null, new CallContext()); - Assert.Throws(() => tokenDecryptionResult.DecryptedToken()); + Assert.Throws(() => tokenDecryptionResult.UnwrapResult()); } public static TheoryData JsonWebTokenHandlerDecryptTokenTestCases @@ -104,13 +108,12 @@ public static TheoryData JsonWebTokenHandlerDecryptTo Token = token, ValidationParameters = new ValidationParameters(), ExpectedException = ExpectedException.SecurityTokenException("IDX10612:"), - TokenDecryptionResult = new TokenDecryptionResult( - token, + Result = new ExceptionDetail( + new MessageDetail(TokenLogMessages.IDX10612), ValidationFailureType.TokenDecryptionFailed, - new ExceptionDetail( - new MessageDetail(TokenLogMessages.IDX10612), - ExceptionDetail.ExceptionType.SecurityToken, - new StackFrame(), null)), + ExceptionType.SecurityToken, + null, + null), }, new TokenDecryptingTheoryData { @@ -118,13 +121,12 @@ public static TheoryData JsonWebTokenHandlerDecryptTo Token = null, ValidationParameters = new ValidationParameters(), ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - TokenDecryptionResult = new TokenDecryptionResult( + Result = new ExceptionDetail( + new MessageDetail(TokenLogMessages.IDX10000, "jwtToken"), + ValidationFailureType.NullArgument, + ExceptionType.ArgumentNull, null, - ValidationFailureType.TokenDecryptionFailed, - new ExceptionDetail( - new MessageDetail(TokenLogMessages.IDX10000, "jwtToken"), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))), + null), }, new TokenDecryptingTheoryData { @@ -132,13 +134,12 @@ public static TheoryData JsonWebTokenHandlerDecryptTo Token = token, ValidationParameters = null, ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - TokenDecryptionResult = new TokenDecryptionResult( - token, - ValidationFailureType.TokenDecryptionFailed, - new ExceptionDetail( - new MessageDetail(TokenLogMessages.IDX10000, "validationParameters"), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))), + Result = new ExceptionDetail( + new MessageDetail(TokenLogMessages.IDX10000, "validationParameters"), + ValidationFailureType.NullArgument, + ExceptionType.ArgumentNull, + null, + null), }, new TokenDecryptingTheoryData { @@ -148,9 +149,7 @@ public static TheoryData JsonWebTokenHandlerDecryptTo { TokenDecryptionKeys = [Default.SymmetricEncryptingCredentials.Key], }, - TokenDecryptionResult = new TokenDecryptionResult( - "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.", - new JsonWebToken(ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims)), + Result = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.", }, new TokenDecryptingTheoryData { @@ -160,9 +159,7 @@ public static TheoryData JsonWebTokenHandlerDecryptTo { TokenDecryptionKeyResolver = (tokenString, token, kid, validationParameters, callContext) => [Default.SymmetricEncryptingCredentials.Key] }, - TokenDecryptionResult = new TokenDecryptionResult( - "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.", - new JsonWebToken(ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims)), + Result = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.", }, new TokenDecryptingTheoryData { @@ -170,9 +167,7 @@ public static TheoryData JsonWebTokenHandlerDecryptTo TokenString = ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims, ValidationParameters = new ValidationParameters(), Configuration = new CustomConfiguration(Default.SymmetricEncryptingCredentials.Key), - TokenDecryptionResult = new TokenDecryptionResult( - "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.", - new JsonWebToken(ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims)), + Result = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.", }, #if NET472 || NET6_0_OR_GREATER new TokenDecryptingTheoryData @@ -184,9 +179,7 @@ public static TheoryData JsonWebTokenHandlerDecryptTo TokenDecryptionKeys = [new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true)], EphemeralDecryptionKey = new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true) }, - TokenDecryptionResult = new TokenDecryptionResult( - "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJleHAiOjI1MzQwMjMwMDgwMCwiaWF0IjowLCJuYmYiOjB9.", - ecdsaToken), + Result = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJleHAiOjI1MzQwMjMwMDgwMCwiaWF0IjowLCJuYmYiOjB9." }, #endif new TokenDecryptingTheoryData @@ -195,17 +188,16 @@ public static TheoryData JsonWebTokenHandlerDecryptTo TokenString = ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims, ValidationParameters = new ValidationParameters(), ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10609:"), - TokenDecryptionResult = new TokenDecryptionResult( - new JsonWebToken(ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims), + Result = new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10609, + LogHelper.MarkAsSecurityArtifact( + new JsonWebToken(ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims), + JwtTokenUtilities.SafeLogJwtToken)), ValidationFailureType.TokenDecryptionFailed, - new ExceptionDetail( - new MessageDetail( - TokenLogMessages.IDX10609, - LogHelper.MarkAsSecurityArtifact( - new JsonWebToken(ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims), - JwtTokenUtilities.SafeLogJwtToken)), - ExceptionDetail.ExceptionType.SecurityTokenDecryptionFailed, - new StackFrame(), null)), + ExceptionType.SecurityTokenDecryptionFailed, + null, + null), } }; } @@ -215,7 +207,7 @@ public static TheoryData JsonWebTokenHandlerDecryptTo public class TokenDecryptingTheoryData : TheoryDataBase { public JsonWebToken Token { get; set; } - internal TokenDecryptionResult TokenDecryptionResult { get; set; } + internal Result Result { get; set; } public BaseConfiguration Configuration { get; internal set; } public SecurityTokenDescriptor SecurityTokenDescriptor { get; internal set; } public string TokenString { get; internal set; } diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ReadTokenTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ReadTokenTests.cs index 848f414789..01912a41ed 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ReadTokenTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ReadTokenTests.cs @@ -17,19 +17,29 @@ public class JsonWebTokenHandlerReadTokenTests public void ReadToken(TokenReadingTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.JsonWebTokenHandlerReadTokenTests", theoryData); - TokenReadingResult tokenReadingResult = JsonWebTokenHandler.ReadToken( + Result result = JsonWebTokenHandler.ReadToken( theoryData.Token, new CallContext()); - if (tokenReadingResult.Exception != null) - theoryData.ExpectedException.ProcessException(tokenReadingResult.Exception); + if (result.IsSuccess) + { + IdentityComparer.AreEqual(result.UnwrapResult(), + theoryData.Result.UnwrapResult(), + context); + + theoryData.ExpectedException.ProcessNoException(context); + } else - theoryData.ExpectedException.ProcessNoException(); + { + ExceptionDetail exceptionDetail = result.UnwrapError(); + IdentityComparer.AreStringsEqual( + exceptionDetail.FailureType.Name, + theoryData.Result.UnwrapError().FailureType.Name, + context); - IdentityComparer.AreTokenReadingResultsEqual( - tokenReadingResult, - theoryData.TokenReadingResult, - context); + Exception exception = exceptionDetail.GetException(); + theoryData.ExpectedException.ProcessException(exception, context); + } TestUtilities.AssertFailIfErrors(context); } @@ -37,11 +47,11 @@ public void ReadToken(TokenReadingTheoryData theoryData) [Fact] public void ReadToken_ThrowsIfAccessingSecurityTokenOnFailedRead() { - TokenReadingResult tokenReadingResult = JsonWebTokenHandler.ReadToken( + Result result = JsonWebTokenHandler.ReadToken( null, new CallContext()); - Assert.Throws(() => tokenReadingResult.SecurityToken()); + Assert.Throws(() => result.UnwrapResult()); } public static TheoryData JsonWebTokenHandlerReadTokenTestCases @@ -55,39 +65,35 @@ public static TheoryData JsonWebTokenHandlerReadTokenTes { TestId = "Valid_Jwt", Token = validToken, - TokenReadingResult = new TokenReadingResult( - new JsonWebToken(validToken), - validToken) + Result = new JsonWebToken(validToken), }, new TokenReadingTheoryData { TestId = "Invalid_NullToken", Token = null, ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - TokenReadingResult = new TokenReadingResult( - null, + Result = new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10000, + LogHelper.MarkAsNonPII("token")), ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - TokenLogMessages.IDX10000, - LogHelper.MarkAsNonPII("token")), - ExceptionDetail.ExceptionType.ArgumentNull, - new System.Diagnostics.StackFrame())) + ExceptionType.ArgumentNull, + null, + null) }, new TokenReadingTheoryData { TestId = "Invalid_EmptyToken", Token = string.Empty, ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - TokenReadingResult = new TokenReadingResult( - string.Empty, + Result = new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10000, + LogHelper.MarkAsNonPII("token")), ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - TokenLogMessages.IDX10000, - LogHelper.MarkAsNonPII("token")), - ExceptionDetail.ExceptionType.ArgumentNull, - new System.Diagnostics.StackFrame())) + ExceptionType.ArgumentNull, + null, + null) }, new TokenReadingTheoryData { @@ -96,15 +102,14 @@ public static TheoryData JsonWebTokenHandlerReadTokenTes ExpectedException = ExpectedException.SecurityTokenMalformedTokenException( "IDX14107:", typeof(SecurityTokenMalformedException)), - TokenReadingResult = new TokenReadingResult( - "malformed-token", + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX14107, + LogHelper.MarkAsNonPII("token")), ValidationFailureType.TokenReadingFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX14107, - LogHelper.MarkAsNonPII("token")), - ExceptionDetail.ExceptionType.SecurityTokenMalformed, - new System.Diagnostics.StackFrame())) + ExceptionType.SecurityTokenMalformed, + null, + new SecurityTokenMalformedException()), } }; } @@ -114,6 +119,6 @@ public static TheoryData JsonWebTokenHandlerReadTokenTes public class TokenReadingTheoryData : TheoryDataBase { public string Token { get; set; } - public object TokenReadingResult { get; set; } + internal Result Result { get; set; } } } diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateSignatureTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateSignatureTests.cs index e1a3c7c27a..b2297b6527 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateSignatureTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateSignatureTests.cs @@ -3,7 +3,6 @@ using System; using System.IdentityModel.Tokens.Jwt.Tests; -using Microsoft.IdentityModel.JsonWebTokens.Results; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.TestUtils; @@ -39,21 +38,36 @@ public void ValidateSignature(JsonWebTokenHandlerValidateSignatureTheoryData the if (theoryData.ValidationParameters is not null && theoryData.KeyToAddToValidationParameters is not null) theoryData.ValidationParameters.IssuerSigningKeys.Add(theoryData.KeyToAddToValidationParameters); - SignatureValidationResult validationResult = JsonWebTokenHandler.ValidateSignature( - jsonWebToken, - theoryData.ValidationParameters, - theoryData.Configuration, - new CallContext + Result result = JsonWebTokenHandler.ValidateSignature( + jsonWebToken, + theoryData.ValidationParameters, + theoryData.Configuration, + new CallContext + { + DebugId = theoryData.TestId + }); + + if (result.IsSuccess) { - DebugId = theoryData.TestId - }); + IdentityComparer.AreSecurityKeysEqual( + result.UnwrapResult(), + theoryData.Result.UnwrapResult(), + context); - if (validationResult.Exception != null) - theoryData.ExpectedException.ProcessException(validationResult.Exception); + theoryData.ExpectedException.ProcessNoException(context); + } else - theoryData.ExpectedException?.ProcessNoException(); + { + ExceptionDetail exceptionDetail = result.UnwrapError(); + IdentityComparer.AreStringsEqual( + exceptionDetail.FailureType.Name, + theoryData.Result.UnwrapError().FailureType.Name, + context); + + Exception exception = exceptionDetail.GetException(); + theoryData.ExpectedException.ProcessException(exception, context); + } - IdentityComparer.AreSignatureValidationResultsEqual(validationResult, theoryData.SignatureValidationResult, context); TestUtilities.AssertFailIfErrors(context); } @@ -68,45 +82,45 @@ public static TheoryData JsonWeb TestId = "Invalid_Null_JWT", JWT = null, ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - SignatureValidationResult = new SignatureValidationResult( - ValidationFailureType.SignatureValidationFailed, - new ExceptionDetail( - new MessageDetail( - TokenLogMessages.IDX10000, - "jwtToken"), - ExceptionDetail.ExceptionType.ArgumentNull, - new System.Diagnostics.StackFrame())) + Result = new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10000, + "jwtToken"), + ValidationFailureType.NullArgument, + ExceptionType.ArgumentNull, + null, + null) }, new JsonWebTokenHandlerValidateSignatureTheoryData { TestId = "Invalid_Null_ValidationParameters", JWT = new JsonWebToken(EncodedJwts.LiveJwt), ValidationParameters = null, ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - SignatureValidationResult = new SignatureValidationResult( - ValidationFailureType.SignatureValidationFailed, - new ExceptionDetail( - new MessageDetail( - TokenLogMessages.IDX10000, - "validationParameters"), - ExceptionDetail.ExceptionType.ArgumentNull, - new System.Diagnostics.StackFrame())) + Result = new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10000, + "validationParameters"), + ValidationFailureType.NullArgument, + ExceptionType.ArgumentNull, + null, + null) }, new JsonWebTokenHandlerValidateSignatureTheoryData { TestId = "Invalid_DelegateReturnsFailure", JWT = new JsonWebToken(EncodedJwts.LiveJwt), ValidationParameters = new ValidationParameters { - SignatureValidator = (token, parameters, configuration, callContext) => SignatureValidationResult.NullParameterFailure("fakeParameter") + SignatureValidator = (token, parameters, configuration, callContext) => ExceptionDetail.NullParameter("fakeParameter", null) }, ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - SignatureValidationResult = new SignatureValidationResult( - ValidationFailureType.SignatureValidationFailed, - new ExceptionDetail( - new MessageDetail( - TokenLogMessages.IDX10000, - "fakeParameter"), - ExceptionDetail.ExceptionType.ArgumentNull, - new System.Diagnostics.StackFrame())) + Result = new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10000, + "fakeParameter"), + ValidationFailureType.NullArgument, + ExceptionType.ArgumentNull, + null, + null) }, new JsonWebTokenHandlerValidateSignatureTheoryData { @@ -114,14 +128,14 @@ public static TheoryData JsonWeb JWT = unsignedToken, ValidationParameters = new ValidationParameters(), ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10504:"), - SignatureValidationResult = new SignatureValidationResult( + Result = new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10504, + LogHelper.MarkAsSecurityArtifact(unsignedToken, JwtTokenUtilities.SafeLogJwtToken)), ValidationFailureType.SignatureValidationFailed, - new ExceptionDetail( - new MessageDetail( - TokenLogMessages.IDX10504, - LogHelper.MarkAsSecurityArtifact(unsignedToken, JwtTokenUtilities.SafeLogJwtToken)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidSignature, - new System.Diagnostics.StackFrame())) + ExceptionType.SecurityTokenInvalidSignature, + null, + null) }, new JsonWebTokenHandlerValidateSignatureTheoryData { @@ -129,9 +143,9 @@ public static TheoryData JsonWeb JWT = new JsonWebToken(EncodedJwts.LiveJwt), ValidationParameters = new ValidationParameters { - SignatureValidator = (token, parameters, configuration, callContext) => SignatureValidationResult.Success() + SignatureValidator = (token, parameters, configuration, callContext) => KeyingMaterial.JsonWebKeyRsa256PublicSigningCredentials.Key }, - SignatureValidationResult = SignatureValidationResult.Success() + Result = KeyingMaterial.JsonWebKeyRsa256PublicSigningCredentials.Key }, new JsonWebTokenHandlerValidateSignatureTheoryData { @@ -139,7 +153,7 @@ public static TheoryData JsonWeb SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, ValidationParameters = new ValidationParameters(), KeyToAddToValidationParameters = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, - SignatureValidationResult = SignatureValidationResult.Success(), + Result = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, }, new JsonWebTokenHandlerValidateSignatureTheoryData { @@ -147,7 +161,7 @@ public static TheoryData JsonWeb SigningCredentials = KeyingMaterial.X509SigningCreds_1024_RsaSha2_Sha2, ValidationParameters = new ValidationParameters(), KeyToAddToValidationParameters = KeyingMaterial.X509SigningCreds_1024_RsaSha2_Sha2.Key, - SignatureValidationResult = SignatureValidationResult.Success() + Result = KeyingMaterial.X509SigningCreds_1024_RsaSha2_Sha2.Key, }, new JsonWebTokenHandlerValidateSignatureTheoryData { @@ -157,7 +171,7 @@ public static TheoryData JsonWeb { IssuerSigningKeyResolver = (token, securityToken, kid, validationParameters, configuration, callContext) => KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key }, - SignatureValidationResult = SignatureValidationResult.Success() + Result = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key }, new JsonWebTokenHandlerValidateSignatureTheoryData { @@ -166,7 +180,7 @@ public static TheoryData JsonWeb Configuration = new OpenIdConnectConfiguration(), KeyToAddToConfiguration = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, ValidationParameters = new ValidationParameters(), - SignatureValidationResult = SignatureValidationResult.Success() + Result = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, }, new JsonWebTokenHandlerValidateSignatureTheoryData { @@ -177,7 +191,7 @@ public static TheoryData JsonWeb TryAllIssuerSigningKeys = true }, KeyToAddToValidationParameters = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2_NoKeyId.Key, - SignatureValidationResult = SignatureValidationResult.Success() + Result = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2_NoKeyId.Key, }, new JsonWebTokenHandlerValidateSignatureTheoryData { @@ -186,12 +200,12 @@ public static TheoryData JsonWeb ValidationParameters = new ValidationParameters(), KeyToAddToValidationParameters = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2_NoKeyId.Key, ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), - SignatureValidationResult = new SignatureValidationResult( + Result = new ExceptionDetail( + new MessageDetail(TokenLogMessages.IDX10500), ValidationFailureType.SignatureValidationFailed, - new ExceptionDetail( - new MessageDetail(TokenLogMessages.IDX10500), - ExceptionDetail.ExceptionType.SecurityTokenSignatureKeyNotFound, - new System.Diagnostics.StackFrame())) + ExceptionType.SecurityTokenSignatureKeyNotFound, + null, + null) }, new JsonWebTokenHandlerValidateSignatureTheoryData { @@ -199,12 +213,12 @@ public static TheoryData JsonWeb JWT = new JsonWebToken(EncodedJwts.LiveJwt), ValidationParameters = new ValidationParameters(), ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), - SignatureValidationResult = new SignatureValidationResult( + Result = new ExceptionDetail( + new MessageDetail(TokenLogMessages.IDX10500), ValidationFailureType.SignatureValidationFailed, - new ExceptionDetail( - new MessageDetail(TokenLogMessages.IDX10500), - ExceptionDetail.ExceptionType.SecurityTokenSignatureKeyNotFound, - new System.Diagnostics.StackFrame())) + ExceptionType.SecurityTokenSignatureKeyNotFound, + null, + null) } }; } @@ -218,7 +232,7 @@ public class JsonWebTokenHandlerValidateSignatureTheoryData : TheoryDataBase public SigningCredentials SigningCredentials { get; internal set; } public SecurityKey KeyToAddToConfiguration { get; internal set; } public SecurityKey KeyToAddToValidationParameters { get; internal set; } - internal SignatureValidationResult SignatureValidationResult { get; set; } + internal Result Result { get; set; } internal ValidationParameters ValidationParameters { get; set; } } } diff --git a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs index f4379d7cc7..614f5f613f 100644 --- a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs +++ b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs @@ -21,7 +21,6 @@ using System.Text.Json; using System.Text.RegularExpressions; using Microsoft.IdentityModel.JsonWebTokens; -using Microsoft.IdentityModel.JsonWebTokens.Results; using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Protocols.WsFederation; @@ -64,7 +63,6 @@ public class IdentityComparer { typeof(IEnumerable).ToString(), AreX509DataEnumsEqual }, { typeof(int).ToString(), AreIntsEqual }, { typeof(IssuerSerial).ToString(), CompareAllPublicProperties }, - { typeof(IssuerValidationResult).ToString(), AreIssuerValidationResultsEqual }, { typeof(JArray).ToString(), AreJArraysEqual }, { typeof(JObject).ToString(), AreJObjectsEqual }, { typeof(JsonElement).ToString(), AreJsonElementsEqual }, @@ -158,73 +156,6 @@ public class IdentityComparer }; // Keep methods in alphabetical order - - public static bool AreAlgorithmValidationResultsEqual(object object1, object object2, CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(object1, object2, context)) - return context.Merge(localContext); - - return AreAlgorithmValidationResultsEqual( - object1 as AlgorithmValidationResult, - object2 as AlgorithmValidationResult, - "AlgorithmValidationResult1", - "AlgorithmValidationResult2", - null, - context); - } - - internal static bool AreAlgorithmValidationResultsEqual( - AlgorithmValidationResult algorithmValidationResult1, - AlgorithmValidationResult algorithmValidationResult2, - string name1, - string name2, - string stackPrefix, - CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(algorithmValidationResult1, algorithmValidationResult2, localContext)) - return context.Merge(localContext); - - if (algorithmValidationResult1.Algorithm != algorithmValidationResult2.Algorithm) - localContext.Diffs.Add($"AlgorithmValidationResult1.Algorithm: '{algorithmValidationResult1.Algorithm}' != AlgorithmValidationResult2.Algorithm: '{algorithmValidationResult2.Algorithm}'"); - - if (algorithmValidationResult1.IsValid != algorithmValidationResult2.IsValid) - localContext.Diffs.Add($"AlgorithmValidationResult1.IsValid: {algorithmValidationResult1.IsValid} != AlgorithmValidationResult2.IsValid: {algorithmValidationResult2.IsValid}"); - - if (algorithmValidationResult1.ValidationFailureType != algorithmValidationResult2.ValidationFailureType) - localContext.Diffs.Add($"AlgorithmValidationResult1.ValidationFailureType: {algorithmValidationResult1.ValidationFailureType} != AlgorithmValidationResult2.ValidationFailureType: {algorithmValidationResult2.ValidationFailureType}"); - - // true => both are not null. - if (ContinueCheckingEquality(algorithmValidationResult1.Exception, algorithmValidationResult2.Exception, localContext)) - { - AreStringsEqual( - algorithmValidationResult1.Exception.Message, - algorithmValidationResult2.Exception.Message, - $"({name1}).Exception.Message", - $"({name2}).Exception.Message", - localContext); - - AreStringsEqual( - algorithmValidationResult1.Exception.Source, - algorithmValidationResult2.Exception.Source, - $"({name1}).Exception.Source", - $"({name2}).Exception.Source", - localContext); - - if (!string.IsNullOrEmpty(stackPrefix)) - AreStringPrefixesEqual( - algorithmValidationResult1.Exception.StackTrace.Trim(), - algorithmValidationResult2.Exception.StackTrace.Trim(), - $"({name1}).Exception.StackTrace", - $"({name2}).Exception.StackTrace", - stackPrefix.Trim(), - localContext); - } - - return context.Merge(localContext); - } - public static bool AreBoolsEqual(object object1, object object2, CompareContext context) { return AreBoolsEqual(object1, object2, "bool1", "bool2", context); @@ -573,6 +504,21 @@ public static bool AreDateTimesEqual(object object1, object object2, CompareCont return context.Merge(localContext); } + public static bool AreDateTimesEqualWithEpsilon(object object1, object object2, int epsilon, CompareContext context) + { + var localContext = new CompareContext(context); + if (!ContinueCheckingEquality(object1, object2, localContext)) + return context.Merge(localContext); + + DateTime dateTime1 = (DateTime)object1; + DateTime dateTime2 = (DateTime)object2; + + if (!AreDatesEqualWithEpsilon(dateTime1, dateTime2, epsilon)) + localContext.Diffs.Add($"dateTime1 != dateTime2. '{dateTime1}' != '{dateTime2}'."); + + return context.Merge(localContext); + } + public static bool AreEqual(object object1, object object2) { return AreEqual(object1, object2, CompareContext.Default); @@ -614,546 +560,6 @@ public static bool AreEqual(object object1, object object2, CompareContext conte return context.Merge(localContext); } - public static bool AreIssuerValidationResultsEqual(object object1, object object2, CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(object1, object2, context)) - return context.Merge(localContext); - - return AreIssuerValidationResultsEqual( - object1 as IssuerValidationResult, - object2 as IssuerValidationResult, - "IssuerValidationResult1", - "IssuerValidationResult2", - null, - context); - } - - internal static bool AreIssuerValidationResultsEqual( - IssuerValidationResult issuerValidationResult1, - IssuerValidationResult issuerValidationResult2, - string name1, - string name2, - string stackPrefix, - CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(issuerValidationResult1, issuerValidationResult2, localContext)) - return context.Merge(localContext); - - if (issuerValidationResult1.Issuer != issuerValidationResult2.Issuer) - localContext.Diffs.Add($"IssuerValidationResult1.Issuer: {issuerValidationResult1.Issuer} != IssuerValidationResult2.Issuer: {issuerValidationResult2.Issuer}"); - - if (issuerValidationResult1.IsValid != issuerValidationResult2.IsValid) - localContext.Diffs.Add($"IssuerValidationResult1.IsValid: {issuerValidationResult1.IsValid} != IssuerValidationResult2.IsValid: {issuerValidationResult2.IsValid}"); - - if (issuerValidationResult1.Source != issuerValidationResult2.Source) - localContext.Diffs.Add($"IssuerValidationResult1.Source: {issuerValidationResult1.Source} != IssuerValidationResult2.Source: {issuerValidationResult2.Source}"); - - // true => both are not null. - if (ContinueCheckingEquality(issuerValidationResult1.Exception, issuerValidationResult2.Exception, localContext)) - { - AreStringsEqual( - issuerValidationResult1.Exception.Message, - issuerValidationResult2.Exception.Message, - $"({name1})issuerValidationResult1.Exception.Message", - $"({name2})issuerValidationResult1.Exception.Message", - localContext); - - AreStringsEqual( - issuerValidationResult1.Exception.Source, - issuerValidationResult2.Exception.Source, - $"({name1})issuerValidationResult1.Exception.Source", - $"({name2})issuerValidationResult2.Exception.Source", - localContext); - - if (!string.IsNullOrEmpty(stackPrefix)) - AreStringPrefixesEqual( - issuerValidationResult1.Exception.StackTrace.Trim(), - issuerValidationResult2.Exception.StackTrace.Trim(), - $"({name1})issuerValidationResult1.Exception.StackTrace", - $"({name2})issuerValidationResult2.Exception.StackTrace", - stackPrefix.Trim(), - localContext); - } - - return context.Merge(localContext); - } - - public static bool AreAudienceValidationResultsEqual(object object1, object object2, CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(object1, object2, context)) - return context.Merge(localContext); - - return AreAudienceValidationResultsEqual( - object1 as AudienceValidationResult, - object2 as AudienceValidationResult, - "AudienceValidationResult1", - "AudienceValidationResult2", - null, - context); - } - - internal static bool AreAudienceValidationResultsEqual( - AudienceValidationResult audienceValidationResult1, - AudienceValidationResult audienceValidationResult2, - string name1, - string name2, - string stackPrefix, - CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(audienceValidationResult1, audienceValidationResult2, localContext)) - return context.Merge(localContext); - - if (audienceValidationResult1.Audience != audienceValidationResult2.Audience) - localContext.Diffs.Add($"AudienceValidationResult1.Audience: '{audienceValidationResult1.Audience}' != AudienceValidationResult2.Audience: '{audienceValidationResult2.Audience}'"); - - if (audienceValidationResult1.IsValid != audienceValidationResult2.IsValid) - localContext.Diffs.Add($"AudienceValidationResult1.IsValid: {audienceValidationResult1.IsValid} != AudienceValidationResult2.IsValid: {audienceValidationResult2.IsValid}"); - - // true => both are not null. - if (ContinueCheckingEquality(audienceValidationResult1.Exception, audienceValidationResult2.Exception, localContext)) - { - AreStringsEqual( - audienceValidationResult1.Exception.Message, - audienceValidationResult2.Exception.Message, - $"({name1})audienceValidationResult1.Exception.Message", - $"({name2})audienceValidationResult2.Exception.Message", - localContext); - - AreStringsEqual( - audienceValidationResult1.Exception.Source, - audienceValidationResult2.Exception.Source, - $"({name1})audienceValidationResult1.Exception.Source", - $"({name2})audienceValidationResult2.Exception.Source", - localContext); - - if (!string.IsNullOrEmpty(stackPrefix)) - AreStringPrefixesEqual( - audienceValidationResult1.Exception.StackTrace.Trim(), - audienceValidationResult2.Exception.StackTrace.Trim(), - $"({name1})audienceValidationResult1.Exception.StackTrace", - $"({name2})audienceValidationResult2.Exception.StackTrace", - stackPrefix.Trim(), - localContext); - } - - return context.Merge(localContext); - } - - public static bool AreSigningKeyValidationResultsEqual(object object1, object object2, CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(object1, object2, context)) - return context.Merge(localContext); - - return AreSigningKeyValidationResultsEqual( - object1 as SigningKeyValidationResult, - object2 as SigningKeyValidationResult, - "SigningKeyValidationResult1", - "SigningKeyValidationResult2", - null, - context); - } - - internal static bool AreSigningKeyValidationResultsEqual( - SigningKeyValidationResult signingKeyValidationResult1, - SigningKeyValidationResult signingKeyValidationResult2, - string name1, - string name2, - string stackPrefix, - CompareContext context) - { - var localContext = new CompareContext(context); - AreSecurityKeysEqual(signingKeyValidationResult1.SigningKey, signingKeyValidationResult2.SigningKey, localContext); - - if (!ContinueCheckingEquality(signingKeyValidationResult1, signingKeyValidationResult2, localContext)) - return context.Merge(localContext); - - if (signingKeyValidationResult1.IsValid != signingKeyValidationResult2.IsValid) - localContext.Diffs.Add($"SigningKeyValidationResult1.IsValid: {signingKeyValidationResult2.IsValid} != SigningKeyValidationResult2.IsValid: {signingKeyValidationResult2.IsValid}"); - - if (signingKeyValidationResult1.ValidationFailureType != signingKeyValidationResult2.ValidationFailureType) - localContext.Diffs.Add($"SigningKeyValidationResult1.ValidationFailureType: {signingKeyValidationResult1.ValidationFailureType} != SigningKeyValidationResult2.ValidationFailureType: {signingKeyValidationResult2.ValidationFailureType}"); - - // true => both are not null. - if (ContinueCheckingEquality(signingKeyValidationResult1.Exception, signingKeyValidationResult2.Exception, localContext)) - { - AreStringsEqual( - signingKeyValidationResult1.Exception.Message, - signingKeyValidationResult2.Exception.Message, - $"({name1})signingKeyValidationResult1.Exception.Message", - $"({name2})signingKeyValidationResult2.Exception.Message", - localContext); - - AreStringsEqual( - signingKeyValidationResult1.Exception.Source, - signingKeyValidationResult2.Exception.Source, - $"({name1})signingKeyValidationResult1.Exception.Source", - $"({name2})signingKeyValidationResult2.Exception.Source", - localContext); - - if (!string.IsNullOrEmpty(stackPrefix)) - AreStringPrefixesEqual( - signingKeyValidationResult1.Exception.StackTrace.Trim(), - signingKeyValidationResult2.Exception.StackTrace.Trim(), - $"({name1})signingKeyValidationResult1.Exception.StackTrace", - $"({name2})signingKeyValidationResult2.Exception.StackTrace", - stackPrefix.Trim(), - localContext); - } - - return context.Merge(localContext); - } - - public static bool AreLifetimeValidationResultsEqual(object object1, object object2, CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(object1, object2, context)) - return context.Merge(localContext); - - return AreLifetimeValidationResultsEqual( - object1 as LifetimeValidationResult, - object2 as LifetimeValidationResult, - "LifetimeValidationResult1", - "LifetimeValidationResult2", - null, - context); - } - - internal static bool AreLifetimeValidationResultsEqual( - LifetimeValidationResult lifetimeValidationResult1, - LifetimeValidationResult lifetimeValidationResult2, - string name1, - string name2, - string stackPrefix, - CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(lifetimeValidationResult1, lifetimeValidationResult2, localContext)) - return context.Merge(localContext); - - if (!AreDatesEqualWithEpsilon(lifetimeValidationResult1.NotBefore, lifetimeValidationResult2.NotBefore, 1)) - localContext.Diffs.Add($"LifetimeValidationResult1.NotBefore: '{lifetimeValidationResult1.NotBefore}' != LifetimeValidationResult2.NotBefore: '{lifetimeValidationResult2.NotBefore}'"); - - if (!AreDatesEqualWithEpsilon(lifetimeValidationResult1.Expires, lifetimeValidationResult2.Expires, 1)) - localContext.Diffs.Add($"LifetimeValidationResult1.Expires: '{lifetimeValidationResult1.Expires}' != LifetimeValidationResult2.Expires: '{lifetimeValidationResult2.Expires}'"); - - if (lifetimeValidationResult1.IsValid != lifetimeValidationResult2.IsValid) - localContext.Diffs.Add($"LifetimeValidationResult1.IsValid: {lifetimeValidationResult1.IsValid} != LifetimeValidationResult2.IsValid: {lifetimeValidationResult2.IsValid}"); - - if (lifetimeValidationResult1.ValidationFailureType != lifetimeValidationResult2.ValidationFailureType) - localContext.Diffs.Add($"LifetimeValidationResult1.ValidationFailureType: {lifetimeValidationResult1.ValidationFailureType} != LifetimeValidationResult2.ValidationFailureType: {lifetimeValidationResult2.ValidationFailureType}"); - - // true => both are not null. - if (ContinueCheckingEquality(lifetimeValidationResult1.Exception, lifetimeValidationResult2.Exception, localContext)) - { - AreStringsEqual( - lifetimeValidationResult1.Exception.Message, - lifetimeValidationResult2.Exception.Message, - $"({name1})lifetimeValidationResult1.Exception.Message", - $"({name2})lifetimeValidationResult2.Exception.Message", - localContext); - - AreStringsEqual( - lifetimeValidationResult1.Exception.Source, - lifetimeValidationResult2.Exception.Source, - $"({name1})lifetimeValidationResult1.Exception.Source", - $"({name2})lifetimeValidationResult2.Exception.Source", - localContext); - - if (!string.IsNullOrEmpty(stackPrefix)) - AreStringPrefixesEqual( - lifetimeValidationResult1.Exception.StackTrace.Trim(), - lifetimeValidationResult2.Exception.StackTrace.Trim(), - $"({name1})lifetimeValidationResult1.Exception.StackTrace", - $"({name2})lifetimeValidationResult2.Exception.StackTrace", - stackPrefix.Trim(), - localContext); - } - - return context.Merge(localContext); - } - - public static bool AreTokenReplayValidationResultsEqual(object object1, object object2, CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(object1, object2, context)) - return context.Merge(localContext); - - return AreTokenReplayValidationResultsEqual( - object1 as ReplayValidationResult, - object2 as ReplayValidationResult, - "ReplayValidationResult1", - "ReplayValidationResult2", - null, - context); - } - - internal static bool AreTokenReplayValidationResultsEqual( - ReplayValidationResult replayValidationResult1, - ReplayValidationResult replayValidationResult2, - string name1, - string name2, - string stackPrefix, - CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(replayValidationResult1, replayValidationResult2, localContext)) - return context.Merge(localContext); - - if (replayValidationResult1.ExpirationTime != replayValidationResult2.ExpirationTime) - localContext.Diffs.Add($"ReplayValidationResult1.ExpirationTime: '{replayValidationResult1.ExpirationTime}' != ReplayValidationResult2.ExpirationTime: '{replayValidationResult2.ExpirationTime}'"); - - if (replayValidationResult1.IsValid != replayValidationResult2.IsValid) - localContext.Diffs.Add($"ReplayValidationResult1.IsValid: {replayValidationResult1.IsValid} != ReplayValidationResult2.IsValid: {replayValidationResult2.IsValid}"); - - if (replayValidationResult1.ValidationFailureType != replayValidationResult2.ValidationFailureType) - localContext.Diffs.Add($"ReplayValidationResult1.ValidationFailureType: {replayValidationResult1.ValidationFailureType} != ReplayValidationResult2.ValidationFailureType: {replayValidationResult2.ValidationFailureType}"); - - // true => both are not null. - if (ContinueCheckingEquality(replayValidationResult1.Exception, replayValidationResult2.Exception, localContext)) - { - AreStringsEqual( - replayValidationResult1.Exception.Message, - replayValidationResult2.Exception.Message, - $"({name1}).Exception.Message", - $"({name2}).Exception.Message", - localContext); - - AreStringsEqual( - replayValidationResult1.Exception.Source, - replayValidationResult2.Exception.Source, - $"({name1}).Exception.Source", - $"({name2}).Exception.Source", - localContext); - - if (!string.IsNullOrEmpty(stackPrefix)) - AreStringPrefixesEqual( - replayValidationResult1.Exception.StackTrace.Trim(), - replayValidationResult2.Exception.StackTrace.Trim(), - $"({name1}).Exception.StackTrace", - $"({name2}).Exception.StackTrace", - stackPrefix.Trim(), - localContext); - } - - return context.Merge(localContext); - } - - public static bool AreTokenTypeValidationResultsEqual(object object1, object object2, CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(object1, object2, context)) - return context.Merge(localContext); - - return AreTokenTypeValidationResultsEqual( - object1 as TokenTypeValidationResult, - object2 as TokenTypeValidationResult, - "TokenTypeValidationResult1", - "TokenTypeValidationResult2", - null, - context); - } - - internal static bool AreTokenTypeValidationResultsEqual( - TokenTypeValidationResult tokenTypeValidationResult1, - TokenTypeValidationResult tokenTypeValidationResult2, - string name1, - string name2, - string stackPrefix, - CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(tokenTypeValidationResult1, tokenTypeValidationResult2, localContext)) - return context.Merge(localContext); - - if (tokenTypeValidationResult1.Type != tokenTypeValidationResult2.Type) - localContext.Diffs.Add($"TokenTypeValidationResult1.Type: '{tokenTypeValidationResult1.Type}' != TokenTypeValidationResult2.Type: '{tokenTypeValidationResult2.Type}'"); - - if (tokenTypeValidationResult1.IsValid != tokenTypeValidationResult2.IsValid) - localContext.Diffs.Add($"TokenTypeValidationResult1.IsValid: {tokenTypeValidationResult1.IsValid} != TokenTypeValidationResult2.IsValid: {tokenTypeValidationResult2.IsValid}"); - - if (tokenTypeValidationResult1.ValidationFailureType != tokenTypeValidationResult2.ValidationFailureType) - localContext.Diffs.Add($"TokenTypeValidationResult1.ValidationFailureType: {tokenTypeValidationResult1.ValidationFailureType} != TokenTypeValidationResult2.ValidationFailureType: {tokenTypeValidationResult2.ValidationFailureType}"); - - // true => both are not null. - if (ContinueCheckingEquality(tokenTypeValidationResult1.Exception, tokenTypeValidationResult2.Exception, localContext)) - { - AreStringsEqual( - tokenTypeValidationResult1.Exception.Message, - tokenTypeValidationResult2.Exception.Message, - $"({name1}).Exception.Message", - $"({name2}).Exception.Message", - localContext); - - AreStringsEqual( - tokenTypeValidationResult1.Exception.Source, - tokenTypeValidationResult2.Exception.Source, - $"({name1}).Exception.Source", - $"({name2}).Exception.Source", - localContext); - - if (!string.IsNullOrEmpty(stackPrefix)) - AreStringPrefixesEqual( - tokenTypeValidationResult1.Exception.StackTrace.Trim(), - tokenTypeValidationResult2.Exception.StackTrace.Trim(), - $"({name1}).Exception.StackTrace", - $"({name2}).Exception.StackTrace", - stackPrefix.Trim(), - localContext); - } - - return context.Merge(localContext); - } - - public static bool AreTokenReadingResultsEqual(object object1, object object2, CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(object1, object2, context)) - return context.Merge(localContext); - - return AreTokenReadingResultsEqual( - object1 as TokenReadingResult, - object2 as TokenReadingResult, - "TokenReadingResult1", - "TokenReadingResult2", - null, - context); - } - - internal static bool AreTokenReadingResultsEqual( - TokenReadingResult tokenReadingResult1, - TokenReadingResult tokenReadingResult2, - string name1, - string name2, - string stackPrefix, - CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(tokenReadingResult1, tokenReadingResult2, localContext)) - return context.Merge(localContext); - - if (tokenReadingResult1.IsValid != tokenReadingResult2.IsValid) - localContext.Diffs.Add($"TokenReadingResult1.IsValid: {tokenReadingResult1.IsValid} != TokenReadingResult2.IsValid: {tokenReadingResult2.IsValid}"); - - if (tokenReadingResult1.TokenInput != tokenReadingResult2.TokenInput) - localContext.Diffs.Add($"TokenReadingResult1.TokenInput: '{tokenReadingResult1.TokenInput}' != TokenReadingResult2.TokenInput: '{tokenReadingResult2.TokenInput}'"); - - // Only compare the security token if both are valid. - if (tokenReadingResult1.IsValid && (tokenReadingResult1.SecurityToken().ToString() != tokenReadingResult2.SecurityToken().ToString())) - localContext.Diffs.Add($"TokenReadingResult1.SecurityToken: '{tokenReadingResult1.SecurityToken()}' != TokenReadingResult2.SecurityToken: '{tokenReadingResult2.SecurityToken()}'"); - - if (tokenReadingResult1.ValidationFailureType != tokenReadingResult2.ValidationFailureType) - localContext.Diffs.Add($"TokenReadingResult1.ValidationFailureType: {tokenReadingResult1.ValidationFailureType} != TokenReadingResult2.ValidationFailureType: {tokenReadingResult2.ValidationFailureType}"); - - // true => both are not null. - if (ContinueCheckingEquality(tokenReadingResult1.Exception, tokenReadingResult2.Exception, localContext)) - { - AreStringsEqual( - tokenReadingResult1.Exception.Message, - tokenReadingResult2.Exception.Message, - $"({name1}).Exception.Message", - $"({name2}).Exception.Message", - localContext); - - AreStringsEqual( - tokenReadingResult1.Exception.Source, - tokenReadingResult2.Exception.Source, - $"({name1}).Exception.Source", - $"({name2}).Exception.Source", - localContext); - - if (!string.IsNullOrEmpty(stackPrefix)) - AreStringPrefixesEqual( - tokenReadingResult1.Exception.StackTrace.Trim(), - tokenReadingResult2.Exception.StackTrace.Trim(), - $"({name1}).Exception.StackTrace", - $"({name2}).Exception.StackTrace", - stackPrefix.Trim(), - localContext); - } - - return context.Merge(localContext); - } - - public static bool AreTokenDecryptingResultsEqual(object object1, object object2, CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(object1, object2, context)) - return context.Merge(localContext); - - return AreTokenDecryptingResultsEqual( - object1 as TokenDecryptionResult, - object2 as TokenDecryptionResult, - "TokenDecryptingResult1", - "TokenDecryptingResult2", - null, - context); - } - - internal static bool AreTokenDecryptingResultsEqual( - TokenDecryptionResult tokenDecryptingResult1, - TokenDecryptionResult tokenDecryptingResult2, - string name1, - string name2, - string stackPrefix, - CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(tokenDecryptingResult1, tokenDecryptingResult2, localContext)) - return context.Merge(localContext); - - if (tokenDecryptingResult1.IsValid != tokenDecryptingResult2.IsValid) - localContext.Diffs.Add($"TokenDecryptingResult1.IsValid: {tokenDecryptingResult1.IsValid} != TokenDecryptingResult2.IsValid: {tokenDecryptingResult2.IsValid}"); - - if (tokenDecryptingResult1.SecurityToken == null || tokenDecryptingResult2.SecurityToken == null) - { - if (tokenDecryptingResult1.SecurityToken != tokenDecryptingResult2.SecurityToken) - localContext.Diffs.Add($"TokenDecryptingResult1.SecurityToken: '{tokenDecryptingResult1.SecurityToken}' != TokenDecryptingResult2.SecurityToken: '{tokenDecryptingResult2.SecurityToken}'"); - } - else if (tokenDecryptingResult1.SecurityToken.ToString() != tokenDecryptingResult2.SecurityToken.ToString()) - localContext.Diffs.Add($"TokenDecryptingResult1.SecurityToken: '{tokenDecryptingResult1.SecurityToken}' != TokenDecryptingResult2.SecurityToken: '{tokenDecryptingResult2.SecurityToken}'"); - - // Only compare the decrypted token if both results are valid. - if (tokenDecryptingResult1.IsValid && (tokenDecryptingResult1.DecryptedToken().ToString() != tokenDecryptingResult2.DecryptedToken().ToString())) - localContext.Diffs.Add($"TokenDecryptingResult1.DecryptedToken: '{tokenDecryptingResult1.DecryptedToken()}' != TokenDecryptingResult2.DecryptedToken: '{tokenDecryptingResult2.DecryptedToken()}'"); - - if (tokenDecryptingResult1.ValidationFailureType != tokenDecryptingResult2.ValidationFailureType) - localContext.Diffs.Add($"TokenDecryptingResult1.ValidationFailureType: {tokenDecryptingResult1.ValidationFailureType} != TokenDecryptingResult1.ValidationFailureType: {tokenDecryptingResult2.ValidationFailureType}"); - - // true => both are not null. - if (ContinueCheckingEquality(tokenDecryptingResult1.Exception, tokenDecryptingResult2.Exception, localContext)) - { - AreStringsEqual( - tokenDecryptingResult1.Exception.Message, - tokenDecryptingResult2.Exception.Message, - $"({name1}).Exception.Message", - $"({name2}).Exception.Message", - localContext); - - AreStringsEqual( - tokenDecryptingResult1.Exception.Source, - tokenDecryptingResult2.Exception.Source, - $"({name1}).Exception.Source", - $"({name2}).Exception.Source", - localContext); - - if (!string.IsNullOrEmpty(stackPrefix)) - AreStringPrefixesEqual( - tokenDecryptingResult1.Exception.StackTrace.Trim(), - tokenDecryptingResult2.Exception.StackTrace.Trim(), - $"({name1}).Exception.StackTrace", - $"({name2}).Exception.StackTrace", - stackPrefix.Trim(), - localContext); - } - - return context.Merge(localContext); - } - public static bool AreJArraysEqual(object object1, object object2, CompareContext context) { var localContext = new CompareContext(context); @@ -1628,69 +1034,6 @@ public static bool AreSecurityKeyEnumsEqual(object object1, object object2, Comp return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreSecurityKeysEqual); } - public static bool AreSignatureValidationResultsEqual(object object1, object object2, CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(object1, object2, context)) - return context.Merge(localContext); - - return AreSignatureValidationResultsEqual( - object1 as SignatureValidationResult, - object2 as SignatureValidationResult, - "SignatureValidationResult1", - "SignatureValidationResult2", - null, - context); - } - - internal static bool AreSignatureValidationResultsEqual( - SignatureValidationResult signatureValidationResult1, - SignatureValidationResult signatureValidationResult2, - string name1, - string name2, - string stackPrefix, - CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(signatureValidationResult1, signatureValidationResult2, localContext)) - return context.Merge(localContext); - - if (signatureValidationResult1.IsValid != signatureValidationResult2.IsValid) - localContext.Diffs.Add($"{name1}.IsValid: {signatureValidationResult1.IsValid} != {name2}.IsValid: {signatureValidationResult2.IsValid}"); - - if (signatureValidationResult1.ValidationFailureType != signatureValidationResult2.ValidationFailureType) - localContext.Diffs.Add($"{name1}.IsValid: {signatureValidationResult1.ValidationFailureType} != {name2}.IsValid: {signatureValidationResult2.ValidationFailureType}"); - - // true => both are not null. - if (ContinueCheckingEquality(signatureValidationResult1.Exception, signatureValidationResult2.Exception, localContext)) - { - AreStringsEqual( - signatureValidationResult1.Exception.Message, - signatureValidationResult2.Exception.Message, - $"({name1}).Exception.Message", - $"({name2}).Exception.Message", - localContext); - - AreStringsEqual( - signatureValidationResult1.Exception.Source, - signatureValidationResult2.Exception.Source, - $"({name1}).Exception.Source", - $"({name2}).Exception.Source", - localContext); - - if (!string.IsNullOrEmpty(stackPrefix)) - AreStringPrefixesEqual( - signatureValidationResult1.Exception.StackTrace.Trim(), - signatureValidationResult2.Exception.StackTrace.Trim(), - $"({name1}).Exception.StackTrace", - $"({name2}).Exception.StackTrace", - stackPrefix.Trim(), - localContext); - } - - return context.Merge(localContext); - } - public static bool AreSignedInfosEqual(SignedInfo signedInfo1, SignedInfo signedInfo2, CompareContext context) { var localContext = new CompareContext(context); @@ -1885,6 +1228,92 @@ public static bool AreTimeSpansEqual(object object1, object object2, CompareCont return context.Merge(localContext); } + internal static bool AreValidatedIssuersEqual(ValidatedIssuer validatedIssuer1, ValidatedIssuer validatedIssuer2, CompareContext context) + { + var localContext = new CompareContext(context); + + AreStringsEqual( + validatedIssuer1.Issuer, + validatedIssuer2.Issuer, + "validatedIssuer1.Issuer", + "validatedIssuer2.Issuer", + localContext); + + AreIntsEqual( + (int)validatedIssuer1.ValidationSource, + (int)validatedIssuer2.ValidationSource, + "validatedIssuer1.ValidationSource", + "validatedIssuer2.ValidationSource", + localContext); + + return context.Merge(localContext); + } + + internal static bool AreValidatedLifetimesEqual(ValidatedLifetime validatedLifetime1, ValidatedLifetime validatedLifetime2, CompareContext context) + { + var localContext = new CompareContext(context); + + AreDateTimesEqualWithEpsilon( + validatedLifetime1.NotBefore, + validatedLifetime2.NotBefore, + 1, + localContext); + + AreDateTimesEqualWithEpsilon( + validatedLifetime1.Expires, + validatedLifetime2.Expires, + 1, + localContext); + + return context.Merge(localContext); + } + + internal static bool AreValidatedSigningKeyLifetimesEqual(ValidatedSigningKeyLifetime validatedSigningKeyLifetime1, ValidatedSigningKeyLifetime validatedSigningKeyLifetime2, CompareContext context) + { + var localContext = new CompareContext(context); + + AreDateTimesEqualWithEpsilon( + validatedSigningKeyLifetime1.ValidFrom, + validatedSigningKeyLifetime2.ValidFrom, + 3, + localContext); + + AreDateTimesEqualWithEpsilon( + validatedSigningKeyLifetime1.ValidTo, + validatedSigningKeyLifetime2.ValidTo, + 3, + localContext); + + AreDateTimesEqualWithEpsilon( + validatedSigningKeyLifetime1.ValidationTime, + validatedSigningKeyLifetime2.ValidationTime, + 3, + localContext); + + return context.Merge(localContext); + } + + internal static bool AreValidatedTokenTypesEqual(ValidatedTokenType validatedTokenType1, ValidatedTokenType validatedTokenType2, CompareContext context) + { + var localContext = new CompareContext(context); + + AreStringsEqual( + validatedTokenType1.Type, + validatedTokenType2.Type, + "validatedTokenType1.Type", + "validatedTokenType2.Type", + localContext); + + AreIntsEqual( + validatedTokenType1.ValidTypeCount, + validatedTokenType2.ValidTypeCount, + "validatedTokenType1.ValidTypeCount", + "validatedTokenType2.ValidTypeCount", + localContext); + + return context.Merge(localContext); + } + private static bool AreValueCollectionsEqual(Object object1, Object object2, CompareContext context) { Dictionary.ValueCollection vc1 = (Dictionary.ValueCollection)object1; diff --git a/test/Microsoft.IdentityModel.TestUtils/InternalsVisibleTo.cs b/test/Microsoft.IdentityModel.TestUtils/InternalsVisibleTo.cs new file mode 100644 index 0000000000..84597b7355 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/InternalsVisibleTo.cs @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.JsonWebTokens.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.Tokens.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AlgorithmValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AlgorithmValidationResultTests.cs index 0dc4f8e77a..a5c0d9fa35 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AlgorithmValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AlgorithmValidationResultTests.cs @@ -1,11 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; -using System.Diagnostics; using Microsoft.IdentityModel.TestUtils; using Microsoft.IdentityModel.Logging; using Xunit; +using System; namespace Microsoft.IdentityModel.Tokens.Validation.Tests { @@ -16,22 +15,33 @@ public void ValidateAlgorithm(AlgorithmTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.AlgorithmValidationResultTests", theoryData); - AlgorithmValidationResult algorithmValidationResult = Validators.ValidateAlgorithm( + Result result = Validators.ValidateAlgorithm( theoryData.Algorithm, theoryData.SecurityKey, theoryData.SecurityToken, theoryData.ValidationParameters, new CallContext()); - if (algorithmValidationResult.Exception != null) - theoryData.ExpectedException.ProcessException(algorithmValidationResult.Exception); - else + if (result.IsSuccess) + { + IdentityComparer.AreStringsEqual( + result.UnwrapResult(), + theoryData.Result.UnwrapResult(), + context); + theoryData.ExpectedException.ProcessNoException(); + } + else + { + ExceptionDetail exceptionDetail = result.UnwrapError(); + IdentityComparer.AreStringsEqual( + exceptionDetail.FailureType.Name, + theoryData.Result.UnwrapError().FailureType.Name, + context); - IdentityComparer.AreAlgorithmValidationResultsEqual( - algorithmValidationResult, - theoryData.AlgorithmValidationResult, - context); + Exception exception = exceptionDetail.GetException(); + theoryData.ExpectedException.ProcessException(exception, context); + } TestUtilities.AssertFailIfErrors(context); } @@ -47,42 +57,39 @@ public static TheoryData AlgorithmValidationTestCases new AlgorithmTheoryData { TestId = "Invalid_ValidationParametersAreNull", - ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), Algorithm = null, + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), SecurityKey = null, SecurityToken = null, ValidationParameters = null, - AlgorithmValidationResult = new AlgorithmValidationResult( - null, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("validationParameters")), ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII("validationParameters")), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))) + ExceptionType.ArgumentNull, + null, // StackFrame + null) // InnerException }, new AlgorithmTheoryData { TestId = "Invalid_ValidateAlgorithmNotAValidAlgorithm", - ExpectedException = ExpectedException.SecurityTokenInvalidAlgorithmException("IDX10696:"), Algorithm = SecurityAlgorithms.Sha256, + ExpectedException = ExpectedException.SecurityTokenInvalidAlgorithmException("IDX10696:"), SecurityKey = securityKey, SecurityToken = null, ValidationParameters = new ValidationParameters { ValidAlgorithms = new[] { SecurityAlgorithms.HmacSha256 } }, - AlgorithmValidationResult = new AlgorithmValidationResult( - SecurityAlgorithms.Sha256, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10696, + LogHelper.MarkAsNonPII(SecurityAlgorithms.Sha256)), ValidationFailureType.AlgorithmValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10696, - LogHelper.MarkAsNonPII(SecurityAlgorithms.Sha256), - securityKey), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAlgorithm, - new StackFrame(true))) + ExceptionType.SecurityTokenInvalidAlgorithm, + null, // StackFrame + null),// InnerException }, new AlgorithmTheoryData { @@ -94,7 +101,7 @@ public static TheoryData AlgorithmValidationTestCases { ValidAlgorithms = null }, - AlgorithmValidationResult = new AlgorithmValidationResult(SecurityAlgorithms.Sha256) + Result = SecurityAlgorithms.Sha256 }, new AlgorithmTheoryData { @@ -106,7 +113,7 @@ public static TheoryData AlgorithmValidationTestCases { ValidAlgorithms = new[] { SecurityAlgorithms.HmacSha256, SecurityAlgorithms.Sha256 } }, - AlgorithmValidationResult = new AlgorithmValidationResult(SecurityAlgorithms.Sha256) + Result = SecurityAlgorithms.Sha256 } }; } @@ -122,7 +129,7 @@ public class AlgorithmTheoryData : TheoryDataBase internal ValidationParameters ValidationParameters { get; set; } - internal AlgorithmValidationResult AlgorithmValidationResult { get; set; } + internal Result Result { get; set; } } } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AsyncValidatorsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AsyncValidatorsTests.cs deleted file mode 100644 index 459a32c88b..0000000000 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AsyncValidatorsTests.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.IdentityModel.TestUtils; -using Xunit; - -namespace Microsoft.IdentityModel.Tokens.Validation.Tests -{ - public class AsyncValidatorTests - { - [Theory, MemberData(nameof(AsyncIssuerValidatorTestCases))] - public async Task AsyncIssuerValidatorTests(IssuerValidatorTheoryData theoryData) - { - CompareContext context = TestUtilities.WriteHeader($"{this}.AsyncIssuerValidatorTests", theoryData); - try - { - IssuerValidationResult result = await Validators.ValidateIssuerAsync( - theoryData.Issuer, - theoryData.SecurityToken, - theoryData.ValidationParameters, - null, - CancellationToken.None); - Exception exception = result.Exception; - context.Diffs.Add("Exception: " + exception.ToString()); - } - catch (Exception ex) - { - context.Diffs.Add("Exception: " + ex.ToString()); - } - } - - public static TheoryData AsyncIssuerValidatorTestCases - { - get - { - TheoryData theoryData = new TheoryData(); - - theoryData.Add(new IssuerValidatorTheoryData - { - Issuer = null, - ValidationParameters = new ValidationParameters(), - }); - - return theoryData; - } - } - } - - public class IssuerValidatorTheoryData : TheoryDataBase - { - public string Issuer { get; set; } - internal ValidationParameters ValidationParameters { get; set; } - public SecurityToken SecurityToken { get; set; } - } -} diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AudienceValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AudienceValidationResultTests.cs index 064c30be8c..a97fcdbea5 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AudienceValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AudienceValidationResultTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.TestUtils; @@ -14,7 +13,7 @@ namespace Microsoft.IdentityModel.Tokens.Validation.Tests { public class AudienceValidationResultTests { - [Theory, MemberData(nameof(ValidateAudienceParametersTestCases), DisableDiscoveryEnumeration = true)] + [Theory, MemberData(nameof(ValidateAudienceTestCases), DisableDiscoveryEnumeration = true)] public void ValidateAudienceParameters(AudienceValidationTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.AudienceValidatorResultTests", theoryData); @@ -25,463 +24,395 @@ public void ValidateAudienceParameters(AudienceValidationTheoryData theoryData) theoryData.ValidationParameters.ValidAudiences.Add(audience); } - AudienceValidationResult audienceValidationResult = Validators.ValidateAudience( + Result result = Validators.ValidateAudience( theoryData.Audiences, theoryData.SecurityToken, theoryData.ValidationParameters, new CallContext()); - if (audienceValidationResult.Exception == null) - theoryData.ExpectedException.ProcessNoException(); + if (result.IsSuccess) + { + IdentityComparer.AreStringsEqual( + result.UnwrapResult(), + theoryData.Result.UnwrapResult(), + context); + + theoryData.ExpectedException.ProcessNoException(context); + } else - theoryData.ExpectedException.ProcessException(audienceValidationResult.Exception, context); + { + ExceptionDetail exceptionDetail = result.UnwrapError(); + IdentityComparer.AreStringsEqual( + exceptionDetail.FailureType.Name, + theoryData.Result.UnwrapError().FailureType.Name, + context); - IdentityComparer.AreAudienceValidationResultsEqual( - audienceValidationResult, - theoryData.AudienceValidationResult, - context); + Exception exception = exceptionDetail.GetException(); + theoryData.ExpectedException.ProcessException(exception, context); + } TestUtilities.AssertFailIfErrors(context); } - public static TheoryData ValidateAudienceParametersTestCases + public static TheoryData ValidateAudienceTestCases { get { + var audience1 = "http://audience1.com"; + var audience2 = "http://audience2.com"; + List audiences1 = new List { "", audience1 }; + List audiences1WithSlash = new List { "", audience1 + "/" }; + List audiences1WithTwoSlashes = new List { "", audience1 + "//" }; + List audiences2 = new List { "", audience2 }; + List audiences2WithSlash = new List { "", audience2 + "/" }; + + var commaAudience1 = ", " + audience1; + var commaAudience2 = ", " + audience2; + var audience1Slash = audience1 + "/"; + var audience2Slash = audience2 + "/"; + var commaAudience1Slash = commaAudience1 + "/"; + var commaAudience2Slash = commaAudience2 + "/"; + return new TheoryData { new AudienceValidationTheoryData { Audiences = new List { "audience1" }, ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - TestId = "ValidationParametersNull", + TestId = "Invalid_ValidationParametersIsNull", ValidationParameters = null, - AudienceValidationResult = new AudienceValidationResult( - "audience1", + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("validationParameters")), ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII("validationParameters")), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true), - null)), + ExceptionType.ArgumentNull, + null, + null) }, new AudienceValidationTheoryData { Audiences = null, + TestId = "Invalid_AudiencesIsNull", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10207:"), - TestId = "AudiencesNull", - AudienceValidationResult = new AudienceValidationResult( - "null", - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10207, - null), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail(LogMessages.IDX10207), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) }, new AudienceValidationTheoryData { Audiences = new List{ }, + TestId = "Invalid_AudiencesIsEmptyList", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10206:"), - TestId = "AudiencesEmptyList", ValidationParameters = new ValidationParameters(), - AudienceValidationResult = new AudienceValidationResult( - "empty", - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10206, - null), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10206, + null), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) }, new AudienceValidationTheoryData { Audiences = new List { "audience1" }, + TestId = "Invalid_ValidAudiencesIsEmptyString", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), - TestId = "ValidAudiencesEmptyString", ValidationParameters = new ValidationParameters(), AudiencesToAdd = [String.Empty], - AudienceValidationResult = new AudienceValidationResult( - "audience1", - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII("audience1"), - LogHelper.MarkAsNonPII(String.Empty)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII("audience1"), + LogHelper.MarkAsNonPII(String.Empty)), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) }, new AudienceValidationTheoryData { Audiences = new List { "audience1" }, + TestId = "Invalid_ValidAudiencesIsWhiteSpace", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), - TestId = "ValidAudiencesWhiteSpace", ValidationParameters = new ValidationParameters(), AudiencesToAdd = [" "], - AudienceValidationResult = new AudienceValidationResult( - "audience1", - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII("audience1"), - LogHelper.MarkAsNonPII(" ")), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), - } - }; - } - } - - [Theory, MemberData(nameof(ValidateAudienceTheoryData))] - public void ValidateAudience(AudienceValidationTheoryData theoryData) - { - var context = TestUtilities.WriteHeader($"{this}.ValidateAudience", theoryData); - - if (theoryData.AudiencesToAdd != null) - { - foreach (string audience in theoryData.AudiencesToAdd) - theoryData.ValidationParameters.ValidAudiences.Add(audience); - } - - AudienceValidationResult audienceValidationResult = Validators.ValidateAudience( - theoryData.Audiences, - theoryData.SecurityToken, - theoryData.ValidationParameters, - new CallContext()); - - if (audienceValidationResult.Exception != null) - theoryData.ExpectedException.ProcessException(audienceValidationResult.Exception); - else - theoryData.ExpectedException.ProcessNoException(context); - - IdentityComparer.AreAudienceValidationResultsEqual( - audienceValidationResult, - theoryData.AudienceValidationResult, - context); - - TestUtilities.AssertFailIfErrors(context); - } - - public static TheoryData ValidateAudienceTheoryData - { - get - { - var audience1 = "http://audience1.com"; - var audience2 = "http://audience2.com"; - List audiences1 = new List { "", audience1 }; - List audiences1WithSlash = new List { "", audience1 + "/" }; - List audiences1WithTwoSlashes = new List { "", audience1 + "//" }; - List audiences2 = new List { "", audience2 }; - List audiences2WithSlash = new List { "", audience2 + "/" }; - - var commaAudience1 = ", " + audience1; - var commaAudience2 = ", " + audience2; - var audience1Slash = audience1 + "/"; - var audience2Slash = audience2 + "/"; - var commaAudience1Slash = commaAudience1 + "/"; - var commaAudience2Slash = commaAudience2 + "/"; - - return new TheoryData - { + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII("audience1"), + LogHelper.MarkAsNonPII(" ")), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) + }, new AudienceValidationTheoryData { Audiences = audiences1, - TestId = "SameLengthMatched", + TestId = "Valid_SameLengthMatched", ValidationParameters = new ValidationParameters(), AudiencesToAdd = [audience1], SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, "Issuer"), - AudienceValidationResult = new AudienceValidationResult(audience1) + Result = audience1 }, new AudienceValidationTheoryData { Audiences = audiences1, + TestId = "Invalid_SameLengthNotMatched", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), - TestId = "SameLengthNotMatched", ValidationParameters = new ValidationParameters(), AudiencesToAdd = [audience2], SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, "Issuer"), - AudienceValidationResult = new AudienceValidationResult( - commaAudience1, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII(commaAudience1), - LogHelper.MarkAsNonPII(audience2)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII(commaAudience1), + LogHelper.MarkAsNonPII(audience2)), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) }, new AudienceValidationTheoryData { Audiences = audiences1, + TestId = "Invalid_AudiencesValidAudienceWithSlashNotMatched", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), - TestId = "AudiencesValidAudienceWithSlashNotMatched", ValidationParameters = new ValidationParameters(), AudiencesToAdd = [audience2 + "/"], SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, "Issuer"), - AudienceValidationResult = new AudienceValidationResult( - commaAudience1, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII(commaAudience1), - LogHelper.MarkAsNonPII(audience2Slash)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII(commaAudience1), + LogHelper.MarkAsNonPII(audience2Slash)), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) }, new AudienceValidationTheoryData { Audiences = audiences2WithSlash, + TestId = "Invalid_AudiencesWithSlashValidAudienceSameLengthNotMatched", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), - TestId = "AudiencesWithSlashValidAudienceSameLengthNotMatched", ValidationParameters = new ValidationParameters(), AudiencesToAdd = [audience1], - AudienceValidationResult = new AudienceValidationResult( - commaAudience2Slash, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII(commaAudience2Slash), - LogHelper.MarkAsNonPII(audience1)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII(commaAudience2Slash), + LogHelper.MarkAsNonPII(audience1)), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) }, new AudienceValidationTheoryData { Audiences = audiences1, + TestId = "Invalid_ValidAudienceWithSlash_IgnoreTrailingSlashFalse", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), - TestId = "ValidAudienceWithSlashVPFalse", ValidationParameters = new ValidationParameters{ IgnoreTrailingSlashWhenValidatingAudience = false }, AudiencesToAdd = [audience1 + "/"], - AudienceValidationResult = new AudienceValidationResult( - commaAudience1, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII(commaAudience1), - LogHelper.MarkAsNonPII(audience1Slash)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII(commaAudience1), + LogHelper.MarkAsNonPII(audience1Slash)), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null) }, new AudienceValidationTheoryData { Audiences = audiences1, - TestId = "ValidAudienceWithSlashVPTrue", + TestId = "Valid_ValidAudienceWithSlash_IgnoreTrailingSlashTrue", ValidationParameters = new ValidationParameters(), AudiencesToAdd = [audience1 + "/"], - AudienceValidationResult = new AudienceValidationResult(audience1) + Result = audience1 }, new AudienceValidationTheoryData { Audiences = audiences1, + TestId = "Invalid_ValidAudiencesWithSlash_IgnoreTrailingSlashFalse", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), - TestId = "ValidAudiencesWithSlashVPFalse", ValidationParameters = new ValidationParameters{ IgnoreTrailingSlashWhenValidatingAudience = false }, AudiencesToAdd = audiences1WithSlash, - AudienceValidationResult = new AudienceValidationResult( - commaAudience1, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII(commaAudience1), - LogHelper.MarkAsNonPII(commaAudience1Slash)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII(commaAudience1), + LogHelper.MarkAsNonPII(commaAudience1Slash)), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) }, new AudienceValidationTheoryData { Audiences = audiences1, - TestId = "ValidAudiencesWithSlashVPTrue", + TestId = "Valid_ValidAudiencesWithSlash_IgnoreTrailingSlashTrue", ValidationParameters = new ValidationParameters(), AudiencesToAdd = audiences1WithSlash, - AudienceValidationResult = new AudienceValidationResult(audience1) + Result = audience1 }, new AudienceValidationTheoryData { Audiences = audiences1, + TestId = "Invalid_ValidAudienceWithExtraChar", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), - TestId = "ValidAudienceWithExtraChar", ValidationParameters = new ValidationParameters(), AudiencesToAdd = [audience1 + "A"], - AudienceValidationResult = new AudienceValidationResult( - commaAudience1, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII(commaAudience1), - LogHelper.MarkAsNonPII(audience1 + "A")), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII(commaAudience1), + LogHelper.MarkAsNonPII(audience1 + "A")), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) }, new AudienceValidationTheoryData { Audiences = audiences1, + TestId = "Invalid_ValidAudienceWithDoubleSlash_IgnoreTrailingSlashTrue", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), - TestId = "ValidAudienceWithDoubleSlashVPTrue", ValidationParameters = new ValidationParameters(), AudiencesToAdd = [audience1 + "//"], - AudienceValidationResult = new AudienceValidationResult( - commaAudience1, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII(commaAudience1), - LogHelper.MarkAsNonPII(audience1 + "//")), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII(commaAudience1), + LogHelper.MarkAsNonPII(audience1 + "//")), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) }, new AudienceValidationTheoryData { Audiences = audiences1, + TestId = "Invalid_ValidAudiencesWithDoubleSlash_IgnoreTrailingSlashTrue", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), - TestId = "ValidAudiencesWithDoubleSlashVPTrue", ValidationParameters = new ValidationParameters(), AudiencesToAdd = audiences1WithTwoSlashes, - AudienceValidationResult = new AudienceValidationResult( - commaAudience1, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII(commaAudience1), - LogHelper.MarkAsNonPII(commaAudience1 + "//")), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII(commaAudience1), + LogHelper.MarkAsNonPII(commaAudience1 + "//")), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) }, new AudienceValidationTheoryData { Audiences = audiences1WithSlash, + TestId = "Invalid_TokenAudienceWithSlash_IgnoreTrailingSlashFalse", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), - TestId = "TokenAudienceWithSlashVPFalse", ValidationParameters = new ValidationParameters{ IgnoreTrailingSlashWhenValidatingAudience = false }, AudiencesToAdd = [audience1], - AudienceValidationResult = new AudienceValidationResult( - commaAudience1Slash, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII(commaAudience1Slash), - LogHelper.MarkAsNonPII(audience1)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII(commaAudience1Slash), + LogHelper.MarkAsNonPII(audience1)), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) }, new AudienceValidationTheoryData { Audiences = audiences1WithSlash, - TestId = "TokenAudienceWithSlashVPTrue", + TestId = "Valid_TokenAudienceWithSlash_IgnoreTrailingSlashTrue", ValidationParameters = new ValidationParameters(), AudiencesToAdd = [audience1], - AudienceValidationResult = new AudienceValidationResult(audience1Slash) + Result = audience1Slash }, new AudienceValidationTheoryData { Audiences = audiences2WithSlash, + TestId = "Invalid_TokenAudienceWithSlashNotEqual", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), - TestId = "TokenAudienceWithSlashNotEqual", ValidationParameters = new ValidationParameters(), AudiencesToAdd = [audience1], - AudienceValidationResult = new AudienceValidationResult( - commaAudience2Slash, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII(commaAudience2Slash), - LogHelper.MarkAsNonPII(audience1)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII(commaAudience2Slash), + LogHelper.MarkAsNonPII(audience1)), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) }, new AudienceValidationTheoryData { Audiences = audiences1WithSlash, + TestId = "Invalid_TokenAudiencesWithSlash_IgnoreTrailingSlashFalse", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), - TestId = "TokenAudiencesWithSlashVPFalse", ValidationParameters = new ValidationParameters{ IgnoreTrailingSlashWhenValidatingAudience = false }, AudiencesToAdd = [audience1], - AudienceValidationResult = new AudienceValidationResult( - commaAudience1Slash, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII(commaAudience1Slash), - LogHelper.MarkAsNonPII(audience1)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII(commaAudience1Slash), + LogHelper.MarkAsNonPII(audience1)), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) }, new AudienceValidationTheoryData { Audiences = audiences1WithSlash, - TestId = "TokenAudiencesWithSlashVPTrue", + TestId = "Valid_TokenAudiencesWithSlash_IgnoreTrailingSlashTrue", ValidationParameters = new ValidationParameters(), AudiencesToAdd = [audience1], - AudienceValidationResult = new AudienceValidationResult(audience1Slash) + Result = audience1Slash }, new AudienceValidationTheoryData { Audiences = audiences1WithSlash, + TestId = "Invalid_TokenAudiencesWithSlashValidAudiencesNotMatched_IgnoreTrailingSlashTrue", ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), - TestId = "TokenAudiencesWithSlashValidAudiencesNotMatchedVPTrue", ValidationParameters = new ValidationParameters(), AudiencesToAdd = audiences2, - AudienceValidationResult = new AudienceValidationResult( - commaAudience1Slash, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII(commaAudience1Slash), - LogHelper.MarkAsNonPII(commaAudience2)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII(commaAudience1Slash), + LogHelper.MarkAsNonPII(commaAudience2)), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) }, new AudienceValidationTheoryData { Audiences = audiences1WithTwoSlashes, - ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), TestId = "TokenAudienceWithTwoSlashesVPTrue", + ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters(), AudiencesToAdd = [audience1], - AudienceValidationResult = new AudienceValidationResult( - commaAudience1 + "//", - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10215, - LogHelper.MarkAsNonPII(commaAudience1 + "//"), - LogHelper.MarkAsNonPII(audience1)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, - new StackFrame(true), - null)), + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10215, + LogHelper.MarkAsNonPII(commaAudience1 + "//"), + LogHelper.MarkAsNonPII(audience1)), + ValidationFailureType.AudienceValidationFailed, + ExceptionType.SecurityTokenInvalidAudience, + null, + null) } }; } @@ -491,14 +422,15 @@ public class AudienceValidationTheoryData : TheoryDataBase { public List Audiences { get; set; } - internal AudienceValidationResult AudienceValidationResult { get; set; } - public SecurityToken SecurityToken { get; set; } internal ValidationParameters ValidationParameters { get; set; } = new ValidationParameters(); internal ValidationFailureType ValidationFailureType { get; set; } + public List AudiencesToAdd { get; internal set; } + + internal Result Result { get; set; } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ExceptionDetailsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ExceptionDetailsTests.cs index 62b1bd2418..c47401adbc 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ExceptionDetailsTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ExceptionDetailsTests.cs @@ -17,8 +17,9 @@ public void ExceptionDetails(ExceptionDetailsTheoryData theoryData) var context = TestUtilities.WriteHeader($"{this}.ExceptionDetails", theoryData); ExceptionDetail exceptionDetail = new ExceptionDetail( new MessageDetail(""), + ValidationFailureType.NullArgument, theoryData.ExceptionType, - new System.Diagnostics.StackFrame()); + null); theoryData.ExpectedException.ProcessException(exceptionDetail.GetException(), context); @@ -30,8 +31,9 @@ public void ExceptionDetails_UnknownType_Throws() { ExceptionDetail exceptionDetail = new ExceptionDetail( new MessageDetail(""), - ExceptionDetail.ExceptionType.Unknown, - new System.Diagnostics.StackFrame()); + ValidationFailureType.NullArgument, + ExceptionType.Unknown, + null); Assert.Throws(() => exceptionDetail.GetException()); } @@ -39,8 +41,8 @@ public void ExceptionDetails_UnknownType_Throws() [Fact] public void All_ExceptionDetails_HaveTests() { - // If this test fails, we are missing a test for a new ExceptionDetail.ExceptionType - Assert.Equal(((int)ExceptionDetail.ExceptionType.ExceptionTypeCount), ExceptionDetailsTestCases.Count()); + // If this test fails, we are missing a test for a new ExceptionType + Assert.Equal(((int)ExceptionType.ExceptionTypeCount), ExceptionDetailsTestCases.Count()); } public static TheoryData ExceptionDetailsTestCases @@ -52,121 +54,127 @@ public static TheoryData ExceptionDetailsTestCases new ExceptionDetailsTheoryData { TestId = "ArgumentNull", - ExceptionType = ExceptionDetail.ExceptionType.ArgumentNull, + ExceptionType = ExceptionType.ArgumentNull, ExpectedException = ExpectedException.ArgumentNullException(), }, new ExceptionDetailsTheoryData + { + TestId = "InvalidArgument", + ExceptionType = ExceptionType.InvalidArgument, + ExpectedException = ExpectedException.ArgumentException(), + }, + new ExceptionDetailsTheoryData { TestId = "InvalidOperation", - ExceptionType = ExceptionDetail.ExceptionType.InvalidOperation, + ExceptionType = ExceptionType.InvalidOperation, ExpectedException = ExpectedException.InvalidOperationException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityToken", - ExceptionType = ExceptionDetail.ExceptionType.SecurityToken, + ExceptionType = ExceptionType.SecurityToken, ExpectedException = ExpectedException.SecurityTokenException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenDecompressionFailed", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenDecompressionFailed, + ExceptionType = ExceptionType.SecurityTokenDecompressionFailed, ExpectedException = ExpectedException.SecurityTokenDecompressionFailedException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenDecryptionFailed", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenDecryptionFailed, + ExceptionType = ExceptionType.SecurityTokenDecryptionFailed, ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenExpired", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenExpired, + ExceptionType = ExceptionType.SecurityTokenExpired, ExpectedException = ExpectedException.SecurityTokenExpiredException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenInvalidAudience", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenInvalidAudience, + ExceptionType = ExceptionType.SecurityTokenInvalidAudience, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenInvalidAlgorithm", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenInvalidAlgorithm, + ExceptionType = ExceptionType.SecurityTokenInvalidAlgorithm, ExpectedException = ExpectedException.SecurityTokenInvalidAlgorithmException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenInvalidIssuer", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenInvalidIssuer, + ExceptionType = ExceptionType.SecurityTokenInvalidIssuer, ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenInvalidLifetime", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenInvalidLifetime, + ExceptionType = ExceptionType.SecurityTokenInvalidLifetime, ExpectedException = ExpectedException.SecurityTokenInvalidLifetimeException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenInvalidSigningKey", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenInvalidSigningKey, + ExceptionType = ExceptionType.SecurityTokenInvalidSigningKey, ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenInvalidSignature", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenInvalidSignature, + ExceptionType = ExceptionType.SecurityTokenInvalidSignature, ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenInvalidType", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenInvalidType, + ExceptionType = ExceptionType.SecurityTokenInvalidType, ExpectedException = ExpectedException.SecurityTokenInvalidTypeException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenKeyWrap", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenKeyWrap, + ExceptionType = ExceptionType.SecurityTokenKeyWrap, ExpectedException = ExpectedException.SecurityTokenKeyWrapException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenMalformed", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenMalformed, + ExceptionType = ExceptionType.SecurityTokenMalformed, ExpectedException = ExpectedException.SecurityTokenMalformedException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenNoExpiration", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenNoExpiration, + ExceptionType = ExceptionType.SecurityTokenNoExpiration, ExpectedException = ExpectedException.SecurityTokenNoExpirationException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenNotYetValid", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenNotYetValid, + ExceptionType = ExceptionType.SecurityTokenNotYetValid, ExpectedException = ExpectedException.SecurityTokenNotYetValidException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenReplayDetected", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenReplayDetected, + ExceptionType = ExceptionType.SecurityTokenReplayDetected, ExpectedException = ExpectedException.SecurityTokenReplayDetectedException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenReplayAddFailed", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenReplayAddFailed, + ExceptionType = ExceptionType.SecurityTokenReplayAddFailed, ExpectedException = ExpectedException.SecurityTokenReplayAddFailedException(), }, new ExceptionDetailsTheoryData { TestId = "SecurityTokenSignatureKeyNotFound", - ExceptionType = ExceptionDetail.ExceptionType.SecurityTokenSignatureKeyNotFound, + ExceptionType = ExceptionType.SecurityTokenSignatureKeyNotFound, ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException(), }, }; @@ -176,6 +184,6 @@ public static TheoryData ExceptionDetailsTestCases public class ExceptionDetailsTheoryData : TheoryDataBase { - internal ExceptionDetail.ExceptionType ExceptionType { get; set; } + internal ExceptionType ExceptionType { get; set; } } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/IssuerValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/IssuerValidationResultTests.cs index 75f96dddc1..d4157e12a0 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/IssuerValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/IssuerValidationResultTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.IdentityModel.JsonWebTokens; @@ -24,22 +23,33 @@ public async Task IssuerValidatorAsyncTests(IssuerValidationResultsTheoryData th if (theoryData.ValidIssuerToAdd != null) theoryData.ValidationParameters.ValidIssuers.Add(theoryData.ValidIssuerToAdd); - IssuerValidationResult issuerValidationResult = await Validators.ValidateIssuerAsync( + Result result = await Validators.ValidateIssuerAsync( theoryData.Issuer, theoryData.SecurityToken, theoryData.ValidationParameters, new CallContext(), CancellationToken.None); - if (issuerValidationResult.Exception != null) - theoryData.ExpectedException.ProcessException(issuerValidationResult.Exception, context); - else - theoryData.ExpectedException.ProcessNoException(); + if (result.IsSuccess) + { + IdentityComparer.AreValidatedIssuersEqual( + theoryData.Result.UnwrapResult(), + result.UnwrapResult(), + context); - IdentityComparer.AreIssuerValidationResultsEqual( - issuerValidationResult, - theoryData.IssuerValidationResult, - context); + theoryData.ExpectedException.ProcessNoException(context); + } + else + { + ExceptionDetail exceptionDetail = result.UnwrapError(); + IdentityComparer.AreStringsEqual( + exceptionDetail.FailureType.Name, + theoryData.Result.UnwrapError().FailureType.Name, + context); + + Exception exception = exceptionDetail.GetException(); + theoryData.ExpectedException.ProcessException(exception, context); + } TestUtilities.AssertFailIfErrors(context); } @@ -58,19 +68,17 @@ public static TheoryData IssuerValdationResul theoryData.Add(new IssuerValidationResultsTheoryData("NULL_Issuer") { ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10211:"), - IssuerValidationResult = new IssuerValidationResult( + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10211, + LogHelper.MarkAsNonPII(null), + LogHelper.MarkAsNonPII(validIssuer), + LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(null)), + LogHelper.MarkAsNonPII(null)), + ValidationFailureType.IssuerValidationFailed, + ExceptionType.SecurityTokenInvalidIssuer, null, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10211, - LogHelper.MarkAsNonPII(null), - LogHelper.MarkAsNonPII(validIssuer), - LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(null)), - LogHelper.MarkAsNonPII(null)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidIssuer, - new StackFrame(true))), - IsValid = false, + null), SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim), ValidationParameters = new ValidationParameters() }); @@ -79,17 +87,14 @@ public static TheoryData IssuerValdationResul { ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), Issuer = issClaim, - IssuerValidationResult = new IssuerValidationResult( - issClaim, - ValidationFailureType.NullArgument, - new ExceptionDetail( + Result = new ExceptionDetail( new MessageDetail( LogMessages.IDX10000, LogHelper.MarkAsNonPII("validationParameters")), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true), - null)), - IsValid = false, + ValidationFailureType.NullArgument, + ExceptionType.ArgumentNull, + null, + null), SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim), ValidationParameters = null }); @@ -98,29 +103,22 @@ public static TheoryData IssuerValdationResul { ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), Issuer = issClaim, - IssuerValidationResult = new IssuerValidationResult( - issClaim, - ValidationFailureType.NullArgument, - new ExceptionDetail( + Result = new ExceptionDetail( new MessageDetail( LogMessages.IDX10000, LogHelper.MarkAsNonPII("securityToken")), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true), - null)), - IsValid = false, + ValidationFailureType.NullArgument, + ExceptionType.ArgumentNull, + null, + null), SecurityToken = null, ValidationParameters = new ValidationParameters() }); theoryData.Add(new IssuerValidationResultsTheoryData("Valid_FromConfig") { - ExpectedException = ExpectedException.NoExceptionExpected, Issuer = issClaim, - IssuerValidationResult = new IssuerValidationResult( - issClaim, - IssuerValidationResult.ValidationSource.IssuerIsConfigurationIssuer), - IsValid = true, + Result = new ValidatedIssuer(issClaim, IssuerValidationSource.IssuerIsConfigurationIssuer), SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim), ValidationParameters = new ValidationParameters() { @@ -130,12 +128,8 @@ public static TheoryData IssuerValdationResul theoryData.Add(new IssuerValidationResultsTheoryData("Valid_FromValidationParametersValidIssuers") { - ExpectedException = ExpectedException.NoExceptionExpected, Issuer = issClaim, - IssuerValidationResult = new IssuerValidationResult( - issClaim, - IssuerValidationResult.ValidationSource.IssuerIsAmongValidIssuers), - IsValid = true, + Result = new ValidatedIssuer(issClaim, IssuerValidationSource.IssuerIsAmongValidIssuers), SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim), ValidationParameters = new ValidationParameters(), ValidIssuerToAdd = issClaim @@ -145,18 +139,16 @@ public static TheoryData IssuerValdationResul { ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10212:"), Issuer = issClaim, - IssuerValidationResult = new IssuerValidationResult( - issClaim, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10212, + LogHelper.MarkAsNonPII(issClaim), + LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validIssuers)), + LogHelper.MarkAsNonPII(null)), ValidationFailureType.IssuerValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10212, - LogHelper.MarkAsNonPII(issClaim), - LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validIssuers)), - LogHelper.MarkAsNonPII(null)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidIssuer, - new StackFrame(true))), - IsValid = false, + ExceptionType.SecurityTokenInvalidIssuer, + null, + null), SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim), ValidationParameters = new ValidationParameters(), ValidIssuerToAdd = validIssuer @@ -177,9 +169,7 @@ public IssuerValidationResultsTheoryData(string testId) : base(testId) public string Issuer { get; set; } - internal IssuerValidationResult IssuerValidationResult { get; set; } - - public bool IsValid { get; set; } + internal Result Result { get; set; } public SecurityToken SecurityToken { get; set; } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/LifetimeValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/LifetimeValidationResultTests.cs index 03eb7a9f6b..386c58182d 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/LifetimeValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/LifetimeValidationResultTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.TestUtils; using Xunit; @@ -16,22 +15,33 @@ public void ValidateLifetime(ValidateLifetimeTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.LifetimeValidatorTests", theoryData); - LifetimeValidationResult lifetimeValidationResult = Validators.ValidateLifetime( + Result result = Validators.ValidateLifetime( theoryData.NotBefore, theoryData.Expires, theoryData.SecurityToken, theoryData.ValidationParameters, new CallContext()); - if (lifetimeValidationResult.Exception == null) + if (result.IsSuccess) + { + IdentityComparer.AreValidatedLifetimesEqual( + theoryData.Result.UnwrapResult(), + result.UnwrapResult(), + context); + theoryData.ExpectedException.ProcessNoException(); + } else - theoryData.ExpectedException.ProcessException(lifetimeValidationResult.Exception, context); + { + ExceptionDetail exceptionDetail = result.UnwrapError(); + IdentityComparer.AreStringsEqual( + exceptionDetail.FailureType.Name, + theoryData.Result.UnwrapError().FailureType.Name, + context); - IdentityComparer.AreLifetimeValidationResultsEqual( - lifetimeValidationResult, - theoryData.LifetimeValidationResult, - context); + Exception exception = exceptionDetail.GetException(); + theoryData.ExpectedException.ProcessException(exception, context); + } TestUtilities.AssertFailIfErrors(context); } @@ -57,14 +67,14 @@ public static TheoryData ValidateLifetimeTestCases { Expires = oneHourFromNow, NotBefore = oneHourAgo, - LifetimeValidationResult = new LifetimeValidationResult(oneHourAgo, oneHourFromNow), + Result = new ValidatedLifetime(oneHourAgo, oneHourFromNow), ValidationParameters = new ValidationParameters() }, new ValidateLifetimeTheoryData("Valid_NotBeforeIsNull") { Expires = oneHourFromNow, NotBefore = null, - LifetimeValidationResult = new LifetimeValidationResult(null, oneHourFromNow), + Result = new ValidatedLifetime(null, oneHourFromNow), ValidationParameters = new ValidationParameters() }, new ValidateLifetimeTheoryData("Valid_SkewForward") @@ -72,144 +82,112 @@ public static TheoryData ValidateLifetimeTestCases Expires = oneHourFromNow, NotBefore = twoMinutesFromNow, ValidationParameters = new ValidationParameters { ClockSkew = TimeSpan.FromMinutes(5) }, - LifetimeValidationResult = new LifetimeValidationResult(twoMinutesFromNow, oneHourFromNow), + Result = new ValidatedLifetime(twoMinutesFromNow, oneHourFromNow), }, new ValidateLifetimeTheoryData("Valid_SkewBackward") { Expires = oneMinuteAgo, NotBefore = twoMinutesAgo, ValidationParameters = new ValidationParameters { ClockSkew = TimeSpan.FromMinutes(5) }, - LifetimeValidationResult = new LifetimeValidationResult(twoMinutesAgo, oneMinuteAgo), + Result = new ValidatedLifetime(twoMinutesAgo, oneMinuteAgo), }, new ValidateLifetimeTheoryData("Invalid_ValidationParametersIsNull") { + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), Expires = oneHourFromNow, NotBefore = oneHourAgo, ValidationParameters = null, - ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - LifetimeValidationResult = new LifetimeValidationResult( - oneHourAgo, - oneHourFromNow, + Result = new ExceptionDetail( + new MessageDetail(LogMessages.IDX10000, "validationParameters"), ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - "validationParameters"), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true), - null)), + ExceptionType.ArgumentNull, + null), }, new ValidateLifetimeTheoryData("Invalid_ExpiresIsNull") { + ExpectedException = ExpectedException.SecurityTokenNoExpirationException("IDX10225:"), NotBefore = oneHourAgo, ValidationParameters = new ValidationParameters(), - ExpectedException = ExpectedException.SecurityTokenNoExpirationException("IDX10225:"), - LifetimeValidationResult = new LifetimeValidationResult( - oneHourAgo, - null, + Result = new ExceptionDetail( + new MessageDetail(LogMessages.IDX10225, "null"), ValidationFailureType.LifetimeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10225, - "null"), - ExceptionDetail.ExceptionType.SecurityTokenNoExpiration, - new StackFrame(true), - null)), + ExceptionType.SecurityTokenNoExpiration, + null), }, new ValidateLifetimeTheoryData("Invalid_NotBeforeIsAfterExpires") { + ExpectedException = ExpectedException.SecurityTokenInvalidLifetimeException("IDX10224:"), Expires = oneHourAgo, NotBefore = oneHourFromNow, ValidationParameters = new ValidationParameters(), - ExpectedException = ExpectedException.SecurityTokenInvalidLifetimeException("IDX10224:"), - LifetimeValidationResult = new LifetimeValidationResult( - oneHourFromNow, // notBefore - oneHourAgo, // expires + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10224, + LogHelper.MarkAsNonPII(oneHourFromNow), + LogHelper.MarkAsNonPII(oneHourAgo)), ValidationFailureType.LifetimeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10224, - LogHelper.MarkAsNonPII(oneHourFromNow), - LogHelper.MarkAsNonPII(oneHourAgo)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidLifetime, - new StackFrame(true), - null)), + ExceptionType.SecurityTokenInvalidLifetime, + null), }, new ValidateLifetimeTheoryData("Invalid_NotYetValid") { + ExpectedException = ExpectedException.SecurityTokenNotYetValidException("IDX10222:"), Expires = twoHoursFromNow, NotBefore = oneHourFromNow, ValidationParameters = new ValidationParameters(), - ExpectedException = ExpectedException.SecurityTokenNotYetValidException("IDX10222:"), - LifetimeValidationResult = new LifetimeValidationResult( - oneHourFromNow, - twoHoursFromNow, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10222, + LogHelper.MarkAsNonPII(oneHourFromNow), + LogHelper.MarkAsNonPII(now)), ValidationFailureType.LifetimeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10222, - LogHelper.MarkAsNonPII(oneHourFromNow), - LogHelper.MarkAsNonPII(now)), - ExceptionDetail.ExceptionType.SecurityTokenNotYetValid, - new StackFrame(true), - null)), + ExceptionType.SecurityTokenNotYetValid, + null), }, new ValidateLifetimeTheoryData("Invalid_Expired") { + ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:"), Expires = oneHourAgo, NotBefore = twoHoursAgo, ValidationParameters = new ValidationParameters(), - ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:"), - LifetimeValidationResult = new LifetimeValidationResult( - twoHoursAgo, - oneHourAgo, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10223, + LogHelper.MarkAsNonPII(oneHourAgo), + LogHelper.MarkAsNonPII(now)), ValidationFailureType.LifetimeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10223, - LogHelper.MarkAsNonPII(oneHourAgo), - LogHelper.MarkAsNonPII(now)), - ExceptionDetail.ExceptionType.SecurityTokenExpired, - new StackFrame(true), - null)), + ExceptionType.SecurityTokenExpired, + null), }, new ValidateLifetimeTheoryData("Invalid_NotYetValid_SkewForward") { + ExpectedException = ExpectedException.SecurityTokenNotYetValidException("IDX10222:"), Expires = oneHourFromNow, NotBefore = sixMinutesFromNow, ValidationParameters = new ValidationParameters { ClockSkew = TimeSpan.FromMinutes(5) }, - ExpectedException = ExpectedException.SecurityTokenNotYetValidException("IDX10222:"), - LifetimeValidationResult = new LifetimeValidationResult( - sixMinutesFromNow, - oneHourFromNow, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10222, + LogHelper.MarkAsNonPII(sixMinutesFromNow), + LogHelper.MarkAsNonPII(now)), ValidationFailureType.LifetimeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10222, - LogHelper.MarkAsNonPII(sixMinutesFromNow), - LogHelper.MarkAsNonPII(now)), - ExceptionDetail.ExceptionType.SecurityTokenNotYetValid, - new StackFrame(true), - null)), + ExceptionType.SecurityTokenNotYetValid, + null), }, new ValidateLifetimeTheoryData("Invalid_Expired_SkewBackward") { + ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:"), Expires = sixMinutesAgo, NotBefore = twoHoursAgo, ValidationParameters = new ValidationParameters { ClockSkew = TimeSpan.FromMinutes(5) }, - ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:"), - LifetimeValidationResult = new LifetimeValidationResult( - twoHoursAgo, - sixMinutesAgo, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10223, + LogHelper.MarkAsNonPII(sixMinutesAgo), + LogHelper.MarkAsNonPII(now)), ValidationFailureType.LifetimeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10223, - LogHelper.MarkAsNonPII(sixMinutesAgo), - LogHelper.MarkAsNonPII(now)), - ExceptionDetail.ExceptionType.SecurityTokenExpired, - new StackFrame(true), - null)), + ExceptionType.SecurityTokenExpired, + null), } }; } @@ -230,7 +208,7 @@ public ValidateLifetimeTheoryData(string testId) : base(testId) internal ValidationParameters ValidationParameters { get; set; } - internal LifetimeValidationResult LifetimeValidationResult { get; set; } + internal Result Result { get; set; } internal ValidationFailureType ValidationFailureType { get; set; } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ReplayValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ReplayValidationResultTests.cs index 5d6124452f..3e3dcfe78b 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ReplayValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ReplayValidationResultTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.TestUtils; using Xunit; @@ -16,24 +15,35 @@ public void ValidateTokenReplay(TokenReplayTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.TokenReplayValidationResultTests", theoryData); - ReplayValidationResult replayValidationResult = Validators.ValidateTokenReplay( + Result result = Validators.ValidateTokenReplay( theoryData.ExpirationTime, theoryData.SecurityToken, theoryData.ValidationParameters, new CallContext()); - if (replayValidationResult.Exception != null) - theoryData.ExpectedException.ProcessException(replayValidationResult.Exception); - else + if (result.IsSuccess) + { + IdentityComparer.AreDateTimesEqualWithEpsilon( + result.UnwrapResult(), + theoryData.Result.UnwrapResult(), + 1, + context); + theoryData.ExpectedException.ProcessNoException(); + } + else + { + ExceptionDetail exceptionDetail = result.UnwrapError(); + IdentityComparer.AreStringsEqual( + exceptionDetail.FailureType.Name, + theoryData.Result.UnwrapError().FailureType.Name, + context); - IdentityComparer.AreTokenReplayValidationResultsEqual( - replayValidationResult, - theoryData.ReplayValidationResult, - context); + Exception exception = exceptionDetail.GetException(); + theoryData.ExpectedException.ProcessException(exception, context); + } TestUtilities.AssertFailIfErrors(context); - } public static TheoryData TokenReplayValidationTestCases @@ -55,7 +65,7 @@ public static TheoryData TokenReplayValidationTestCases { TokenReplayCache = null }, - ReplayValidationResult = new ReplayValidationResult(oneHourAgo) + Result = oneHourAgo, }, new TokenReplayTheoryData { @@ -66,65 +76,60 @@ public static TheoryData TokenReplayValidationTestCases { TokenReplayCache = new TokenReplayCache { OnAddReturnValue = true, OnFindReturnValue = false }, }, - ReplayValidationResult = new ReplayValidationResult(oneHourFromNow) + Result = oneHourFromNow, }, new TokenReplayTheoryData { TestId = "Invalid_SecurityToken_Null", + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), ExpirationTime = now, SecurityToken = null, ValidationParameters = new ValidationParameters(), - ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - ReplayValidationResult = new ReplayValidationResult( - now, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("securityToken")), ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII("securityToken")), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(), - null)) + ExceptionType.ArgumentNull, + null, + null), }, new TokenReplayTheoryData { TestId = "Invalid_SecurityToken_Empty", + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), ExpirationTime = now, SecurityToken = string.Empty, ValidationParameters = new ValidationParameters(), - ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - ReplayValidationResult = new ReplayValidationResult( - now, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("securityToken")), ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII("securityToken")), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(), - null)) + ExceptionType.ArgumentNull, + null, + null), }, new TokenReplayTheoryData { TestId = "Invalid_ValidationParameters_Null", + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), ExpirationTime = now, SecurityToken = "token", ValidationParameters = null, - ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - ReplayValidationResult = new ReplayValidationResult( - now, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("validationParameters")), ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII("validationParameters")), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(), - null)) + ExceptionType.ArgumentNull, + null, + null), }, new TokenReplayTheoryData { TestId = "Invalid_ReplayCacheIsPresent_ExpirationTimeIsNull", + ExpectedException = ExpectedException.SecurityTokenReplayDetected("IDX10227:"), ExpirationTime = null, SecurityToken = "token", ValidationParameters = new ValidationParameters @@ -135,21 +140,19 @@ public static TheoryData TokenReplayValidationTestCases OnFindReturnValue = false } }, - ExpectedException = ExpectedException.SecurityTokenReplayDetected("IDX10227:"), - ReplayValidationResult = new ReplayValidationResult( - null, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10227, + LogHelper.MarkAsUnsafeSecurityArtifact("token", t => t.ToString())), ValidationFailureType.TokenReplayValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10227, - LogHelper.MarkAsUnsafeSecurityArtifact("token", t => t.ToString())), - ExceptionDetail.ExceptionType.SecurityTokenReplayDetected, - new StackFrame(), - null)) + ExceptionType.SecurityTokenReplayDetected, + null, + null), }, new TokenReplayTheoryData { TestId = "Invalid_ReplayCacheIsPresent_TokenIsAlreadyInCache", + ExpectedException = ExpectedException.SecurityTokenReplayDetected("IDX10228:"), ExpirationTime = oneHourFromNow, SecurityToken= "token", ValidationParameters = new ValidationParameters @@ -160,17 +163,14 @@ public static TheoryData TokenReplayValidationTestCases OnFindReturnValue = true }, }, - ExpectedException = ExpectedException.SecurityTokenReplayDetected("IDX10228:"), - ReplayValidationResult = new ReplayValidationResult( - oneHourFromNow, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10228, + LogHelper.MarkAsUnsafeSecurityArtifact("token", t => t.ToString())), ValidationFailureType.TokenReplayValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10228, - LogHelper.MarkAsUnsafeSecurityArtifact("token", t => t.ToString())), - ExceptionDetail.ExceptionType.SecurityTokenReplayDetected, - new StackFrame(), - null)) + ExceptionType.SecurityTokenReplayDetected, + null, + null), }, new TokenReplayTheoryData { @@ -186,16 +186,14 @@ public static TheoryData TokenReplayValidationTestCases } }, ExpectedException = ExpectedException.SecurityTokenReplayAddFailed("IDX10229:"), - ReplayValidationResult = new ReplayValidationResult( - oneHourFromNow, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10229, + LogHelper.MarkAsUnsafeSecurityArtifact("token", t => t.ToString())), ValidationFailureType.TokenReplayValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10229, - LogHelper.MarkAsUnsafeSecurityArtifact("token", t => t.ToString())), - ExceptionDetail.ExceptionType.SecurityTokenReplayDetected, - new StackFrame(), - null)) + ExceptionType.SecurityTokenReplayDetected, + null, + null), } }; } @@ -210,6 +208,6 @@ public class TokenReplayTheoryData : TheoryDataBase internal ValidationParameters ValidationParameters { get; set; } - internal ReplayValidationResult ReplayValidationResult { get; set; } + internal Result Result { get; set; } } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs index 1a2f055d3d..b93b6daf74 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using System.IdentityModel.Tokens.Jwt; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.TestUtils; @@ -17,22 +16,33 @@ public void SecurityKey(SigningKeyValidationTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.SigningKeyValidationResultTests", theoryData); - SigningKeyValidationResult signingKeyValidationResult = Validators.ValidateIssuerSigningKey( + Result result = Validators.ValidateIssuerSigningKey( theoryData.SecurityKey, theoryData.SecurityToken, theoryData.ValidationParameters, theoryData.BaseConfiguration, new CallContext()); - if (signingKeyValidationResult.Exception != null) - theoryData.ExpectedException.ProcessException(signingKeyValidationResult.Exception); - else + if (result.IsSuccess) + { + IdentityComparer.AreValidatedSigningKeyLifetimesEqual( + theoryData.Result.UnwrapResult(), + result.UnwrapResult(), + context); + theoryData.ExpectedException.ProcessNoException(); + } + else + { + ExceptionDetail exceptionDetail = result.UnwrapError(); + IdentityComparer.AreStringsEqual( + exceptionDetail.FailureType.Name, + theoryData.Result.UnwrapError().FailureType.Name, + context); - IdentityComparer.AreSigningKeyValidationResultsEqual( - signingKeyValidationResult, - theoryData.SigningKeyValidationResult, - context); + Exception exception = exceptionDetail.GetException(); + theoryData.ExpectedException.ProcessException(exception, context); + } TestUtilities.AssertFailIfErrors(context); } @@ -50,11 +60,10 @@ public static TheoryData SigningKeyValidationTes new SigningKeyValidationTheoryData { TestId = "Valid_SecurityTokenIsPresent", - ExpectedException = ExpectedException.NoExceptionExpected, SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, SecurityToken = new JwtSecurityToken(), ValidationParameters = new ValidationParameters(), - SigningKeyValidationResult = new SigningKeyValidationResult(KeyingMaterial.SymmetricSecurityKey2_256) + Result = new ValidatedSigningKeyLifetime(null, null, utcNow) }, new SigningKeyValidationTheoryData { @@ -63,47 +72,44 @@ public static TheoryData SigningKeyValidationTes SecurityKey = null, SecurityToken = new JwtSecurityToken(), ValidationParameters = new ValidationParameters(), - SigningKeyValidationResult = new SigningKeyValidationResult( - null, // SecurityKey - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail(LogMessages.IDX10253), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))) + Result = new ExceptionDetail( + new MessageDetail(LogMessages.IDX10253), + ValidationFailureType.SigningKeyValidationFailed, + ExceptionType.ArgumentNull, + null, + null), }, new SigningKeyValidationTheoryData { TestId = "Invalid_SecurityTokenIsNull", - ExpectedException = ExpectedException.ArgumentNullException(), + ExpectedException = ExpectedException.ArgumentNullException(substringExpected: "IDX10000:"), SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, SecurityToken = null, ValidationParameters = new ValidationParameters (), - SigningKeyValidationResult = new SigningKeyValidationResult( - KeyingMaterial.SymmetricSecurityKey2_256, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("securityToken")), ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII("securityToken")), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))) + ExceptionType.ArgumentNull, + null, + null), }, new SigningKeyValidationTheoryData { TestId = "Invalid_ValidationParametersIsNull", - ExpectedException = ExpectedException.ArgumentNullException(), + ExpectedException = ExpectedException.ArgumentNullException(substringExpected: "IDX10000:"), SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, SecurityToken = new JwtSecurityToken(), ValidationParameters = null, - SigningKeyValidationResult = new SigningKeyValidationResult( - KeyingMaterial.SymmetricSecurityKey2_256, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("validationParameters")), ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII("validationParameters")), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))) + ExceptionType.ArgumentNull, + null, + null), }, new SigningKeyValidationTheoryData { @@ -112,16 +118,15 @@ public static TheoryData SigningKeyValidationTes SecurityKey = KeyingMaterial.ExpiredX509SecurityKey_Public, SecurityToken = new JwtSecurityToken(), ValidationParameters = new ValidationParameters (), - SigningKeyValidationResult = new SigningKeyValidationResult( - KeyingMaterial.ExpiredX509SecurityKey_Public, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10249, + LogHelper.MarkAsNonPII(utcExpired), + LogHelper.MarkAsNonPII(utcNow)), ValidationFailureType.SigningKeyValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10249, - LogHelper.MarkAsNonPII(utcExpired), - LogHelper.MarkAsNonPII(utcNow)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidSigningKey, - new StackFrame(true))) + ExceptionType.SecurityTokenInvalidSigningKey, + null, + null), }, new SigningKeyValidationTheoryData { @@ -130,31 +135,29 @@ public static TheoryData SigningKeyValidationTes SecurityKey = KeyingMaterial.NotYetValidX509SecurityKey_Public, SecurityToken = new JwtSecurityToken(), ValidationParameters = new ValidationParameters (), - SigningKeyValidationResult = new SigningKeyValidationResult( - KeyingMaterial.NotYetValidX509SecurityKey_Public, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10248, + LogHelper.MarkAsNonPII(utcNotYetValid), + LogHelper.MarkAsNonPII(utcNow)), ValidationFailureType.SigningKeyValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10248, - LogHelper.MarkAsNonPII(utcNotYetValid), - LogHelper.MarkAsNonPII(utcNow)), - ExceptionDetail.ExceptionType.SecurityTokenInvalidSigningKey, - new StackFrame(true))) + ExceptionType.SecurityTokenInvalidSigningKey, + null, + null), }, new SigningKeyValidationTheoryData { TestId = "Invalid_SecurityKeyIsNull", - ExpectedException = ExpectedException.ArgumentNullException(substringExpected: "IDX10253:"), + ExpectedException = ExpectedException.ArgumentNullException("IDX10253:"), SecurityKey = null, SecurityToken = new JwtSecurityToken(), ValidationParameters = new ValidationParameters (), - SigningKeyValidationResult = new SigningKeyValidationResult( + Result = new ExceptionDetail( + new MessageDetail(LogMessages.IDX10253), + ValidationFailureType.SigningKeyValidationFailed, + ExceptionType.ArgumentNull, null, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail(LogMessages.IDX10253), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))) + null), }, }; @@ -168,6 +171,6 @@ public class SigningKeyValidationTheoryData : TheoryDataBase public SecurityToken SecurityToken { get; set; } internal ValidationParameters ValidationParameters { get; set; } public BaseConfiguration BaseConfiguration { get; set; } - internal SigningKeyValidationResult SigningKeyValidationResult { get; set; } + internal Result Result { get; set; } } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/TokenTypeValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/TokenTypeValidationResultTests.cs index 11548f61ad..10d59be7d4 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/TokenTypeValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/TokenTypeValidationResultTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using Microsoft.IdentityModel.TestUtils; using Microsoft.IdentityModel.Tokens.Json.Tests; using Microsoft.IdentityModel.JsonWebTokens; @@ -25,21 +24,32 @@ public void ValidateTokenType(TokenTypeTheoryData theoryData) theoryData.ValidationParameters.ValidTypes.Add(tokenType); } - TokenTypeValidationResult tokenTypeValidationResult = Validators.ValidateTokenType( + Result result = Validators.ValidateTokenType( theoryData.Type, theoryData.SecurityToken, theoryData.ValidationParameters, new CallContext()); - if (tokenTypeValidationResult.Exception != null) - theoryData.ExpectedException.ProcessException(tokenTypeValidationResult.Exception); - else + if (result.IsSuccess) + { + IdentityComparer.AreValidatedTokenTypesEqual( + result.UnwrapResult(), + theoryData.Result.UnwrapResult(), + context); + theoryData.ExpectedException.ProcessNoException(); + } + else + { + ExceptionDetail exceptionDetail = result.UnwrapError(); + IdentityComparer.AreStringsEqual( + exceptionDetail.FailureType.Name, + theoryData.Result.UnwrapError().FailureType.Name, + context); - IdentityComparer.AreTokenTypeValidationResultsEqual( - tokenTypeValidationResult, - theoryData.TokenTypeValidationResult, - context); + Exception exception = exceptionDetail.GetException(); + theoryData.ExpectedException.ProcessException(exception, context); + } TestUtilities.AssertFailIfErrors(context); @@ -61,7 +71,7 @@ public static TheoryData TokenTypeValidationTestCases SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, "JWT"), ValidationParameters = new ValidationParameters(), TokenTypesToAdd = validTypesWithJwt, - TokenTypeValidationResult = new TokenTypeValidationResult("JWT") + Result = new ValidatedTokenType("JWT", 4) }, new TokenTypeTheoryData { @@ -70,15 +80,14 @@ public static TheoryData TokenTypeValidationTestCases Type = "JWT", SecurityToken = null, ValidationParameters = null, - TokenTypeValidationResult = new TokenTypeValidationResult( - "JWT", + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("securityToken")), ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII("securityToken")), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))) + ExceptionType.ArgumentNull, + null, + null) }, new TokenTypeTheoryData { @@ -87,15 +96,14 @@ public static TheoryData TokenTypeValidationTestCases Type = "JWT", SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, "JWT"), ValidationParameters = null, - TokenTypeValidationResult = new TokenTypeValidationResult( - "JWT", + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("validationParameters")), ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII("validationParameters")), - ExceptionDetail.ExceptionType.ArgumentNull, - new StackFrame(true))) + ExceptionType.ArgumentNull, + null, + null) }, new TokenTypeTheoryData { @@ -105,15 +113,14 @@ public static TheoryData TokenTypeValidationTestCases SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, String.Empty), ValidationParameters = new ValidationParameters(), TokenTypesToAdd = validTypesNoJwt, - TokenTypeValidationResult = new TokenTypeValidationResult( - string.Empty, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10256, + LogHelper.MarkAsNonPII("type")), ValidationFailureType.TokenTypeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10256, - LogHelper.MarkAsNonPII("type")), - ExceptionDetail.ExceptionType.SecurityTokenInvalidType, - new StackFrame(true))) + ExceptionType.SecurityTokenInvalidType, + null, + null) }, new TokenTypeTheoryData { @@ -123,15 +130,14 @@ public static TheoryData TokenTypeValidationTestCases SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, null), ValidationParameters = new ValidationParameters(), TokenTypesToAdd = validTypesNoJwt, - TokenTypeValidationResult = new TokenTypeValidationResult( - null, + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10256, + LogHelper.MarkAsNonPII("type")), ValidationFailureType.TokenTypeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10256, - LogHelper.MarkAsNonPII("type")), - ExceptionDetail.ExceptionType.SecurityTokenInvalidType, - new StackFrame(true))) + ExceptionType.SecurityTokenInvalidType, + null, + null) }, new TokenTypeTheoryData { @@ -141,16 +147,15 @@ public static TheoryData TokenTypeValidationTestCases SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, "JWT"), ValidationParameters = new ValidationParameters(), TokenTypesToAdd = validTypesNoJwt, - TokenTypeValidationResult = new TokenTypeValidationResult( - "JWT", + Result = new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10257, + LogHelper.MarkAsNonPII("type"), + LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validTypesNoJwt))), ValidationFailureType.TokenTypeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10257, - LogHelper.MarkAsNonPII("type"), - LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validTypesNoJwt))), - ExceptionDetail.ExceptionType.SecurityTokenInvalidType, - new StackFrame(true))) + ExceptionType.SecurityTokenInvalidType, + null, + null) } }; } @@ -163,8 +168,7 @@ public class TokenTypeTheoryData : TheoryDataBase public SecurityToken SecurityToken { get; set; } public IList TokenTypesToAdd { get; internal set; } internal ValidationParameters ValidationParameters { get; set; } - - internal TokenTypeValidationResult TokenTypeValidationResult { get; set; } + internal Result Result { get; set; } } } }