diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/Experimental/JsonWebTokenHandler.ValidateSignature.cs b/src/Microsoft.IdentityModel.JsonWebTokens/Experimental/JsonWebTokenHandler.ValidateSignature.cs index 57c577230f..f455b0adbe 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/Experimental/JsonWebTokenHandler.ValidateSignature.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/Experimental/JsonWebTokenHandler.ValidateSignature.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; @@ -139,10 +140,14 @@ private static ValidationResult ValidateSignatureU StringBuilder? exceptionStrings = null; StringBuilder? keysAttempted = null; + // We want to capture all stack frames that were involved with faults. + // We capture the stack frames and add to the error. + IList? stackFrames = null; + foreach (SecurityKey key in keys) { if (key is null) - continue; // skip null keys + continue; keysTried = true; @@ -154,10 +159,17 @@ private static ValidationResult ValidateSignatureU callContext); if (result.Succeeded) + { + jwtToken.SigningKey = key; return result; + } if (result.Error is ValidationError validationError) { + stackFrames ??= []; + foreach (StackFrame stackFrame in validationError.StackFrames) + stackFrames.Add(stackFrame); + exceptionStrings ??= new StringBuilder(); keysAttempted ??= new StringBuilder(); exceptionStrings.AppendLine(validationError.MessageDetail.Message); @@ -165,11 +177,18 @@ private static ValidationResult ValidateSignatureU } } + // This method tries a number of different keys, for each failure we add a stack frame to the stackFrames collection. + // We want to add the current stack frame to the end of the list, to keep the order of the stack frames. + // If for some reason stackFrames is null or empty, we add the current stack frame as the first and only entry. + StackFrame currentStackFrame = ValidationError.GetCurrentStackFrame(); + StackFrame firstStackFrame = (stackFrames == null || stackFrames.Count == 0) ? currentStackFrame : stackFrames[0]; + SignatureValidationError signatureValidationError; + if (keysTried) { if (kidExists) { - return new SignatureValidationError( + signatureValidationError = new SignatureValidationError( new MessageDetail( TokenLogMessages.IDX10522, LogHelper.MarkAsNonPII(jwtToken.Kid), @@ -177,27 +196,24 @@ private static ValidationResult ValidateSignatureU LogHelper.MarkAsNonPII(configuration?.SigningKeys?.Count ?? 0), LogHelper.MarkAsSecurityArtifact(jwtToken.EncodedToken, JwtTokenUtilities.SafeLogJwtToken)), SignatureValidationFailure.SigningKeyNotFound, - ValidationError.GetCurrentStackFrame()); + firstStackFrame); } else { - return new SignatureValidationError( + signatureValidationError = new SignatureValidationError( new MessageDetail( TokenLogMessages.IDX10523, LogHelper.MarkAsNonPII(validationParameters.SigningKeys.Count), LogHelper.MarkAsNonPII(configuration?.SigningKeys?.Count ?? 0), LogHelper.MarkAsSecurityArtifact(jwtToken.EncodedToken, JwtTokenUtilities.SafeLogJwtToken)), SignatureValidationFailure.SigningKeyNotFound, - ValidationError.GetCurrentStackFrame()); + firstStackFrame); } } - - if (kidExists) + else if (kidExists) { - // No keys were attempted, return the error. - // This is the case where the user specified a kid, but no keys were found. - // This is not an error, but a warning that no keys were found for the specified kid. - return new SignatureValidationError( + // There is a kid, but no keys were found. + signatureValidationError = new SignatureValidationError( new MessageDetail( TokenLogMessages.IDX10524, LogHelper.MarkAsNonPII(jwtToken.Kid), @@ -205,17 +221,32 @@ private static ValidationResult ValidateSignatureU LogHelper.MarkAsNonPII(configuration?.SigningKeys?.Count ?? 0), LogHelper.MarkAsSecurityArtifact(jwtToken.EncodedToken, JwtTokenUtilities.SafeLogJwtToken)), SignatureValidationFailure.SigningKeyNotFound, - ValidationError.GetCurrentStackFrame()); + firstStackFrame); + } + else + { + signatureValidationError = new SignatureValidationError( + new MessageDetail( + TokenLogMessages.IDX10525, + LogHelper.MarkAsNonPII(validationParameters.SigningKeys.Count), + LogHelper.MarkAsNonPII(configuration?.SigningKeys?.Count ?? 0), + LogHelper.MarkAsSecurityArtifact(jwtToken.EncodedToken, JwtTokenUtilities.SafeLogJwtToken)), + SignatureValidationFailure.SigningKeyNotFound, + firstStackFrame); } - return new SignatureValidationError( - new MessageDetail( - TokenLogMessages.IDX10525, - LogHelper.MarkAsNonPII(validationParameters.SigningKeys.Count), - LogHelper.MarkAsNonPII(configuration?.SigningKeys?.Count ?? 0), - LogHelper.MarkAsSecurityArtifact(jwtToken.EncodedToken, JwtTokenUtilities.SafeLogJwtToken)), - SignatureValidationFailure.SigningKeyNotFound, - ValidationError.GetCurrentStackFrame()); + if (stackFrames != null) + { + for (int i = 1; i < stackFrames.Count; i++) + { + if (stackFrames[i] != null) + signatureValidationError.StackFrames.Add(stackFrames[i]); + } + + signatureValidationError.StackFrames.Add(currentStackFrame); + } + + return signatureValidationError; } private static ValidationResult ValidateSignatureWithKey( @@ -234,7 +265,7 @@ private static ValidationResult ValidateSignatureW TokenLogMessages.IDX10652, LogHelper.MarkAsNonPII(jsonWebToken.Alg), key), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, ValidationError.GetCurrentStackFrame()); } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/Experimental/JsonWebTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.JsonWebTokens/Experimental/JsonWebTokenHandler.ValidateToken.Internal.cs index a8af1ab2d8..ed58efb4db 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/Experimental/JsonWebTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/Experimental/JsonWebTokenHandler.ValidateToken.Internal.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.IdentityModel.Logging; @@ -22,77 +23,114 @@ internal override async Task> CallContext callContext, CancellationToken cancellationToken) { + StackFrame? stackFrame; if (string.IsNullOrEmpty(token)) { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return ValidationError.NullParameter( nameof(token), - ValidationError.GetCurrentStackFrame()); + stackFrame!); } if (validationParameters is null) { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return ValidationError.NullParameter( nameof(validationParameters), - ValidationError.GetCurrentStackFrame()); + stackFrame!); } if (token.Length > MaximumTokenSizeInBytes) { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return new ValidationError( new MessageDetail( TokenLogMessages.IDX10209, LogHelper.MarkAsNonPII(token.Length), LogHelper.MarkAsNonPII(MaximumTokenSizeInBytes)), - ValidationFailureType.SecurityTokenTooLarge, - ValidationError.GetCurrentStackFrame()); + ValidationFailureType.TokenExceedsMaximumSize, + stackFrame!); } ValidationResult readResult = ReadToken(token, callContext); - if (readResult.Succeeded) + if (!readResult.Succeeded) { - ValidationResult validationResult = await ValidateTokenAsync( - readResult.Result!, - validationParameters, - callContext, - cancellationToken) - .ConfigureAwait(false); + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return readResult.Error!.AddStackFrame(stackFrame!); + } + + ValidationResult validationResult = await ValidateTokenAsync( + readResult.Result!, + validationParameters, + callContext, + cancellationToken) + .ConfigureAwait(false); - if (validationResult.Succeeded) - return validationResult; // No need to unwrap and re-wrap the result. + if (!validationResult.Succeeded) + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); - return validationResult.Error!.AddStackFrame(ValidationError.GetCurrentStackFrame()); + return validationResult.Error!.AddStackFrame(stackFrame!); } - return readResult.Error!.AddCurrentStackFrame(); + return validationResult; } /// internal override async Task> ValidateTokenAsync( - SecurityToken token, + SecurityToken securityToken, ValidationParameters validationParameters, CallContext callContext, CancellationToken cancellationToken) { - if (token is null) + StackFrame? stackFrame; + string key = string.Empty; + if (securityToken is null) { + key = ValidationError.GetStackFrameKey(memberName: nameof(TokenHandler.ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return ValidationError.NullParameter( - nameof(token), - ValidationError.GetCurrentStackFrame()); + nameof(securityToken), + stackFrame!); } if (validationParameters is null) { + key = ValidationError.GetStackFrameKey(memberName: nameof(TokenHandler.ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return ValidationError.NullParameter( nameof(validationParameters), - ValidationError.GetCurrentStackFrame()); + stackFrame!); } - if (token is not JsonWebToken jsonWebToken) + if (securityToken is not JsonWebToken jsonWebToken) { + key = ValidationError.GetStackFrameKey(memberName: nameof(TokenHandler.ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return new ValidationError( - new MessageDetail(TokenLogMessages.IDX10001, nameof(token), nameof(JsonWebToken)), + new MessageDetail(TokenLogMessages.IDX10001, nameof(securityToken), nameof(JsonWebToken)), ValidationFailureType.SecurityTokenNotExpectedType, - ValidationError.GetCurrentStackFrame()); + stackFrame!); } BaseConfiguration? currentConfiguration = @@ -107,7 +145,11 @@ await ValidateJWEAsync(jsonWebToken, validationParameters, currentConfiguration, if (result.Succeeded) return result; - return result.Error!.AddStackFrame(ValidationError.GetCurrentStackFrame()); + key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return result.Error!.AddStackFrame(stackFrame!); } if (result.Succeeded) @@ -172,7 +214,12 @@ await ValidateJWEAsync(jsonWebToken, validationParameters, lkgConfiguration, cal } // If we reach this point, the token validation failed and we should return the error. - return result.Error!.AddCurrentStackFrame(); + key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + // If we reach this point, the securityToken validation failed and we should return the error. + return result.Error!.AddStackFrame(stackFrame!); } private async ValueTask> ValidateJWEAsync( @@ -183,16 +230,29 @@ private async ValueTask> Valid CancellationToken cancellationToken) { ValidationResult decryptionResult = DecryptToken( - jwtToken, validationParameters, configuration, callContext); + jwtToken, + validationParameters, + configuration, + callContext); + + StackFrame? stackFrame; if (!decryptionResult.Succeeded) { - return decryptionResult.Error!.AddCurrentStackFrame(); + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateJWEAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return decryptionResult.Error!.AddStackFrame(stackFrame!); } ValidationResult readResult = ReadToken(decryptionResult.Result!, callContext); if (!readResult.Succeeded) { - return readResult.Error!.AddCurrentStackFrame(); + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateJWEAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return readResult.Error!.AddStackFrame(stackFrame!); } JsonWebToken decryptedToken = (readResult.Result as JsonWebToken)!; @@ -202,7 +262,11 @@ await ValidateJWSAsync(decryptedToken!, validationParameters, configuration, cal if (!validationResult.Succeeded) { - return validationResult.Error!.AddCurrentStackFrame(); + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateJWSAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return validationResult.Error!.AddStackFrame(stackFrame!); } JsonWebToken jsonWebToken = (validationResult.Result!.SecurityToken as JsonWebToken)!; @@ -220,6 +284,7 @@ private async ValueTask> Valid CallContext callContext, CancellationToken cancellationToken) { + StackFrame? stackFrame; DateTime? expires = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Exp) ? jsonWebToken.ValidTo : null; DateTime? notBefore = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Nbf) ? jsonWebToken.ValidFrom : null; @@ -232,20 +297,32 @@ private async ValueTask> Valid callContext); if (!lifetimeResult.Succeeded) - return lifetimeResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateJWSAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return lifetimeResult.Error!.AddStackFrame(stackFrame!); + } - if (jsonWebToken.Audiences is not IList tokenAudiences) - tokenAudiences = [.. jsonWebToken.Audiences]; + if (jsonWebToken.Audiences is not IList audiences) + audiences = [.. jsonWebToken.Audiences]; ValidationResult audienceResult = Validators.ValidateAudienceInternal( - tokenAudiences, + audiences, jsonWebToken, validationParameters, callContext); if (!audienceResult.Succeeded) - return audienceResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateJWSAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return audienceResult.Error!.AddStackFrame(stackFrame!); + } ValidationResult issuerResult = await Validators.ValidateIssuerInternalAsync( @@ -256,17 +333,29 @@ await Validators.ValidateIssuerInternalAsync( cancellationToken).ConfigureAwait(false); if (!issuerResult.Succeeded) - return issuerResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateJWSAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); - ValidationResult? tokenReplayResult = + return issuerResult.Error!.AddStackFrame(stackFrame!); + } + + ValidationResult tokenReplayResult = Validators.ValidateTokenReplayInternal( expires, jsonWebToken.EncodedToken, validationParameters, callContext); - if (!tokenReplayResult.Value.Succeeded) - return tokenReplayResult.Value.Error!.AddCurrentStackFrame(); + if (!tokenReplayResult.Succeeded) + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateJWSAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return tokenReplayResult.Error!.AddStackFrame(stackFrame!); + } ValidationResult tokenTypeResult = Validators.ValidateTokenTypeInternal( @@ -276,7 +365,13 @@ await Validators.ValidateIssuerInternalAsync( callContext); if (!tokenTypeResult.Succeeded) - return tokenTypeResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateJWSAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return tokenTypeResult.Error!.AddStackFrame(stackFrame!); + } ValidationResult algorithmResult = Validators.ValidateAlgorithmInternal( @@ -286,7 +381,13 @@ await Validators.ValidateIssuerInternalAsync( callContext); if (!algorithmResult.Succeeded) - return algorithmResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateJWSAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return algorithmResult.Error!.AddStackFrame(stackFrame!); + } // The signature validation delegate is yet to be migrated to ValidationParameters. ValidationResult signatureResult = @@ -297,7 +398,13 @@ await Validators.ValidateIssuerInternalAsync( callContext); if (!signatureResult.Succeeded) - return signatureResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateJWSAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return signatureResult.Error!.AddStackFrame(stackFrame!); + } ValidationResult signatureKeyResult = Validators.ValidateSignatureKeyInternal( @@ -307,7 +414,13 @@ await Validators.ValidateIssuerInternalAsync( callContext); if (!signatureKeyResult.Succeeded) - return signatureKeyResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateJWSAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return signatureKeyResult.Error!.AddStackFrame(stackFrame!); + } // actor validation ValidationResult? actorResult = null; @@ -315,24 +428,29 @@ await Validators.ValidateIssuerInternalAsync( { ValidationResult readResult = ReadToken(jsonWebToken.Actor, callContext); if (!readResult.Succeeded) - return readResult.Error!.AddCurrentStackFrame(); - - if (validationParameters.ActorValidationParameters is null) - return ValidationError.NullParameter( - nameof(validationParameters.ActorValidationParameters), - ValidationError.GetCurrentStackFrame()); - - // TODO - what if actor token is encrypted? - JsonWebToken actorToken = (readResult.Result as JsonWebToken)!; - actorResult = await ValidateJWSAsync( - actorToken, - validationParameters.ActorValidationParameters, - configuration, + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateJWSAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return readResult.Error!.AddStackFrame(stackFrame!); + } + + ValidationParameters actorValidationParameters = validationParameters.ActorValidationParameters ?? validationParameters; + actorResult = await ValidateTokenAsync( + readResult.Result!, + actorValidationParameters, callContext, cancellationToken).ConfigureAwait(false); if (!actorResult.Value.Succeeded) - return actorResult.Value.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateJWSAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return actorResult.Value.Error!.AddStackFrame(stackFrame!); + } } return new ValidatedToken(jsonWebToken, this, validationParameters) @@ -361,8 +479,7 @@ await Validators.ValidateIssuerInternalAsync( #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. - // TODO: Move to CallContext. + // and signing key set directly on them, allowing the library to continue with securityToken validation. //if (LogHelper.IsEnabled(EventLogLevel.Warning)) // LogHelper.LogWarning(LogHelper.FormatInvariant(TokenLogMessages.IDX10261, validationParameters.ConfigurationManager.MetadataAddress, ex.ToString())); } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs index 887e20b55b..e2c991af81 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs @@ -548,7 +548,7 @@ internal void ReadToken(ReadOnlyMemory encodedTokenMemory) { throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant( LogMessages.IDX14102, - LogHelper.MarkAsUnsafeSecurityArtifact(encodedTokenSpan.Slice(0, Dot1).ToString(), t => t.ToString())), // TODO: Add an overload to LogHelper.MarkAsUnsafeSecurityArtifact that accepts span? + LogHelper.MarkAsUnsafeSecurityArtifact(encodedTokenSpan.Slice(0, Dot1).ToString(), t => t.ToString())), ex)); } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/Microsoft.IdentityModel.JsonWebTokens.csproj b/src/Microsoft.IdentityModel.JsonWebTokens/Microsoft.IdentityModel.JsonWebTokens.csproj index b6f7239a7a..d4a9d4aaa7 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/Microsoft.IdentityModel.JsonWebTokens.csproj +++ b/src/Microsoft.IdentityModel.JsonWebTokens/Microsoft.IdentityModel.JsonWebTokens.csproj @@ -17,6 +17,10 @@ true + + $(NoWarn);nullable + + True diff --git a/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/SignedHttpRequestHandler.cs b/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/SignedHttpRequestHandler.cs index 24e5fc7163..1df66d1a06 100644 --- a/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/SignedHttpRequestHandler.cs +++ b/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/SignedHttpRequestHandler.cs @@ -216,7 +216,6 @@ internal void CreateHttpRequestPayload(ref Utf8JsonWriter writer, SignedHttpRequ /// A structure that wraps parameters needed for SignedHttpRequest creation. internal virtual void AddAtClaim(ref Utf8JsonWriter writer, SignedHttpRequestDescriptor signedHttpRequestDescriptor) { - // TODO - use JsonEncodedText for property names writer.WriteString(SignedHttpRequestClaimTypes.At, signedHttpRequestDescriptor.AccessToken); } diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt index 5bd6330639..926ba0e334 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt @@ -1,4 +1,6 @@ +const Microsoft.IdentityModel.Tokens.Saml.LogMessages.IDX11138 = "IDX11138: SamlSerializer: '{0}' was unable to read a SAML Token" -> string const Microsoft.IdentityModel.Tokens.Saml.LogMessages.IDX11315 = "IDX11315: Unable to validate token. SamlSecurityToken.Assertion is null or empty." -> string +const Microsoft.IdentityModel.Tokens.Saml2.LogMessages.IDX13315 = "IDX13315: Saml2Serializer: '{0}' was unable to read a SAML2 Token" -> string Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.CreateClaimsIdentity(Microsoft.IdentityModel.Tokens.Saml.SamlSecurityToken samlToken, Microsoft.IdentityModel.Tokens.Experimental.ValidationParameters validationParameters, string issuer) -> System.Security.Claims.ClaimsIdentity Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.CreateClaimsIdentity(Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken samlToken, Microsoft.IdentityModel.Tokens.Experimental.ValidationParameters validationParameters, string issuer) -> System.Security.Claims.ClaimsIdentity override Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.CreateClaimsIdentityInternal(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.Experimental.ValidationParameters validationParameters, string issuer) -> System.Security.Claims.ClaimsIdentity @@ -7,11 +9,11 @@ override Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidateTo override Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.CreateClaimsIdentityInternal(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.Experimental.ValidationParameters validationParameters, string issuer) -> System.Security.Claims.ClaimsIdentity override Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.Experimental.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> override Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateTokenAsync(string token, Microsoft.IdentityModel.Tokens.Experimental.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> +static Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ReadToken(string token, Microsoft.IdentityModel.Tokens.Saml.SamlSerializer serializer, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.Experimental.ValidationResult static Microsoft.IdentityModel.Tokens.Saml.SamlTokenUtilities.ResolveTokenSigningKey(Microsoft.IdentityModel.Xml.KeyInfo keyInfo, System.Collections.Generic.IEnumerable signingKeys) -> Microsoft.IdentityModel.Tokens.SecurityKey static Microsoft.IdentityModel.Tokens.Saml.SamlTokenUtilities.SafeLogSamlToken(object obj) -> string static Microsoft.IdentityModel.Tokens.Saml.SamlTokenUtilities.ValidateSignature(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Xml.Signature signature, string canonicalString, Microsoft.IdentityModel.Tokens.Experimental.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.BaseConfiguration configuration, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.Experimental.ValidationResult +static Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ReadToken(string token, Microsoft.IdentityModel.Tokens.Saml2.Saml2Serializer serializer, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.Experimental.ValidationResult virtual Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ProcessStatements(Microsoft.IdentityModel.Tokens.Saml.SamlSecurityToken samlToken, string issuer, Microsoft.IdentityModel.Tokens.Experimental.ValidationParameters validationParameters) -> System.Collections.Generic.IEnumerable -virtual Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ReadSamlToken(string token, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.Experimental.ValidationResult -virtual Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ReadSaml2Token(string token, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.Experimental.ValidationResult virtual Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateOneTimeUseCondition(Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken samlToken, Microsoft.IdentityModel.Tokens.Experimental.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.Experimental.ValidationError virtual Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateProxyRestriction(Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken samlToken, Microsoft.IdentityModel.Tokens.Experimental.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.Experimental.ValidationError diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Experimental/SamlSecurityTokenHandler.ReadToken.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Experimental/SamlSecurityTokenHandler.ReadToken.cs index 219c64d11f..15a900145b 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Experimental/SamlSecurityTokenHandler.ReadToken.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Experimental/SamlSecurityTokenHandler.ReadToken.cs @@ -6,7 +6,6 @@ using System.Xml; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens.Experimental; -using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; namespace Microsoft.IdentityModel.Tokens.Saml { @@ -16,29 +15,33 @@ public partial class SamlSecurityTokenHandler : SecurityTokenHandler /// Converts a string into an instance of , returned inside of a . /// /// A Saml token as a string. - /// + /// A that is used to read the string. + /// A that contains call information. /// A with the or a . - internal virtual ValidationResult ReadSamlToken(string token, CallContext callContext) + internal static ValidationResult ReadToken( + string token, + SamlSerializer serializer, +#pragma warning disable CA1801 // Remove unused parameter + CallContext callContext) +#pragma warning restore CA1801 // Remove unused parameter { if (string.IsNullOrEmpty(token)) return ValidationError.NullParameter( nameof(token), ValidationError.GetCurrentStackFrame()); - if (token.Length > MaximumTokenSizeInBytes) - return new ValidationError( - new MessageDetail( - TokenLogMessages.IDX10209, - LogHelper.MarkAsNonPII(token.Length), - LogHelper.MarkAsNonPII(MaximumTokenSizeInBytes)), - ValidationFailureType.TokenExceedsMaximumSize, - ValidationError.GetCurrentStackFrame()); - try { using (var reader = XmlDictionaryReader.CreateTextReader(Encoding.UTF8.GetBytes(token), XmlDictionaryReaderQuotas.Max)) { - return ReadSamlToken(reader); + var assertion = serializer.ReadAssertion(reader); + if (assertion == null) + return new ValidationError( + new MessageDetail(LogMessages.IDX11138, LogHelper.MarkAsNonPII(serializer.GetType())), + ValidationFailureType.TokenReadingFailed, + ValidationError.GetCurrentStackFrame()); + + return new SamlSecurityToken(assertion); } } #pragma warning disable CA1031 // Do not catch general exception types diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Experimental/SamlSecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Experimental/SamlSecurityTokenHandler.ValidateToken.Internal.cs index 1d2620f7a9..6f9af51eb5 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Experimental/SamlSecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Experimental/SamlSecurityTokenHandler.ValidateToken.Internal.cs @@ -3,9 +3,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; +using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens.Experimental; +using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; #nullable enable namespace Microsoft.IdentityModel.Tokens.Saml @@ -22,25 +25,70 @@ internal override async Task> CallContext callContext, CancellationToken cancellationToken) { - if (token is null) + StackFrame? stackFrame; + if (string.IsNullOrEmpty(token)) + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return ValidationError.NullParameter( nameof(token), - ValidationError.GetCurrentStackFrame()); + stackFrame!); + } if (validationParameters is null) + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return ValidationError.NullParameter( nameof(validationParameters), - ValidationError.GetCurrentStackFrame()); + stackFrame!); + } - var tokenReadingResult = ReadSamlToken(token, callContext); - if (!tokenReadingResult.Succeeded) - return tokenReadingResult.Error!.AddCurrentStackFrame(); + if (token.Length > MaximumTokenSizeInBytes) + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); - return await ValidateTokenAsync( - tokenReadingResult.Result!, - validationParameters, - callContext, - cancellationToken).ConfigureAwait(false); + return new ValidationError( + new MessageDetail( + TokenLogMessages.IDX10209, + LogHelper.MarkAsNonPII(token.Length), + LogHelper.MarkAsNonPII(MaximumTokenSizeInBytes)), + ValidationFailureType.TokenExceedsMaximumSize, + stackFrame!); + } + + ValidationResult readResult = ReadToken(token, Serializer, callContext); + if (!readResult.Succeeded) + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return readResult.Error!.AddStackFrame(stackFrame!); + } + + ValidationResult validationResult = await ValidateTokenAsync( + readResult.Result!, + validationParameters, + callContext, + cancellationToken).ConfigureAwait(false); + + if (!validationResult.Succeeded) + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return validationResult.Error!.AddStackFrame(stackFrame!); + } + + return validationResult; } /// @@ -50,38 +98,51 @@ internal override async Task> CallContext callContext, CancellationToken cancellationToken) { + StackFrame? stackFrame; if (securityToken is null) { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return ValidationError.NullParameter( nameof(securityToken), - ValidationError.GetCurrentStackFrame()); + stackFrame!); } if (validationParameters is null) { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return ValidationError.NullParameter( nameof(validationParameters), - ValidationError.GetCurrentStackFrame()); + stackFrame!); } if (securityToken is not SamlSecurityToken samlToken) { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return new ValidationError( - new MessageDetail( - LogMessages.IDX11400, - this, - typeof(SamlSecurityToken), - securityToken.GetType()), + new MessageDetail(TokenLogMessages.IDX10001, nameof(securityToken), nameof(SamlSecurityToken)), ValidationFailureType.SecurityTokenNotExpectedType, - ValidationError.GetCurrentStackFrame()); + stackFrame!); } if (samlToken.Assertion is null) { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return new ValidationError( new MessageDetail(LogMessages.IDX11315), ValidationFailureType.SecurityTokenNotExpectedType, - ValidationError.GetCurrentStackFrame()); + stackFrame!); } ValidationResult lifetimeResult = @@ -93,7 +154,13 @@ internal override async Task> callContext); if (!lifetimeResult.Succeeded) - return lifetimeResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return lifetimeResult.Error!.AddStackFrame(stackFrame!); + } List audiences = []; if (samlToken.Assertion?.Conditions?.Conditions is not null) @@ -114,7 +181,13 @@ internal override async Task> callContext); if (!audienceResult.Succeeded) - return audienceResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return audienceResult.Error!.AddStackFrame(stackFrame!); + } ValidationResult issuerResult = await Validators.ValidateIssuerInternalAsync( @@ -125,17 +198,29 @@ await Validators.ValidateIssuerInternalAsync( cancellationToken).ConfigureAwait(false); if (!issuerResult.Succeeded) - return issuerResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); - ValidationResult? tokenReplayResult = + return issuerResult.Error!.AddStackFrame(stackFrame!); + } + + ValidationResult tokenReplayResult = Validators.ValidateTokenReplayInternal( samlToken.Assertion!.Conditions?.NotOnOrAfter, samlToken.Assertion!.CanonicalString!, validationParameters, callContext); - if (!tokenReplayResult.Value.Succeeded) - return tokenReplayResult.Value.Error!.AddCurrentStackFrame(); + if (!tokenReplayResult.Succeeded) + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return tokenReplayResult.Error!.AddStackFrame(stackFrame!); + } ValidationResult algorithmResult = Validators.ValidateAlgorithmInternal( @@ -145,7 +230,13 @@ await Validators.ValidateIssuerInternalAsync( callContext); if (!algorithmResult.Succeeded) - return algorithmResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return algorithmResult.Error!.AddStackFrame(stackFrame!); + } BaseConfiguration? configuration = null; if (validationParameters.ConfigurationManager is not null) @@ -173,7 +264,13 @@ await Validators.ValidateIssuerInternalAsync( callContext); if (!signatureResult.Succeeded) - return signatureResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return signatureResult.Error!.AddStackFrame(stackFrame!); + } ValidationResult signatureKeyResult = Validators.ValidateSignatureKeyInternal( @@ -183,7 +280,13 @@ await Validators.ValidateIssuerInternalAsync( callContext); if (!signatureKeyResult.Succeeded) - return signatureKeyResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return signatureKeyResult.Error!.AddStackFrame(stackFrame!); + } return new ValidatedToken(samlToken, this, validationParameters) { @@ -222,25 +325,25 @@ async Task> IResultBasedValida } async Task> IResultBasedValidation.ValidateTokenAsync( - SecurityToken token, + SecurityToken securityToken, ValidationParameters validationParameters, CallContext callContext) { return await ValidateTokenAsync( - token, + securityToken, validationParameters, callContext, default).ConfigureAwait(false); } async Task> IResultBasedValidation.ValidateTokenAsync( - SecurityToken token, + SecurityToken securityToken, ValidationParameters validationParameters, CallContext callContext, CancellationToken cancellationToken) { return await ValidateTokenAsync( - token, + securityToken, validationParameters, callContext, cancellationToken).ConfigureAwait(false); diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Experimental/SamlTokenUtilities.Experimental.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Experimental/SamlTokenUtilities.Experimental.cs index 59d9596f11..b7145f5b8e 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Experimental/SamlTokenUtilities.Experimental.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Experimental/SamlTokenUtilities.Experimental.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Text; using Microsoft.IdentityModel.Logging; @@ -49,8 +50,6 @@ internal static string SafeLogSamlToken(object obj) if (obj == null) return string.Empty; - // TODO - remove signature, if present, from the token. - // For now, we just return the type of the object. return obj.GetType().ToString(); } @@ -192,8 +191,6 @@ private static ValidationResult ValidateSignatureW CallContext callContext) #pragma warning restore CA1801 // Review unused parameters { - // TODO - this is not an AlgorithmValidationFailure, but a CryptoProviderFactory failure. - // TODO we need tests across token handlers CryptoProviderFactory cryptoProviderFactory = validationParameters.CryptoProviderFactory ?? key.CryptoProviderFactory; if (!cryptoProviderFactory.IsSupportedAlgorithm(signature.SignedInfo.SignatureMethod, key)) { @@ -202,7 +199,7 @@ private static ValidationResult ValidateSignatureW Tokens.LogMessages.IDX10652, LogHelper.MarkAsNonPII(signature.SignedInfo.SignatureMethod), key), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, ValidationError.GetCurrentStackFrame()); } @@ -265,16 +262,20 @@ private static ValidationResult ValidateSignatureU CallContext callContext) { bool keysTried = false; - bool kidExists = !string.IsNullOrEmpty(signature?.KeyInfo?.Id); + bool kidExists = signature?.KeyInfo is not null; IEnumerable keys = TokenUtilities.GetAllSigningKeys(configuration, validationParameters, callContext); StringBuilder exceptionStrings = null; StringBuilder keysAttempted = null; + // We want to capture all stack frames that were involved with faults. + // We capture the stack frames and add to the error. + IList stackFrames = null; + foreach (SecurityKey key in keys) { if (key is null) - continue; // skip null keys + continue; keysTried = true; @@ -293,20 +294,28 @@ private static ValidationResult ValidateSignatureU return result; } - if (result.Error is SignatureValidationError signatureValidationError) + if (result.Error is ValidationError validationError) { + stackFrames ??= []; + foreach (StackFrame stackFrame in validationError.StackFrames) + stackFrames.Add(stackFrame); + exceptionStrings ??= new StringBuilder(); keysAttempted ??= new StringBuilder(); - exceptionStrings.AppendLine(signatureValidationError.MessageDetail.Message); + exceptionStrings.AppendLine(validationError.MessageDetail.Message); keysAttempted.AppendLine(key.ToString()); } } + StackFrame currentStackFrame = ValidationError.GetCurrentStackFrame(); + StackFrame firstStackFrame = (stackFrames == null) ? currentStackFrame! : stackFrames[0]; + SignatureValidationError signatureValidationError; + if (keysTried) { if (kidExists) { - return new SignatureValidationError( + signatureValidationError = new SignatureValidationError( new MessageDetail( Tokens.LogMessages.IDX10522, LogHelper.MarkAsNonPII(signature?.KeyInfo?.Id), @@ -314,42 +323,56 @@ private static ValidationResult ValidateSignatureU LogHelper.MarkAsNonPII(configuration?.SigningKeys?.Count ?? 0), LogHelper.MarkAsSecurityArtifact(canonicalString, SafeLogSamlToken)), SignatureValidationFailure.SigningKeyNotFound, - ValidationError.GetCurrentStackFrame()); + currentStackFrame); } else { - return new SignatureValidationError( + signatureValidationError = new SignatureValidationError( new MessageDetail( Tokens.LogMessages.IDX10523, LogHelper.MarkAsNonPII(validationParameters.SigningKeys.Count), LogHelper.MarkAsNonPII(configuration?.SigningKeys?.Count ?? 0), LogHelper.MarkAsSecurityArtifact(canonicalString, SafeLogSamlToken)), SignatureValidationFailure.SigningKeyNotFound, - ValidationError.GetCurrentStackFrame()); + currentStackFrame); } } - - if (kidExists) + else if (kidExists) { - return new SignatureValidationError( + signatureValidationError = new SignatureValidationError( new MessageDetail( - Tokens.LogMessages.IDX10526, + Tokens.LogMessages.IDX10524, LogHelper.MarkAsNonPII(signature?.KeyInfo?.Id), LogHelper.MarkAsNonPII(validationParameters.SigningKeys.Count), LogHelper.MarkAsNonPII(configuration?.SigningKeys?.Count ?? 0), LogHelper.MarkAsSecurityArtifact(canonicalString, SafeLogSamlToken)), SignatureValidationFailure.SigningKeyNotFound, - ValidationError.GetCurrentStackFrame()); + currentStackFrame); + } + else + { + signatureValidationError = new SignatureValidationError( + new MessageDetail( + Tokens.LogMessages.IDX10525, + LogHelper.MarkAsNonPII(validationParameters.SigningKeys.Count), + LogHelper.MarkAsNonPII(configuration?.SigningKeys?.Count ?? 0), + LogHelper.MarkAsSecurityArtifact(canonicalString, SafeLogSamlToken)), + SignatureValidationFailure.SigningKeyNotFound, + currentStackFrame); } - return new SignatureValidationError( - new MessageDetail( - Tokens.LogMessages.IDX10525, - LogHelper.MarkAsNonPII(validationParameters.SigningKeys.Count), - LogHelper.MarkAsNonPII(configuration?.SigningKeys?.Count ?? 0), - LogHelper.MarkAsSecurityArtifact(canonicalString, SafeLogSamlToken)), - SignatureValidationFailure.SigningKeyNotFound, - ValidationError.GetCurrentStackFrame()); + if (stackFrames is not null) + { + for (int i = 1; i < stackFrames.Count; i++) + { + if (stackFrames[i] != null) + signatureValidationError.StackFrames.Add(stackFrames[i]); + } + + signatureValidationError.StackFrames.Add(currentStackFrame); + } + + return signatureValidationError; } } } diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/LogMessages.cs index b83a539bcb..329d6d8dcf 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/LogMessages.cs @@ -56,6 +56,7 @@ internal static class LogMessages internal const string IDX11135 = "IDX11135: Unable to read SamlSecurityToken. Saml element '{0}' must have value."; internal const string IDX11136 = "IDX11136: 'AuthorizationDecisionStatement' cannot be empty."; internal const string IDX11137 = "IDX11137: 'SamlAction' must have a value."; + internal const string IDX11138 = "IDX11138: SamlSerializer: '{0}' was unable to read a SAML Token"; // Saml writting internal const string IDX11501 = "IDX11501: SamlAssertion Id cannot be null or empty."; diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Experimental/Saml2SecurityTokenHandler.ReadToken.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Experimental/Saml2SecurityTokenHandler.ReadToken.cs index 64b06675f3..ef25717d81 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Experimental/Saml2SecurityTokenHandler.ReadToken.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Experimental/Saml2SecurityTokenHandler.ReadToken.cs @@ -6,7 +6,6 @@ using System.Xml; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens.Experimental; -using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; namespace Microsoft.IdentityModel.Tokens.Saml2 { @@ -16,27 +15,31 @@ public partial class Saml2SecurityTokenHandler : SecurityTokenHandler /// Converts a string into an instance of , returned inside of a . /// /// A Saml2 token as a string. - /// + /// A that is used to read the string. + /// A that contains call information. /// A with the or a . - internal virtual ValidationResult ReadSaml2Token(string token, CallContext callContext) +#pragma warning disable CA1801 // Review unused parameters + internal static ValidationResult ReadToken( + string token, + Saml2Serializer serializer, + CallContext callContext) +#pragma warning restore CA1801 // Review unused parameters { if (string.IsNullOrEmpty(token)) return ValidationError.NullParameter(nameof(token), ValidationError.GetCurrentStackFrame()); - if (token.Length > MaximumTokenSizeInBytes) - return new ValidationError( - new MessageDetail( - TokenLogMessages.IDX10209, - LogHelper.MarkAsNonPII(token.Length), - LogHelper.MarkAsNonPII(MaximumTokenSizeInBytes)), - ValidationFailureType.TokenExceedsMaximumSize, - ValidationError.GetCurrentStackFrame()); - try { using (var reader = XmlDictionaryReader.CreateTextReader(Encoding.UTF8.GetBytes(token), XmlDictionaryReaderQuotas.Max)) { - return ReadSaml2Token(reader); + var assertion = serializer.ReadAssertion(reader); + if (assertion == null) + return new ValidationError( + new MessageDetail(LogMessages.IDX13315, LogHelper.MarkAsNonPII(serializer.GetType())), + ValidationFailureType.TokenReadingFailed, + ValidationError.GetCurrentStackFrame()); + + return new Saml2SecurityToken(assertion); } } #pragma warning disable CA1031 // Do not catch general exception types diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Experimental/Saml2SecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Experimental/Saml2SecurityTokenHandler.ValidateToken.Internal.cs index 66d6dd9a6f..844de6e24f 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Experimental/Saml2SecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Experimental/Saml2SecurityTokenHandler.ValidateToken.Internal.cs @@ -3,10 +3,13 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; +using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens.Experimental; using Microsoft.IdentityModel.Tokens.Saml; +using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; #nullable enable namespace Microsoft.IdentityModel.Tokens.Saml2 @@ -23,17 +26,71 @@ internal override async Task> CallContext callContext, CancellationToken cancellationToken) { - if (token is null) - return ValidationError.NullParameter(nameof(token), ValidationError.GetCurrentStackFrame()); + StackFrame? stackFrame; + if (string.IsNullOrEmpty(token)) + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return ValidationError.NullParameter( + nameof(token), + stackFrame!); + } if (validationParameters is null) - return ValidationError.NullParameter(nameof(validationParameters), ValidationError.GetCurrentStackFrame()); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return ValidationError.NullParameter( + nameof(validationParameters), + stackFrame!); + } + + if (token.Length > MaximumTokenSizeInBytes) + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); - var tokenReadingResult = ReadSaml2Token(token, callContext); - if (!tokenReadingResult.Succeeded) - return tokenReadingResult.Error!.AddCurrentStackFrame(); + return new ValidationError( + new MessageDetail( + TokenLogMessages.IDX10209, + LogHelper.MarkAsNonPII(token.Length), + LogHelper.MarkAsNonPII(MaximumTokenSizeInBytes)), + ValidationFailureType.TokenExceedsMaximumSize, + stackFrame!); + } - return await ValidateTokenAsync(tokenReadingResult.Result!, validationParameters, callContext, cancellationToken).ConfigureAwait(false); + ValidationResult readResult = ReadToken(token, Serializer, callContext); + if (!readResult.Succeeded) + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return readResult.Error!.AddStackFrame(stackFrame!); + } + + ValidationResult validationResult = + await ValidateTokenAsync( + readResult.Result!, + validationParameters, + callContext, + cancellationToken).ConfigureAwait(false); + + if (!validationResult.Succeeded) + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return validationResult.Error!.AddStackFrame(stackFrame!); + } + + return validationResult; } /// @@ -43,38 +100,51 @@ internal async override Task> CallContext callContext, CancellationToken cancellationToken) { + StackFrame? stackFrame; if (securityToken is null) { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return ValidationError.NullParameter( nameof(securityToken), - ValidationError.GetCurrentStackFrame()); + stackFrame!); } if (validationParameters is null) { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return ValidationError.NullParameter( nameof(validationParameters), - ValidationError.GetCurrentStackFrame()); + stackFrame!); } if (securityToken is not Saml2SecurityToken samlToken) { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return new ValidationError( - new MessageDetail( - Saml.LogMessages.IDX11400, - this, - typeof(Saml2SecurityToken), - securityToken.GetType()), - ValidationFailureType.SecurityTokenNotExpectedType, - ValidationError.GetCurrentStackFrame()); + new MessageDetail(TokenLogMessages.IDX10001, nameof(securityToken), nameof(Saml2SecurityToken)), + ValidationFailureType.SecurityTokenNotExpectedType, + stackFrame!); } if (samlToken.Assertion is null) { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return new ValidationError( new MessageDetail(Saml.LogMessages.IDX11315), ValidationFailureType.SecurityTokenNotExpectedType, - ValidationError.GetCurrentStackFrame()); + stackFrame!); } ValidationResult lifetimeResult = @@ -86,12 +156,17 @@ internal async override Task> callContext); if (!lifetimeResult.Succeeded) - return lifetimeResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return lifetimeResult.Error!.AddStackFrame(stackFrame!); + } List audiences = []; if (samlToken.Assertion?.Conditions is not null) { - foreach (var audienceRestriction in samlToken.Assertion!.Conditions.AudienceRestrictions) { // AudienceRestriction.Audiences is a List but returned as ICollection @@ -125,7 +200,13 @@ internal async override Task> callContext); if (!audienceResult.Succeeded) - return audienceResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return audienceResult.Error!.AddStackFrame(stackFrame!); + } ValidationResult issuerResult = await Validators.ValidateIssuerInternalAsync( @@ -136,7 +217,13 @@ await Validators.ValidateIssuerInternalAsync( cancellationToken).ConfigureAwait(false); if (!issuerResult.Succeeded) - return issuerResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return issuerResult.Error!.AddStackFrame(stackFrame!); + } ValidationResult? tokenReplayResult = Validators.ValidateTokenReplayInternal( samlToken.Assertion!.Conditions?.NotOnOrAfter, @@ -145,7 +232,13 @@ await Validators.ValidateIssuerInternalAsync( callContext); if (!tokenReplayResult.Value.Succeeded) - return tokenReplayResult.Value.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return tokenReplayResult.Value.Error!.AddStackFrame(stackFrame!); + } ValidationResult algorithmResult = Validators.ValidateAlgorithmInternal( @@ -155,7 +248,13 @@ await Validators.ValidateIssuerInternalAsync( callContext); if (!algorithmResult.Succeeded) - return algorithmResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return algorithmResult.Error!.AddStackFrame(stackFrame!); + } BaseConfiguration? configuration = null; if (validationParameters.ConfigurationManager is not null) @@ -182,7 +281,13 @@ await Validators.ValidateIssuerInternalAsync( callContext); if (!signatureResult.Succeeded) - return signatureResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return signatureResult.Error!.AddStackFrame(stackFrame!); + } ValidationResult signingKeyResult = Validators.ValidateSignatureKeyInternal( @@ -192,7 +297,13 @@ await Validators.ValidateIssuerInternalAsync( callContext); if (!signingKeyResult.Succeeded) - return signingKeyResult.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: nameof(ValidateTokenAsync)); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return signingKeyResult.Error!.AddStackFrame(stackFrame!); + } return new ValidatedToken(samlToken, this, validationParameters) { diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/LogMessages.cs index d60b4b4a5a..da39924253 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/LogMessages.cs @@ -61,6 +61,7 @@ internal static class LogMessages internal const string IDX13313 = "IDX13313: 'AuthnStatement' cannot be empty."; internal const string IDX13312 = "IDX13312: 'AuthnContext' cannot be empty."; internal const string IDX13314 = "IDX13314: 'AuthzDecisionStatement' cannot be empty (must have at least one 'Subject')."; + internal const string IDX13315 = "IDX13315: Saml2Serializer: '{0}' was unable to read a SAML2 Token"; // Saml2SecurityTokenHandler writing internal const string IDX13142 = "IDX13142: A Saml2SamlAttributeStatement can only have one Saml2Attribute of type 'Actor'. This special Saml2Attribute is used in delegation scenarios."; diff --git a/src/Microsoft.IdentityModel.Tokens/EventBasedLRUCache.cs b/src/Microsoft.IdentityModel.Tokens/EventBasedLRUCache.cs index 5414c89064..68688aa68c 100644 --- a/src/Microsoft.IdentityModel.Tokens/EventBasedLRUCache.cs +++ b/src/Microsoft.IdentityModel.Tokens/EventBasedLRUCache.cs @@ -58,7 +58,6 @@ internal class EventBasedLRUCache #region event queue private int _eventQueuePollingInterval = 50; // The idle timeout, the _eventQueueTask will end after being idle for the specified time interval (execution continues even if the queue is empty to reduce the task startup overhead), default to 120 seconds. - // TODO: consider implementing a better algorithm that tracks and predicts the usage patterns and adjusts this value dynamically. private long _eventQueueTaskIdleTimeoutInSeconds = 120; // The time when the _eventQueueTask should end. The intent is to reduce the overhead costs of starting/ending tasks too frequently // but at the same time keep the _eventQueueTask a short running task. diff --git a/src/Microsoft.IdentityModel.Tokens/Experimental/TokenHandler.Internal.cs b/src/Microsoft.IdentityModel.Tokens/Experimental/TokenHandler.Internal.cs index 6274d77fbb..28c08e174d 100644 --- a/src/Microsoft.IdentityModel.Tokens/Experimental/TokenHandler.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens/Experimental/TokenHandler.Internal.cs @@ -16,15 +16,15 @@ namespace Microsoft.IdentityModel.Tokens public abstract partial class TokenHandler { /// - /// Validates a token. + /// Validates a security token. /// On validation failure no exception will be thrown. will contain information pertaining to the error. /// - /// The token to be validated. - /// The to be used for validating the token. + /// The security token to be validated. + /// The to be used for validating the security token. /// A that contains call information. /// A that can be used to request cancellation of the asynchronous operation. /// An with either a - /// if the token was validated or a containing the failure information. + /// if the security token was validated or a containing the failure information. internal virtual Task> ValidateTokenAsync( string token, ValidationParameters validationParameters, @@ -35,23 +35,23 @@ internal virtual Task> Validat new NotImplementedException( FormatInvariant( LogMessages.IDX10267, - MarkAsNonPII("internal virtual Task> " + + MarkAsNonPII("internal virtual Task> " + "ValidateTokenAsync(string token, ValidationParameters validationParameters, CallContext callContext, CancellationToken cancellationToken)"), MarkAsNonPII(GetType().FullName)))); } /// - /// Validates a token. + /// Validates a security token. /// On validation failure no exception will be thrown. will contain information pertaining to the error. /// - /// The token to be validated. - /// The to be used for validating the token. + /// The to be validated. + /// The to be used for validating the securityToken. /// A that contains call information. /// A that can be used to request cancellation of the asynchronous operation. /// An with either a - /// if the token was validated or a containing the failure information. + /// if the securityToken was validated or a containing the failure information. internal virtual Task> ValidateTokenAsync( - SecurityToken token, + SecurityToken securityToken, ValidationParameters validationParameters, CallContext callContext, CancellationToken cancellationToken) @@ -60,7 +60,7 @@ internal virtual Task> Validat new NotImplementedException( FormatInvariant( LogMessages.IDX10267, - MarkAsNonPII("internal virtual Task> " + + MarkAsNonPII("internal virtual Task> " + "ValidateTokenAsync(SecurityToken token, ValidationParameters validationParameters, CallContext callContext, CancellationToken cancellationToken)"), MarkAsNonPII(GetType().FullName)))); } @@ -70,7 +70,7 @@ internal virtual Task> Validat /// Currently only used by the JsonWebTokenHandler when called with ValidationParameters to allow for a Lazy creation. /// /// the that has the Claims. - /// the that was used to validate the token. + /// the that was used to validate the securityToken. /// the 'issuer' to use by default when creating a Claim. /// A . /// diff --git a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/AsyncValidate.cd b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/AsyncValidate.cd index 7dc6d1c10b..a9aebcac45 100644 --- a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/AsyncValidate.cd +++ b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/AsyncValidate.cd @@ -28,27 +28,6 @@ Validation\Results\Details\AlgorithmValidationError.cs - - - - - - - - Validation\Results\Details\ValidationError.cs - - - - - AQAEAAAIAAAAAAACAAAAGAAEAAwAAAAQBEAAJAAAAAA= - Validation\Results\Details\ValidationError.cs - - @@ -142,24 +121,6 @@ - - - - - - AAAAAAAAAAAAAQACgAgAAEQBAAACIIAAABAAAAAAAiA= - Validation\Results\ValidationResult.cs - - - - - - diff --git a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/AlgorithmValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/AlgorithmValidationError.cs index 96d02500fe..7aad5367ac 100644 --- a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/AlgorithmValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/AlgorithmValidationError.cs @@ -65,7 +65,7 @@ public override Exception GetException() return Exception; if (FailureType == AlgorithmValidationFailure.ValidationFailed - || FailureType == AlgorithmValidationFailure.AlgorithmIsNotSupported + || FailureType == AlgorithmValidationFailure.NotSupported || FailureType == AlgorithmValidationFailure.ValidatorThrew) { Exception = new SecurityTokenInvalidAlgorithmException( diff --git a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/AudienceValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/AudienceValidationError.cs index 72f8fc4685..4ffa186933 100644 --- a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/AudienceValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/AudienceValidationError.cs @@ -70,8 +70,8 @@ public override Exception GetException() return Exception; if (FailureType == AudienceValidationFailure.NoAudienceInToken - || FailureType == AudienceValidationFailure.NoValidationParameterAudiencesProvided - || FailureType == AudienceValidationFailure.AudienceDidNotMatch + || FailureType == AudienceValidationFailure.NoAudiencesProvided + || FailureType == AudienceValidationFailure.DidNotMatch || FailureType == AudienceValidationFailure.ValidatorThrew) { if (TokenAudiences == null) diff --git a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/IdentityModelStackFrame.cs b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/IdentityModelStackFrame.cs new file mode 100644 index 0000000000..aab69fe9c3 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/IdentityModelStackFrame.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +using System.Diagnostics; + +#nullable enable + +namespace Microsoft.IdentityModel.Tokens.Experimental +{ + internal sealed class IdentityModelStackFrame : StackFrame + { + private readonly string _memberName; + private readonly int _lineNumber; + private readonly int _columnNumber; + private readonly string _fileName; + private readonly int _nativeOffset; + private readonly int _ilOffset; + + public IdentityModelStackFrame( + int ilOffset, + int nativeOffset, + string fileName, + int lineNumber, + int columnNumber, + string memberName) + : base(fileName, lineNumber, columnNumber) + { + _lineNumber = lineNumber; + _columnNumber = columnNumber; + _memberName = memberName; + _fileName = fileName; + _nativeOffset = nativeOffset; + _ilOffset = ilOffset; + } + + public override string ToString() + { + return $"{_memberName} at offset " + + $"{_nativeOffset} in file: line: column {_fileName}" + + $":{_lineNumber}:{_columnNumber}"; + } + + public override string GetFileName() => _fileName; + + public override int GetFileLineNumber() => _lineNumber; + + public override int GetFileColumnNumber() => _columnNumber; + + public override int GetNativeOffset() => _nativeOffset; + + public override int GetILOffset() => _ilOffset; + + public string MemberName => _memberName; + } +} +#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/IssuerValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/IssuerValidationError.cs index 9d4661d2e4..5e899e9fb8 100644 --- a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/IssuerValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/IssuerValidationError.cs @@ -65,9 +65,10 @@ public override Exception GetException() return Exception; if (FailureType == IssuerValidationFailure.NoIssuerInToken + || FailureType == IssuerValidationFailure.IssuerDidNotMatch || FailureType == IssuerValidationFailure.ValidationFailed || FailureType == IssuerValidationFailure.ValidatorThrew - || FailureType == IssuerValidationFailure.NoValidationParameterIssuersProvided) + || FailureType == IssuerValidationFailure.NoIssuersProvided) { Exception = new SecurityTokenInvalidIssuerException(MessageDetail.Message, this, InnerException); return Exception; diff --git a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/ValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/ValidationError.cs index ea8b3b51e7..b18db2e89a 100644 --- a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/ValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Results/Details/ValidationError.cs @@ -11,10 +11,13 @@ namespace Microsoft.IdentityModel.Tokens.Experimental { /// - /// Represents an error that occurred during a validation. + /// Represents an error that occurred during the validation of a . /// public class ValidationError { + // The CachedStckFrames dictionary contains the cached StackFrames for the entire runtime. + internal static ConcurrentDictionary CachedStackFrames { get; } = new(); + /// /// Creates an instance of . /// @@ -79,7 +82,7 @@ public virtual Exception GetException() /// Gets or sets the exception associated with the . /// #pragma warning disable CA1721 // Property names should not match get methods - protected Exception? Exception { get; set; } + public Exception? Exception { get; protected set; } #pragma warning restore CA1721 // Property names should not match get methods /// @@ -105,17 +108,14 @@ public virtual Exception GetException() public Exception? InnerException { get; } /// - /// Gets the message that contains details of the error. - /// - public string Message => MessageDetail.Message; - - /// - /// Gets the message which contains information about the error. Can be used to provide details for error messages. + /// Gets the which contains information about the error. Can be used to provide details for error messages. /// - internal MessageDetail MessageDetail { get; } + public MessageDetail MessageDetail { get; } /// - /// Gets the stack frames where the exception occurred. + /// Gets the collection of instances that represent the call stack locations + /// where this was recorded or augmented. This can be used for enhanced + /// diagnostics and tracing of validation errors, especially in asynchronous or layered validation flows. /// public IList StackFrames { get; } @@ -130,18 +130,82 @@ public ValidationError AddStackFrame(StackFrame stackFrame) return this; } + /// + /// Adds the to the cache using the key. + /// If there is no cache entry for the key, a new stack frame is created and added to the cache. + /// + /// The key for the cached stack frame. + /// The to use as a template if a cached frame does not exist. + /// The name of the calling member. Will be automatically provided by the compiler. + /// The that was found or added. + internal static StackFrame GetAsyncStackFrame( + string key, + StackFrame stackFrame, + [CallerMemberName] string memberName = "") + { + // GetOrAdd is thread-safe and will only create a new entry if it does not already exist. + StackFrame cachedFrame = CachedStackFrames.GetOrAdd(key, _ => + { + StackFrame frame = stackFrame ?? new StackFrame(1, true); + return new IdentityModelStackFrame( + frame.GetILOffset(), + frame.GetNativeOffset(), + frame.GetFileName() ?? string.Empty, + frame.GetFileLineNumber(), + frame.GetFileColumnNumber(), + memberName); + }); + + // Defensive: never return null + return cachedFrame ?? new StackFrame(0, true); + } + /// /// Adds the current stack frame to the list of stack frames and returns the updated object. /// If there is no cache entry for the given file path and line number, a new stack frame is created and added to the cache. /// + /// The key for the cached stack frame. + /// The that was found. + /// true if found, false otherwise. + internal static bool TryGetStackFrame( + string key, + out StackFrame? cachedFrame) + { + return CachedStackFrames.TryGetValue(key, out cachedFrame); + } + + /// + /// Gets the key for the cached stack frame based on the current member name, file path, and line number. + /// + /// The name of the calling member. Will be automatically provided by the compiler by default. + /// The path to the file from which this method is called. Captured automatically by default. + /// The line number from which this method is called. Captured automatically by default. + /// The updated object. + internal static string GetStackFrameKey( + [CallerMemberName] string memberName = "", + [CallerFilePath] string filePath = "", + [CallerLineNumber] int lineNumber = 0) + { + return memberName + filePath + lineNumber; + } + + /// + /// Adds the current stack frame to the list of stack frames and returns the updated object. + /// If there is no cache entry for the given file path and line number, a new stack frame is created and added to the cache. + /// + /// The name of the calling member. Will be automatically provided by the compiler by default. /// The path to the file from which this method is called. Captured automatically by default. /// The line number from which this method is called. CAptured automatically by default. /// The number of stack frames to skip when capturing. Used to avoid capturing this method and get the caller instead. /// The updated object. - public ValidationError AddCurrentStackFrame([CallerFilePath] string filePath = "", [CallerLineNumber] int lineNumber = 0, int skipFrames = 1) + internal ValidationError AddCurrentStackFrame( + [CallerMemberName] string memberName = "", + [CallerFilePath] string filePath = "", + [CallerLineNumber] int lineNumber = 0, + int skipFrames = 1) { // We add 1 to the skipped frames to skip the current method - StackFrames.Add(GetCurrentStackFrame(filePath, lineNumber, skipFrames + 1)); + StackFrames.Add(GetCurrentStackFrame(memberName, filePath, lineNumber, skipFrames + 1)); return this; } @@ -149,25 +213,22 @@ public ValidationError AddCurrentStackFrame([CallerFilePath] string filePath = " /// Returns the stack frame corresponding to the file path and line number from which this method is called. /// If there is no cache entry for the given file path and line number, a new stack frame is created and added to the cache. /// + /// The name of the calling member. Will be automatically provided by the compiler by default. /// The path to the file from which this method is called. Captured automatically by default. /// The line number from which this method is called. CAptured automatically by default. /// The number of stack frames to skip when capturing. Used to avoid capturing this method and get the caller instead. /// The captured stack frame. /// If this is called from a helper method, consider adding an extra skip frame to avoid capturing the helper instead. - public static StackFrame GetCurrentStackFrame( - [CallerFilePath] string filePath = "", [CallerLineNumber] int lineNumber = 0, int skipFrames = 1) + internal static StackFrame + GetCurrentStackFrame( + [CallerMemberName] string memberName = "", + [CallerFilePath] string filePath = "", + [CallerLineNumber] int lineNumber = 0, int skipFrames = 1) { - // String is allocated, but it goes out of scope immediately after the call - string key = filePath + lineNumber; - return CachedStackFrames.GetOrAdd( - key, - // Need to skip the call to the delegate + GetOrAdd when creating the frame + memberName + filePath + lineNumber, _ => new StackFrame(skipFrames + 2, true)); } - - // ConcurrentDictionary is thread-safe and only locks when adding a new item. - private static ConcurrentDictionary CachedStackFrames { get; } = new(); } } #nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/ValidationDelegates.cd b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/ValidationDelegates.cd index a6af5bfb82..c88b9da62f 100644 --- a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/ValidationDelegates.cd +++ b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/ValidationDelegates.cd @@ -3,19 +3,9 @@ - YAgEEAQAKMQck0AAi5R6AACRWgBkBQIAAQgYQsaIkxA= - Validation\TokenValidationParameters.IssuerValidationDelegate.cs + YAgEEAQAaMQck0AAi5R6AACRWgBkBQIAAAgYQsaIkxA= + TokenValidationParameters.cs - - - - - - - AAAAAAAAAACAAAAAACAQBAAAAAAAAAAAgAAAAAAAAAA= - Validation\Validators.Issuer.cs - - \ No newline at end of file diff --git a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/ValidationFailureType.cs b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/ValidationFailureType.cs index 5272cf550a..8449e99b0e 100644 --- a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/ValidationFailureType.cs +++ b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/ValidationFailureType.cs @@ -96,28 +96,28 @@ protected AudienceValidationFailure(string name) : base(name) } /// - /// Audience validation failed. + /// There was an audience in the token, but it did not match any of the expected audiences in . /// - public static readonly Experimental.AudienceValidationFailure AudienceDidNotMatch = new AudienceValidationFailed("AudienceDidNotMatch"); - private class AudienceValidationFailed : Experimental.AudienceValidationFailure { internal AudienceValidationFailed(string name) : base(name) { } } + public static readonly AudienceValidationFailure DidNotMatch = new AudienceDidNotMatchType("AudienceDidNotMatch"); + private class AudienceDidNotMatchType : AudienceValidationFailure { internal AudienceDidNotMatchType(string name) : base(name) { } } /// - /// Audience validation delegate threw an exception. + /// The audience validation delegate threw an exception. /// - public static readonly Experimental.AudienceValidationFailure ValidatorThrew = new AudienceValidatorThrewFailure("AudienceValidatorThrew"); - private class AudienceValidatorThrewFailure : Experimental.AudienceValidationFailure { internal AudienceValidatorThrewFailure(string name) : base(name) { } } + public static readonly AudienceValidationFailure ValidatorThrew = new AudienceValidatorThrew("AudienceValidatorThrew"); + private class AudienceValidatorThrew : AudienceValidationFailure { internal AudienceValidatorThrew(string name) : base(name) { } } /// - /// No audience found in the . + /// No audience was found in the . /// - public static readonly Experimental.AudienceValidationFailure NoAudienceInToken = new NoAudienceInTokenFailure("NoAudienceInToken"); - private class NoAudienceInTokenFailure : Experimental.AudienceValidationFailure { internal NoAudienceInTokenFailure(string name) : base(name) { } } + public static readonly AudienceValidationFailure NoAudienceInToken = new NoAudienceInTokenType("NoAudienceInToken"); + private class NoAudienceInTokenType : AudienceValidationFailure { internal NoAudienceInTokenType(string name) : base(name) { } } /// /// No audiences were found in . /// - public static readonly Experimental.AudienceValidationFailure NoValidationParameterAudiencesProvided = new NoValidationParameterAudiencesProvidedFailure("NoValidationParameterAudiencesProvided"); - private class NoValidationParameterAudiencesProvidedFailure : Experimental.AudienceValidationFailure { internal NoValidationParameterAudiencesProvidedFailure(string name) : base(name) { } } + public static readonly AudienceValidationFailure NoAudiencesProvided = new NoAudiencesProvidedType("NoAudiencesProvided"); + private class NoAudiencesProvidedType : AudienceValidationFailure { internal NoAudiencesProvidedType(string name) : base(name) { } } } /// @@ -133,61 +133,20 @@ protected AlgorithmValidationFailure(string name) : base(name) { } /// /// General algorithm validation failed. /// - public static readonly AlgorithmValidationFailure ValidationFailed = new AlgorithmValidationFailedType("AlgorithmValidationFailed"); - private class AlgorithmValidationFailedType : AlgorithmValidationFailure { internal AlgorithmValidationFailedType(string name) : base(name) { } } + public static readonly AlgorithmValidationFailure ValidationFailed = new AlgorithmValidationFailed("AlgorithmValidationFailed"); + private class AlgorithmValidationFailed : AlgorithmValidationFailure { internal AlgorithmValidationFailed(string name) : base(name) { } } /// /// Algorithm validation delegate threw an exception. /// - public static readonly AlgorithmValidationFailure ValidatorThrew = new AlgorithmValidatorThrewType("AlgorithmValidatorThrew"); - private class AlgorithmValidatorThrewType : AlgorithmValidationFailure { internal AlgorithmValidatorThrewType(string name) : base(name) { } } + public static readonly AlgorithmValidationFailure ValidatorThrew = new AlgorithmValidatorThrew("AlgorithmValidatorThrew"); + private class AlgorithmValidatorThrew : AlgorithmValidationFailure { internal AlgorithmValidatorThrew(string name) : base(name) { } } /// - /// Unsupported algorithm. + /// The algorithm is not supported by the runtime. /// - public static readonly AlgorithmValidationFailure AlgorithmIsNotSupported = new AlgorithmIsNotSupportedType("AlgorithmIsNotSupported"); - private class AlgorithmIsNotSupportedType : AlgorithmValidationFailure { internal AlgorithmIsNotSupportedType(string name) : base(name) { } } - } - - /// - /// Failures that occur during validation of a the signature key. - /// - public abstract class SignatureKeyValidationFailure : ValidationFailureType - { - /// - /// Creates an instance of - /// - protected SignatureKeyValidationFailure(string name) : base(name) { } - - /// - /// Signature key validation failed. - /// - public static readonly SignatureKeyValidationFailure ValidationFailed = new SignatureKeyValidationFailedType("SignatureKeyValidationFailed"); - private class SignatureKeyValidationFailedType : SignatureKeyValidationFailure { internal SignatureKeyValidationFailedType(string name) : base(name) { } } - - /// - /// The signature key validator delegate threw an exception. - /// - public static readonly SignatureKeyValidationFailure ValidatorThrew = new SignatureKeyValidatorThrewType("SignatureKeyValidatorThrew"); - private class SignatureKeyValidatorThrewType : SignatureKeyValidationFailure { internal SignatureKeyValidatorThrewType(string name) : base(name) { } } - - /// - /// Signature key was not yet valid. - /// - public static readonly SignatureKeyValidationFailure NotYetValid = new SigningKeyNotYetValidType("NotYetValid"); - private class SigningKeyNotYetValidType : SignatureKeyValidationFailure { internal SigningKeyNotYetValidType(string name) : base(name) { } } - - /// - /// Signature key was expired. - /// - public static readonly SignatureKeyValidationFailure KeyExpired = new SigningKeyExpiredType("KeyExpired"); - private class SigningKeyExpiredType : SignatureKeyValidationFailure { internal SigningKeyExpiredType(string name) : base(name) { } } - - /// - /// Signature key is null. - /// - public static readonly SignatureKeyValidationFailure KeyIsNull = new SigningKeyIsNullType("KeyIsNull"); - private class SigningKeyIsNullType : SignatureKeyValidationFailure { internal SigningKeyIsNullType(string name) : base(name) { } } + public static readonly AlgorithmValidationFailure NotSupported = new AlgorithmIsNotSupported("AlgorithmIsNotSupported"); + private class AlgorithmIsNotSupported : AlgorithmValidationFailure { internal AlgorithmIsNotSupported(string name) : base(name) { } } } /// @@ -203,17 +162,24 @@ protected IssuerValidationFailure(string name) : base(name) { } /// /// General issuer validation failure. /// - public static readonly IssuerValidationFailure ValidationFailed = new IssuerValidationFailedType("IssuerValidationFailed"); - private class IssuerValidationFailedType : IssuerValidationFailure { internal IssuerValidationFailedType(string name) : base(name) { } } + public static readonly IssuerValidationFailure ValidationFailed = new IssuerValidationFailed("IssuerValidationFailed"); + private class IssuerValidationFailed : IssuerValidationFailure { internal IssuerValidationFailed(string name) : base(name) { } } /// /// Issuer validation delegate threw an exception. /// - public static readonly IssuerValidationFailure ValidatorThrew = new IssuerValidatorThrewType("IssuerValidatorThrew"); - private class IssuerValidatorThrewType : IssuerValidationFailure { internal IssuerValidatorThrewType(string name) : base(name) { } } + public static readonly IssuerValidationFailure ValidatorThrew = new IssuerValidatorThrew("IssuerValidatorThrew"); + private class IssuerValidatorThrew : IssuerValidationFailure { internal IssuerValidatorThrew(string name) : base(name) { } } + + /// + /// There was an issuer in the token, but it did not match any of the valid issuers in + /// or found using . + /// + public static readonly IssuerValidationFailure IssuerDidNotMatch = new IssuerDidNotMatchType("IssuerDidNotMatch"); + private class IssuerDidNotMatchType : IssuerValidationFailure { internal IssuerDidNotMatchType(string name) : base(name) { } } /// - /// No issuer was found in the security token. + /// No issuer was found in the security token or the issuer was an empty string. /// public static readonly IssuerValidationFailure NoIssuerInToken = new NoIssuerInTokenType("NoIssuerInToken"); private class NoIssuerInTokenType : IssuerValidationFailure { internal NoIssuerInTokenType(string name) : base(name) { } } @@ -221,8 +187,8 @@ private class NoIssuerInTokenType : IssuerValidationFailure { internal NoIssuerI /// /// No issuers were found in . /// - public static readonly IssuerValidationFailure NoValidationParameterIssuersProvided = new NoValidationParameterIssuersProvidedType("NoValidationParameterIssuersProvided"); - private class NoValidationParameterIssuersProvidedType : IssuerValidationFailure { internal NoValidationParameterIssuersProvidedType(string name) : base(name) { } } + public static readonly IssuerValidationFailure NoIssuersProvided = new NoIssuersProvidedType("NoIssuersProvided"); + private class NoIssuersProvidedType : IssuerValidationFailure { internal NoIssuersProvidedType(string name) : base(name) { } } } /// @@ -278,6 +244,47 @@ private class LifetimeExpiredType : LifetimeValidationFailure { internal Lifetim private class IssuedInFutureType : LifetimeValidationFailure { internal IssuedInFutureType(string name) : base(name) { } } } + /// + /// Failures that occur during validation of a the signature key. + /// + public abstract class SignatureKeyValidationFailure : ValidationFailureType + { + /// + /// Creates an instance of + /// + protected SignatureKeyValidationFailure(string name) : base(name) { } + + /// + /// Signature key validation failed. + /// + public static readonly SignatureKeyValidationFailure ValidationFailed = new SignatureKeyValidationType("SignatureKeyValidationFailed"); + private class SignatureKeyValidationType : SignatureKeyValidationFailure { internal SignatureKeyValidationType(string name) : base(name) { } } + + /// + /// The signature key validator delegate threw an exception. + /// + public static readonly SignatureKeyValidationFailure ValidatorThrew = new SignatureKeyValidatorThrew("SignatureKeyValidatorThrew"); + private class SignatureKeyValidatorThrew : SignatureKeyValidationFailure { internal SignatureKeyValidatorThrew(string name) : base(name) { } } + + /// + /// Signature key was not yet valid. + /// + public static readonly SignatureKeyValidationFailure NotYetValid = new NotYetValidType("NotYetValid"); + private class NotYetValidType : SignatureKeyValidationFailure { internal NotYetValidType(string name) : base(name) { } } + + /// + /// Signature key was expired. + /// + public static readonly SignatureKeyValidationFailure KeyExpired = new SigningKeyExpiredType("KeyExpired"); + private class SigningKeyExpiredType : SignatureKeyValidationFailure { internal SigningKeyExpiredType(string name) : base(name) { } } + + /// + /// Signature key is null. + /// + public static readonly SignatureKeyValidationFailure KeyIsNull = new SigningKeyIsNullType("KeyIsNull"); + private class SigningKeyIsNullType : SignatureKeyValidationFailure { internal SigningKeyIsNullType(string name) : base(name) { } } + } + /// /// Failures that occur during validation of a signature. /// @@ -306,12 +313,6 @@ private class SignatureValidatorThrewType : SignatureValidationFailure { interna public static readonly SignatureValidationFailure TokenIsNotSigned = new TokenIsNotSignedType("TokenIsNotSigned"); private class TokenIsNotSignedType : SignatureValidationFailure { internal TokenIsNotSignedType(string name) : base(name) { } } - /// - /// The token's signature algorithm validation failed. - /// - public static readonly SignatureValidationFailure AlgorithmValidationFailed = new AlgorithmValidationFailedType("AlgorithmValidationFailed"); - private class AlgorithmValidationFailedType : SignatureValidationFailure { internal AlgorithmValidationFailedType(string name) : base(name) { } } - /// /// Signing key not found. /// @@ -319,7 +320,7 @@ private class AlgorithmValidationFailedType : SignatureValidationFailure { inter private class SignatureKeyNotFoundType : SignatureValidationFailure { internal SignatureKeyNotFoundType(string name) : base(name) { } } /// - /// Defines a type that represents that an XML validation failed. + /// An XML token reference digest validation failed. /// public static readonly SignatureValidationFailure ReferenceDigestValidationFailed = new ReferenceDigestValidationFailedType("ReferenceDigestValidationFailed"); private class ReferenceDigestValidationFailedType : SignatureValidationFailure { internal ReferenceDigestValidationFailedType(string name) : base(name) { } } diff --git a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.Algorithm.cs b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.Algorithm.cs index d590d35dd4..e7fed112ae 100644 --- a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.Algorithm.cs +++ b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.Algorithm.cs @@ -109,7 +109,7 @@ public static ValidationResult ValidateAlgorithm( new MessageDetail( LogMessages.IDX10696, LogHelper.MarkAsNonPII(algorithm)), - AlgorithmValidationFailure.ValidationFailed, + AlgorithmValidationFailure.NotSupported, ValidationError.GetCurrentStackFrame(), algorithm, null); diff --git a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.Audience.cs b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.Audience.cs index 5afd666b5e..bc682112db 100644 --- a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.Audience.cs +++ b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.Audience.cs @@ -25,11 +25,9 @@ public static partial class Validators /// An EXACT match is required. internal static ValidationResult ValidateAudienceInternal( IList audiences, -#pragma warning disable CA1801 SecurityToken? securityToken, ValidationParameters validationParameters, CallContext callContext) -#pragma warning restore CA1801 { if (validationParameters == null) { @@ -109,7 +107,7 @@ public static ValidationResult ValidateAudience( { return new AudienceValidationError( new MessageDetail(LogMessages.IDX10268), - AudienceValidationFailure.NoValidationParameterAudiencesProvided, + AudienceValidationFailure.NoAudiencesProvided, ValidationError.GetCurrentStackFrame(), tokenAudiences, validationParameters.ValidAudiences); @@ -130,7 +128,7 @@ public static ValidationResult ValidateAudience( LogMessages.IDX10215, LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(tokenAudiences)), LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidAudiences))), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, ValidationError.GetCurrentStackFrame(), tokenAudiences, validationParameters.ValidAudiences); @@ -138,7 +136,7 @@ public static ValidationResult ValidateAudience( return new AudienceValidationError( new MessageDetail( LogMessages.IDX10215S), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, ValidationError.GetCurrentStackFrame(), null, null); diff --git a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.Issuer.cs b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.Issuer.cs index 0f5700959b..fc7c2f5638 100644 --- a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.Issuer.cs +++ b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.Issuer.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.IdentityModel.Logging; @@ -32,13 +33,21 @@ internal static async Task> V CallContext callContext, CancellationToken cancellationToken) { + StackFrame? stackFrame; + if (validationParameters == null) + { + string key = ValidationError.GetStackFrameKey(memberName: "ValidateIssuerInternalAsync"); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return new IssuerValidationError( MessageDetail.NullParameter(nameof(validationParameters)), ValidationFailureType.NullArgument, - ValidationError.GetCurrentStackFrame(), + stackFrame!, issuer, null); + } try { @@ -51,7 +60,13 @@ await validationParameters.IssuerValidatorAsync( cancellationToken).ConfigureAwait(false); if (!result.Succeeded) - return result.Error!.AddCurrentStackFrame(); + { + string key = ValidationError.GetStackFrameKey(memberName: "ValidateIssuerInternalAsync"); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + + return result.Error!.AddStackFrame(stackFrame!); + } return result; } @@ -59,10 +74,14 @@ await validationParameters.IssuerValidatorAsync( catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { + string key = ValidationError.GetStackFrameKey(memberName: "ValidateIssuerInternalAsync"); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return new IssuerValidationError( new MessageDetail(LogMessages.IDX10269), IssuerValidationFailure.ValidatorThrew, - ValidationError.GetCurrentStackFrame(), + stackFrame!, issuer, ex); } @@ -87,23 +106,35 @@ public static async Task> Val #pragma warning restore CA1801 // Review unused parameters CancellationToken cancellationToken) { + StackFrame? stackFrame; + string key = string.Empty; if (string.IsNullOrWhiteSpace(issuer)) { + key = ValidationError.GetStackFrameKey(memberName: "ValidateIssuerInternalAsync"); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return new IssuerValidationError( new MessageDetail(LogMessages.IDX10211), IssuerValidationFailure.NoIssuerInToken, - ValidationError.GetCurrentStackFrame(), + stackFrame!, issuer, null); } if (validationParameters == null) + { + key = ValidationError.GetStackFrameKey(memberName: "ValidateIssuerInternalAsync"); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return new IssuerValidationError( MessageDetail.NullParameter(nameof(validationParameters)), ValidationFailureType.NullArgument, - ValidationError.GetCurrentStackFrame(), + stackFrame!, issuer, null); + } BaseConfiguration? configuration = null; if (validationParameters.ConfigurationManager != null) @@ -111,16 +142,21 @@ public static async Task> Val // Return failed IssuerValidationResult if all possible places to validate against are null or empty. if (validationParameters.ValidIssuers.Count == 0 && string.IsNullOrWhiteSpace(configuration?.Issuer)) + { + key = ValidationError.GetStackFrameKey(memberName: "ValidateIssuerInternalAsync"); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return new IssuerValidationError( new MessageDetail( LogMessages.IDX10212, LogHelper.MarkAsNonPII(issuer), - "ValdIssuers is empty", - LogHelper.MarkAsNonPII(configuration?.Issuer) - ), - IssuerValidationFailure.NoValidationParameterIssuersProvided, - ValidationError.GetCurrentStackFrame(), + "ValidIssuers is empty", + LogHelper.MarkAsNonPII(configuration?.Issuer)), + IssuerValidationFailure.NoIssuersProvided, + stackFrame!, issuer); + } if (configuration != null) { @@ -158,14 +194,18 @@ public static async Task> Val } } + key = ValidationError.GetStackFrameKey(memberName: "ValidateIssuerInternalAsync"); + if (!ValidationError.TryGetStackFrame(key, out stackFrame)) + stackFrame = ValidationError.GetAsyncStackFrame(key, new StackFrame(0, true)); + return new IssuerValidationError( new MessageDetail( LogMessages.IDX10212, LogHelper.MarkAsNonPII(issuer), LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidIssuers)), LogHelper.MarkAsNonPII(configuration?.Issuer)), - IssuerValidationFailure.ValidationFailed, - ValidationError.GetCurrentStackFrame(), + IssuerValidationFailure.IssuerDidNotMatch, + stackFrame!, issuer); } } diff --git a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.SignatureKey.cs b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.SignatureKey.cs index b16308fb20..c3aadde795 100644 --- a/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.SignatureKey.cs +++ b/src/Microsoft.IdentityModel.Tokens/Experimental/Validation/Validators.SignatureKey.cs @@ -79,7 +79,6 @@ public static ValidationResult ValidateS nameof(validationParameters), ValidationError.GetCurrentStackFrame()); - // TODO error message IDX10253 is not correct and needs adjusting. if (securityKey == null) return new SignatureKeyValidationError( new MessageDetail(LogMessages.IDX10253, nameof(securityKey)), diff --git a/src/Microsoft.IdentityModel.Tokens/Microsoft.IdentityModel.Tokens.csproj b/src/Microsoft.IdentityModel.Tokens/Microsoft.IdentityModel.Tokens.csproj index 25197b6b90..059c7cbd47 100644 --- a/src/Microsoft.IdentityModel.Tokens/Microsoft.IdentityModel.Tokens.csproj +++ b/src/Microsoft.IdentityModel.Tokens/Microsoft.IdentityModel.Tokens.csproj @@ -30,6 +30,10 @@ $(DefineConstants);DEBUG + + $(NoWarn);nullable + + True diff --git a/src/Microsoft.IdentityModel.Tokens/PublicAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Tokens/PublicAPI.Unshipped.txt index 0fe36c2ef5..ab071d83b8 100644 --- a/src/Microsoft.IdentityModel.Tokens/PublicAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.Tokens/PublicAPI.Unshipped.txt @@ -105,13 +105,11 @@ Microsoft.IdentityModel.Tokens.Experimental.ValidatedTokenType.ValidatedTokenTyp Microsoft.IdentityModel.Tokens.Experimental.ValidatedTokenType.ValidatedTokenType(string! type, int validTypeCount) -> void Microsoft.IdentityModel.Tokens.Experimental.ValidatedTokenType.ValidTypeCount.get -> int Microsoft.IdentityModel.Tokens.Experimental.ValidationError -Microsoft.IdentityModel.Tokens.Experimental.ValidationError.AddCurrentStackFrame(string! filePath = "", int lineNumber = 0, int skipFrames = 1) -> Microsoft.IdentityModel.Tokens.Experimental.ValidationError! -Microsoft.IdentityModel.Tokens.Experimental.ValidationError.AddStackFrame(System.Diagnostics.StackFrame! stackFrame) -> Microsoft.IdentityModel.Tokens.Experimental.ValidationError! Microsoft.IdentityModel.Tokens.Experimental.ValidationError.Exception.get -> System.Exception? Microsoft.IdentityModel.Tokens.Experimental.ValidationError.Exception.set -> void Microsoft.IdentityModel.Tokens.Experimental.ValidationError.FailureType.get -> Microsoft.IdentityModel.Tokens.Experimental.ValidationFailureType! Microsoft.IdentityModel.Tokens.Experimental.ValidationError.InnerException.get -> System.Exception? -Microsoft.IdentityModel.Tokens.Experimental.ValidationError.Message.get -> string! +Microsoft.IdentityModel.Tokens.Experimental.ValidationError.MessageDetail.get -> Microsoft.IdentityModel.Tokens.Experimental.MessageDetail! Microsoft.IdentityModel.Tokens.Experimental.ValidationError.StackFrames.get -> System.Collections.Generic.IList! Microsoft.IdentityModel.Tokens.Experimental.ValidationError.ValidationError(Microsoft.IdentityModel.Tokens.Experimental.MessageDetail! messageDetail, Microsoft.IdentityModel.Tokens.Experimental.ValidationFailureType! validationFailure, System.Diagnostics.StackFrame! stackFrame) -> void Microsoft.IdentityModel.Tokens.Experimental.ValidationError.ValidationError(Microsoft.IdentityModel.Tokens.Experimental.MessageDetail! messageDetail, Microsoft.IdentityModel.Tokens.Experimental.ValidationFailureType! validationFailure, System.Diagnostics.StackFrame! stackFrame, System.Exception? innerException) -> void @@ -224,17 +222,17 @@ static Microsoft.IdentityModel.Tokens.Experimental.ValidatedSignatureKey.operato static Microsoft.IdentityModel.Tokens.Experimental.ValidatedSignatureKey.operator ==(Microsoft.IdentityModel.Tokens.Experimental.ValidatedSignatureKey left, Microsoft.IdentityModel.Tokens.Experimental.ValidatedSignatureKey right) -> bool static Microsoft.IdentityModel.Tokens.Experimental.ValidatedTokenType.operator !=(Microsoft.IdentityModel.Tokens.Experimental.ValidatedTokenType left, Microsoft.IdentityModel.Tokens.Experimental.ValidatedTokenType right) -> bool static Microsoft.IdentityModel.Tokens.Experimental.ValidatedTokenType.operator ==(Microsoft.IdentityModel.Tokens.Experimental.ValidatedTokenType left, Microsoft.IdentityModel.Tokens.Experimental.ValidatedTokenType right) -> bool -static Microsoft.IdentityModel.Tokens.Experimental.ValidationError.GetCurrentStackFrame(string! filePath = "", int lineNumber = 0, int skipFrames = 1) -> System.Diagnostics.StackFrame! static Microsoft.IdentityModel.Tokens.Experimental.ValidationError.NullParameter(string! parameterName, System.Diagnostics.StackFrame! stackFrame) -> Microsoft.IdentityModel.Tokens.Experimental.ValidationError! -static readonly Microsoft.IdentityModel.Tokens.Experimental.AlgorithmValidationFailure.AlgorithmIsNotSupported -> Microsoft.IdentityModel.Tokens.Experimental.AlgorithmValidationFailure! +static readonly Microsoft.IdentityModel.Tokens.Experimental.AlgorithmValidationFailure.NotSupported -> Microsoft.IdentityModel.Tokens.Experimental.AlgorithmValidationFailure! static readonly Microsoft.IdentityModel.Tokens.Experimental.AlgorithmValidationFailure.ValidationFailed -> Microsoft.IdentityModel.Tokens.Experimental.AlgorithmValidationFailure! static readonly Microsoft.IdentityModel.Tokens.Experimental.AlgorithmValidationFailure.ValidatorThrew -> Microsoft.IdentityModel.Tokens.Experimental.AlgorithmValidationFailure! -static readonly Microsoft.IdentityModel.Tokens.Experimental.AudienceValidationFailure.AudienceDidNotMatch -> Microsoft.IdentityModel.Tokens.Experimental.AudienceValidationFailure! +static readonly Microsoft.IdentityModel.Tokens.Experimental.AudienceValidationFailure.DidNotMatch -> Microsoft.IdentityModel.Tokens.Experimental.AudienceValidationFailure! static readonly Microsoft.IdentityModel.Tokens.Experimental.AudienceValidationFailure.NoAudienceInToken -> Microsoft.IdentityModel.Tokens.Experimental.AudienceValidationFailure! -static readonly Microsoft.IdentityModel.Tokens.Experimental.AudienceValidationFailure.NoValidationParameterAudiencesProvided -> Microsoft.IdentityModel.Tokens.Experimental.AudienceValidationFailure! +static readonly Microsoft.IdentityModel.Tokens.Experimental.AudienceValidationFailure.NoAudiencesProvided -> Microsoft.IdentityModel.Tokens.Experimental.AudienceValidationFailure! static readonly Microsoft.IdentityModel.Tokens.Experimental.AudienceValidationFailure.ValidatorThrew -> Microsoft.IdentityModel.Tokens.Experimental.AudienceValidationFailure! +static readonly Microsoft.IdentityModel.Tokens.Experimental.IssuerValidationFailure.IssuerDidNotMatch -> Microsoft.IdentityModel.Tokens.Experimental.IssuerValidationFailure! static readonly Microsoft.IdentityModel.Tokens.Experimental.IssuerValidationFailure.NoIssuerInToken -> Microsoft.IdentityModel.Tokens.Experimental.IssuerValidationFailure! -static readonly Microsoft.IdentityModel.Tokens.Experimental.IssuerValidationFailure.NoValidationParameterIssuersProvided -> Microsoft.IdentityModel.Tokens.Experimental.IssuerValidationFailure! +static readonly Microsoft.IdentityModel.Tokens.Experimental.IssuerValidationFailure.NoIssuersProvided -> Microsoft.IdentityModel.Tokens.Experimental.IssuerValidationFailure! static readonly Microsoft.IdentityModel.Tokens.Experimental.IssuerValidationFailure.ValidationFailed -> Microsoft.IdentityModel.Tokens.Experimental.IssuerValidationFailure! static readonly Microsoft.IdentityModel.Tokens.Experimental.IssuerValidationFailure.ValidatorThrew -> Microsoft.IdentityModel.Tokens.Experimental.IssuerValidationFailure! static readonly Microsoft.IdentityModel.Tokens.Experimental.IssuerValidationSource.IssuerMatchedConfiguration -> Microsoft.IdentityModel.Tokens.Experimental.IssuerValidationSource! @@ -252,7 +250,6 @@ static readonly Microsoft.IdentityModel.Tokens.Experimental.SignatureKeyValidati static readonly Microsoft.IdentityModel.Tokens.Experimental.SignatureKeyValidationFailure.NotYetValid -> Microsoft.IdentityModel.Tokens.Experimental.SignatureKeyValidationFailure! static readonly Microsoft.IdentityModel.Tokens.Experimental.SignatureKeyValidationFailure.ValidationFailed -> Microsoft.IdentityModel.Tokens.Experimental.SignatureKeyValidationFailure! static readonly Microsoft.IdentityModel.Tokens.Experimental.SignatureKeyValidationFailure.ValidatorThrew -> Microsoft.IdentityModel.Tokens.Experimental.SignatureKeyValidationFailure! -static readonly Microsoft.IdentityModel.Tokens.Experimental.SignatureValidationFailure.AlgorithmValidationFailed -> Microsoft.IdentityModel.Tokens.Experimental.SignatureValidationFailure! static readonly Microsoft.IdentityModel.Tokens.Experimental.SignatureValidationFailure.ReferenceDigestValidationFailed -> Microsoft.IdentityModel.Tokens.Experimental.SignatureValidationFailure! static readonly Microsoft.IdentityModel.Tokens.Experimental.SignatureValidationFailure.SigningKeyNotFound -> Microsoft.IdentityModel.Tokens.Experimental.SignatureValidationFailure! static readonly Microsoft.IdentityModel.Tokens.Experimental.SignatureValidationFailure.TokenIsNotSigned -> Microsoft.IdentityModel.Tokens.Experimental.SignatureValidationFailure! @@ -323,3 +320,4 @@ static Microsoft.IdentityModel.Tokens.Validators.ValidateTokenType(string? type, ~override Microsoft.IdentityModel.Tokens.Experimental.ValidationResult.Equals(object obj) -> bool ~Microsoft.IdentityModel.Tokens.Experimental.ValidationResult.Equals(Microsoft.IdentityModel.Tokens.Experimental.ValidationResult other) -> bool ~static Microsoft.IdentityModel.Tokens.Experimental.ValidationResult.operator ==(Microsoft.IdentityModel.Tokens.Experimental.ValidationResult left, Microsoft.IdentityModel.Tokens.Experimental.ValidationResult right) -> bool +Microsoft.IdentityModel.Tokens.Experimental.ValidationError.AddStackFrame(System.Diagnostics.StackFrame! stackFrame) -> Microsoft.IdentityModel.Tokens.Experimental.ValidationError! diff --git a/src/Microsoft.IdentityModel.Tokens/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Tokens/PublicAPI/net8.0/PublicAPI.Unshipped.txt index ddb7553f60..adef10032b 100644 --- a/src/Microsoft.IdentityModel.Tokens/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.Tokens/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -1 +1 @@ -~Microsoft.IdentityModel.Tokens.CaseSensitiveClaimsIdentity.SecurityToken.set -> void \ No newline at end of file +~Microsoft.IdentityModel.Tokens.CaseSensitiveClaimsIdentity.SecurityToken.set -> void diff --git a/src/Microsoft.IdentityModel.Xml/Experimental/Signature.Experimental.cs b/src/Microsoft.IdentityModel.Xml/Experimental/Signature.Experimental.cs index 7c8f6173b1..bcf02d0dd3 100644 --- a/src/Microsoft.IdentityModel.Xml/Experimental/Signature.Experimental.cs +++ b/src/Microsoft.IdentityModel.Xml/Experimental/Signature.Experimental.cs @@ -41,7 +41,7 @@ public partial class Signature : DSigElement if (!cryptoProviderFactory.IsSupportedAlgorithm(SignedInfo.SignatureMethod, key)) return new SignatureValidationError( new MessageDetail(LogMessages.IDX30207, SignedInfo.SignatureMethod, cryptoProviderFactory.GetType()), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, ValidationError.GetCurrentStackFrame()); var signatureProvider = cryptoProviderFactory.CreateForVerifying(key, SignedInfo.SignatureMethod); @@ -55,7 +55,6 @@ public partial class Signature : DSigElement try { - // TODO - follow JsonWebTokenHandler for IDX10511 parameters. using (var memoryStream = new MemoryStream()) { SignedInfo.GetCanonicalBytes(memoryStream); diff --git a/src/Microsoft.IdentityModel.Xml/Experimental/SignedInfo.Experimental.cs b/src/Microsoft.IdentityModel.Xml/Experimental/SignedInfo.Experimental.cs index 36df68ee15..af24baea63 100644 --- a/src/Microsoft.IdentityModel.Xml/Experimental/SignedInfo.Experimental.cs +++ b/src/Microsoft.IdentityModel.Xml/Experimental/SignedInfo.Experimental.cs @@ -21,7 +21,6 @@ public partial class SignedInfo : DSigElement CryptoProviderFactory cryptoProviderFactory, CallContext callContext) { - // TODO needs to return ValidationResult if (cryptoProviderFactory == null) return ValidationError.NullParameter( nameof(cryptoProviderFactory), diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.DecryptTokenTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.DecryptTokenTests.cs deleted file mode 100644 index f39bcd1d48..0000000000 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.DecryptTokenTests.cs +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.IdentityModel.Tokens.Jwt.Tests; -using Microsoft.IdentityModel.Logging; -using Microsoft.IdentityModel.Protocols; -using Microsoft.IdentityModel.TestUtils; -using Microsoft.IdentityModel.Tokens; -using Microsoft.IdentityModel.Tokens.Experimental; -using Xunit; - -#if NET472_OR_GREATER || NET6_0_OR_GREATER -using Newtonsoft.Json.Linq; -using System.Collections.Generic; -#endif -using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; - -namespace Microsoft.IdentityModel.JsonWebTokens.Tests -{ - public class JsonWebTokenHandlerDecryptTokenTests - { - [Theory, MemberData(nameof(JsonWebTokenHandlerDecryptTokenTestCases), DisableDiscoveryEnumeration = false)] - public void DecryptToken(TokenDecryptingTheoryData theoryData) - { - JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); - if (theoryData.Token == null) - { - string tokenString = null; - if (theoryData.SecurityTokenDescriptor != null) - tokenString = jsonWebTokenHandler.CreateToken(theoryData.SecurityTokenDescriptor); - else - tokenString = theoryData.TokenString; - - if (tokenString != null) - theoryData.Token = new JsonWebToken(tokenString); - } - - CompareContext context = TestUtilities.WriteHeader($"{this}.JsonWebTokenHandlerDecryptTokenTests", theoryData); - ValidationResult validationResult = jsonWebTokenHandler.DecryptToken( - theoryData.Token, - theoryData.ValidationParameters, - theoryData.Configuration, - theoryData.CallContext); - - if (validationResult.Succeeded) - { - IdentityComparer.AreStringsEqual( - validationResult.Result, - theoryData.OperationResult.Result, - context); - - theoryData.ExpectedException.ProcessNoException(context); - } - else - { - ValidationError validationError = validationResult.Error; - IdentityComparer.AreStringsEqual( - validationError.FailureType.Name, - theoryData.OperationResult.Error.FailureType.Name, - context); - - Exception exception = validationError.GetException(); - theoryData.ExpectedException.ProcessException(exception, context); - } - - TestUtilities.AssertFailIfErrors(context); - } - - [Fact] - public void DecryptToken_ThrowsIfAccessingSecurityTokenOnFailedRead() - { - JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); - ValidationResult tokenDecryptionResult = jsonWebTokenHandler.DecryptToken( - null, - null, - null, - new CallContext()); - - // TODO what did this test do? - } - - public static TheoryData JsonWebTokenHandlerDecryptTokenTestCases - { - get - { - var validToken = EncodedJwts.LiveJwt; - var token = new JsonWebToken(validToken); -#if NET472 || NET6_0_OR_GREATER - var ecdsaEncryptingCredentials = new EncryptingCredentials( - new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true), - SecurityAlgorithms.EcdhEsA256kw, - SecurityAlgorithms.Aes128CbcHmacSha256) - { - KeyExchangePublicKey = KeyingMaterial.JsonWebKeyP256_Public - }; - var ecdsaTokenDescriptor = new SecurityTokenDescriptor - { - EncryptingCredentials = ecdsaEncryptingCredentials, - Expires = DateTime.MaxValue, - NotBefore = DateTime.MinValue, - IssuedAt = DateTime.MinValue, - AdditionalHeaderClaims = AdditionalEcdhEsHeaderParameters(KeyingMaterial.JsonWebKeyP256_Public), - }; - - var jsonWebTokenHandler = new JsonWebTokenHandler(); - var ecdsaToken = new JsonWebToken(jsonWebTokenHandler.CreateToken(ecdsaTokenDescriptor)); - - static Dictionary AdditionalEcdhEsHeaderParameters(JsonWebKey publicKeySender) - { - var epkJObject = new JObject(); - epkJObject.Add(JsonWebKeyParameterNames.Kty, publicKeySender.Kty); - epkJObject.Add(JsonWebKeyParameterNames.Crv, publicKeySender.Crv); - epkJObject.Add(JsonWebKeyParameterNames.X, publicKeySender.X); - epkJObject.Add(JsonWebKeyParameterNames.Y, publicKeySender.Y); - - Dictionary additionalHeaderParams = new Dictionary() - { - { JsonWebTokens.JwtHeaderParameterNames.Apu, Guid.NewGuid().ToString() }, - { JsonWebTokens.JwtHeaderParameterNames.Apv, Guid.NewGuid().ToString() }, - { JsonWebTokens.JwtHeaderParameterNames.Epk, epkJObject.ToString(Newtonsoft.Json.Formatting.None) } - }; - - return additionalHeaderParams; - } -#endif - var rsaKey = new RsaSecurityKey(KeyingMaterial.RsaParameters_2048) { KeyId = "CustomRsaSecurityKey_2048" }; - var configurationThatThrows = CreateCustomConfigurationThatThrows(rsaKey); - - var configurationWithMismatchedKeys = new CustomConfiguration(rsaKey); - - return new TheoryData - { - new TokenDecryptingTheoryData("Invalid_TokenIsNotEncrypted") - { - Token = token, - ValidationParameters = new ValidationParameters(), - ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10612:"), - OperationResult = new ValidationError( - new MessageDetail(TokenLogMessages.IDX10612), - ValidationFailureType.TokenDecryptionFailed, - null), - }, - new TokenDecryptingTheoryData("Invalid_SecurityTokenIsNull") - { - Token = null, - ValidationParameters = new ValidationParameters(), - ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - OperationResult = new ValidationError( - new MessageDetail(TokenLogMessages.IDX10000, "jwtToken"), - ValidationFailureType.NullArgument, - null), - }, - new TokenDecryptingTheoryData("Invalid_ValidationParametersIsNull") - { - Token = token, - ValidationParameters = null, - ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - OperationResult = new ValidationError( - new MessageDetail(TokenLogMessages.IDX10000, "validationParameters"), - ValidationFailureType.NullArgument, - null), - }, - new TokenDecryptingTheoryData("Valid_Aes128_FromValidationParameters") - { - TokenString = ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims, - ValidationParameters = ValidationUtils.CreateValidationParameters( - decryptionKeys: [Default.SymmetricEncryptingCredentials.Key]), - OperationResult = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.", - }, - new TokenDecryptingTheoryData("Valid_Aes128_FromKeyResolver") - { - TokenString = ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims, - ValidationParameters = new ValidationParameters - { - DecryptionKeyResolver = (tokenString, token, kid, validationParameters, callContext) => [Default.SymmetricEncryptingCredentials.Key] - }, - OperationResult = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.", - }, - new TokenDecryptingTheoryData("Valid_Aes128_FromConfiguration") - { - TokenString = ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims, - ValidationParameters = new ValidationParameters - { - ConfigurationManager = new StaticConfigurationManager(new CustomConfiguration(Default.SymmetricEncryptingCredentials.Key)) - }, - Configuration = new CustomConfiguration(Default.SymmetricEncryptingCredentials.Key), - OperationResult = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.", - }, -#if NET472 || NET6_0_OR_GREATER - new TokenDecryptingTheoryData("Valid_Ecdsa256_FromValidationParameters") - { - Token = ecdsaToken, - ValidationParameters = ValidationUtils.CreateValidationParameters( - decryptionKeys: [new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true)] - ), - OperationResult = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJleHAiOjI1MzQwMjMwMDgwMCwiaWF0IjowLCJuYmYiOjB9." - }, -#endif - new TokenDecryptingTheoryData("Invalid_NoKeysProvided") - { - TokenString = ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims, - ValidationParameters = new ValidationParameters(), - ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10609:"), - OperationResult = new ValidationError( - new MessageDetail( - TokenLogMessages.IDX10609, - LogHelper.MarkAsSecurityArtifact( - new JsonWebToken(ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims), - JwtTokenUtilities.SafeLogJwtToken)), - ValidationFailureType.TokenDecryptionFailed, - null), - }, - new TokenDecryptingTheoryData("Valid_OneKeyThrowsOnUnwrap_DecryptionSucceeds") - { - SecurityTokenDescriptor = new SecurityTokenDescriptor - { - SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, - EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256), - Claims = Default.PayloadDictionary - }, - ValidationParameters = new ValidationParameters - { - ConfigurationManager = new StaticConfigurationManager(configurationThatThrows) - }, - Configuration = configurationThatThrows, - OperationResult = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikpzb25XZWJLZXlSc2FfMjA0OCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwOi8vRGVmYXVsdC5BdWRpZW5jZS5jb20iLCJhenAiOiJodHRwOi8vRGVmYXVsdC5BenAuY29tIiwiZW1haWwiOiJCb2JAY29udG9zby5jb20iLCJleHAiOiIyNTM0MDIzMDA3OTkiLCJnaXZlbl9uYW1lIjoiQm9iIiwiaXNzIjoiaHR0cDovL0RlZmF1bHQuSXNzdWVyLmNvbSIsImlhdCI6IjE0ODk3NzU2MTciLCJqdGkiOiJKdGkiLCJuYmYiOiIxNDg5Nzc1NjE3In0.Et69LAC4sn6nNm_HNz_AnJ8siLT6LRTjDSb1aY8APcwJmPn-TxU-8GG5_bmNkoVukR7hkYG2JuWPxJKbjDd73BlmelaiyZBoPUyU0S-GX3XgyC2v_CkOq4yYbtD-kq5s7kNNj5QJjZDq0oJeqcUMrq4xRWATPtUMkIZ0GpEhO_C5MFxT8jAWe_a2gyUA4KoibalKtkYgFvgLcvyZJhUx7AERbli6b7OkUksFp9zIwmc_jZZCXJ_F_wASyj9KgHQKN9VHER3bB2zQeWHR0q32ODYC4ggsan-Nkm-jIsATi2tgkKzROzK55dy8ZdFArXUYJRpI_raYkTUHRK_wP3GqtQ", - }, - new TokenDecryptingTheoryData("Invalid_AlgorithmMismatch_DecryptionFails") - { - ExpectedException = ExpectedException.SecurityTokenKeyWrapException("IDX10618:"), - SecurityTokenDescriptor = new SecurityTokenDescriptor - { - SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, - EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.DefaultX509Key_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256), - Claims = Default.PayloadDictionary - }, - ValidationParameters = ValidationUtils.CreateValidationParameters( - decryptionKeys: [KeyingMaterial.RsaSecurityKey_2048]), - OperationResult = new ValidationError( - new MessageDetail( - TokenLogMessages.IDX10609, - LogHelper.MarkAsSecurityArtifact( - new JsonWebToken(ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims), - JwtTokenUtilities.SafeLogJwtToken)), - ValidationFailureType.KeyWrapFailed, - null), - }, - new TokenDecryptingTheoryData("KeyIdMismatch_TryAllDecryptionKeysTrue_DecryptionSucceeds") - { - SecurityTokenDescriptor = new SecurityTokenDescriptor - { - SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, - EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256), - Claims = Default.PayloadDictionary - }, - ValidationParameters = new ValidationParameters // TryAllDecryptionKeys is true by default - { - ConfigurationManager = new StaticConfigurationManager(configurationWithMismatchedKeys) - }, - Configuration = configurationWithMismatchedKeys, - OperationResult = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikpzb25XZWJLZXlSc2FfMjA0OCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwOi8vRGVmYXVsdC5BdWRpZW5jZS5jb20iLCJhenAiOiJodHRwOi8vRGVmYXVsdC5BenAuY29tIiwiZW1haWwiOiJCb2JAY29udG9zby5jb20iLCJleHAiOiIyNTM0MDIzMDA3OTkiLCJnaXZlbl9uYW1lIjoiQm9iIiwiaXNzIjoiaHR0cDovL0RlZmF1bHQuSXNzdWVyLmNvbSIsImlhdCI6IjE0ODk3NzU2MTciLCJqdGkiOiJKdGkiLCJuYmYiOiIxNDg5Nzc1NjE3In0.Et69LAC4sn6nNm_HNz_AnJ8siLT6LRTjDSb1aY8APcwJmPn-TxU-8GG5_bmNkoVukR7hkYG2JuWPxJKbjDd73BlmelaiyZBoPUyU0S-GX3XgyC2v_CkOq4yYbtD-kq5s7kNNj5QJjZDq0oJeqcUMrq4xRWATPtUMkIZ0GpEhO_C5MFxT8jAWe_a2gyUA4KoibalKtkYgFvgLcvyZJhUx7AERbli6b7OkUksFp9zIwmc_jZZCXJ_F_wASyj9KgHQKN9VHER3bB2zQeWHR0q32ODYC4ggsan-Nkm-jIsATi2tgkKzROzK55dy8ZdFArXUYJRpI_raYkTUHRK_wP3GqtQ", - }, - new TokenDecryptingTheoryData("KeyIdMismatch_TryAllDecryptionKeysFalse_DecryptionFails") - { - ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10609:"), - SecurityTokenDescriptor = new SecurityTokenDescriptor - { - SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, - EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256), - Claims = Default.PayloadDictionary - }, - ValidationParameters = new ValidationParameters - { - TryAllDecryptionKeys = false, - }, - Configuration = configurationWithMismatchedKeys, - OperationResult = new ValidationError( - new MessageDetail( - TokenLogMessages.IDX10609, - LogHelper.MarkAsSecurityArtifact( - new JsonWebToken(ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims), - JwtTokenUtilities.SafeLogJwtToken)), - ValidationFailureType.TokenDecryptionFailed, - null), - }, - }; - } - } - - private static CustomConfiguration CreateCustomConfigurationThatThrows(SecurityKey rsaKey) - { - var customCryptoProviderFactory = new DerivedCryptoProviderFactory - { - IsSupportedAlgImpl = (alg, key) => true, - CreateKeyWrapProviderForUnwrapImpl = (key, alg) => throw new InvalidOperationException("Test exception") - }; - - var sym512Hey = new SymmetricSecurityKey(KeyingMaterial.DefaultSymmetricKeyBytes_512) { KeyId = "CustomSymmetricSecurityKey_512" }; - sym512Hey.CryptoProviderFactory = customCryptoProviderFactory; - - var configurationWithCustomCryptoProviderFactory = new CustomConfiguration(rsaKey); - configurationWithCustomCryptoProviderFactory.TokenDecryptionKeys.Add(sym512Hey); - - return configurationWithCustomCryptoProviderFactory; - } - } - - public class TokenDecryptingTheoryData : TheoryDataBase - { - public TokenDecryptingTheoryData(string testId) : base(testId) { } - public JsonWebToken Token { get; set; } - internal ValidationResult OperationResult { get; set; } - public BaseConfiguration Configuration { get; internal set; } - public SecurityTokenDescriptor SecurityTokenDescriptor { get; internal set; } - public string TokenString { get; internal set; } - internal ValidationParameters ValidationParameters { get; set; } - } - - public class CustomConfiguration : BaseConfiguration - { - public CustomConfiguration(SecurityKey tokenDecryptionKey) : base() - { - TokenDecryptionKeys.Add(tokenDecryptionKey); - } - } -} diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateSignatureTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateSignatureTests.cs index 9872b5922e..563a10223f 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateSignatureTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateSignatureTests.cs @@ -49,7 +49,7 @@ public void ValidateSignature(JsonWebTokenHandlerValidateSignatureTheoryData the { IdentityComparer.AreSecurityKeysEqual( validationResult.Result, - theoryData.OperationResult.Result, + theoryData.ValidationResult.Result, context); theoryData.ExpectedException.ProcessNoException(context); @@ -59,7 +59,7 @@ public void ValidateSignature(JsonWebTokenHandlerValidateSignatureTheoryData the ValidationError validationError = validationResult.Error; IdentityComparer.AreStringsEqual( validationError.FailureType.Name, - theoryData.OperationResult.Error.FailureType.Name, + theoryData.ValidationResult.Error.FailureType.Name, context); Exception exception = validationError.GetException(); @@ -83,7 +83,7 @@ public static TheoryData JsonWeb { JWT = null, ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - OperationResult = new ValidationError( + ValidationResult = new ValidationError( new MessageDetail( TokenLogMessages.IDX10000, "jwtToken"), @@ -95,7 +95,7 @@ public static TheoryData JsonWeb JWT = new JsonWebToken(EncodedJwts.LiveJwt), ValidationParameters = null, ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - OperationResult = new ValidationError( + ValidationResult = new ValidationError( new MessageDetail( TokenLogMessages.IDX10000, "validationParameters"), @@ -113,7 +113,7 @@ public static TheoryData JsonWeb ValidationError.GetCurrentStackFrame()) }, ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - OperationResult = new ValidationError( + ValidationResult = new ValidationError( new MessageDetail( TokenLogMessages.IDX10000, "NullArgument"), @@ -125,7 +125,7 @@ public static TheoryData JsonWeb JWT = unsignedToken, ValidationParameters = new ValidationParameters(), ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10504:"), - OperationResult = new ValidationError( + ValidationResult = new ValidationError( new MessageDetail( TokenLogMessages.IDX10504, LogHelper.MarkAsSecurityArtifact(unsignedToken, JwtTokenUtilities.SafeLogJwtToken)), @@ -139,21 +139,21 @@ public static TheoryData JsonWeb { SignatureValidator = (token, parameters, configuration, callContext) => KeyingMaterial.JsonWebKeyRsa256PublicSigningCredentials.Key }, - OperationResult = KeyingMaterial.JsonWebKeyRsa256PublicSigningCredentials.Key + ValidationResult = KeyingMaterial.JsonWebKeyRsa256PublicSigningCredentials.Key }, new JsonWebTokenHandlerValidateSignatureTheoryData("Valid_SignatureValidationResult_Success_KidMatches") { SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, ValidationParameters = new ValidationParameters(), KeyToAddToValidationParameters = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, - OperationResult = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, + ValidationResult = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, }, new JsonWebTokenHandlerValidateSignatureTheoryData("Valid_SignatureValidationResult_Success_X5tMatches") { SigningCredentials = KeyingMaterial.X509SigningCreds_1024_RsaSha2_Sha2, ValidationParameters = new ValidationParameters(), KeyToAddToValidationParameters = KeyingMaterial.X509SigningCreds_1024_RsaSha2_Sha2.Key, - OperationResult = KeyingMaterial.X509SigningCreds_1024_RsaSha2_Sha2.Key, + ValidationResult = KeyingMaterial.X509SigningCreds_1024_RsaSha2_Sha2.Key, }, new JsonWebTokenHandlerValidateSignatureTheoryData("Valid_IssuerSigningKeyResolverReturnsKeyThatMatches") { @@ -162,7 +162,7 @@ public static TheoryData JsonWeb { SignatureKeyResolver = (token, securityToken, kid, validationParameters, configuration, callContext) => KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key }, - OperationResult = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key + ValidationResult = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key }, new JsonWebTokenHandlerValidateSignatureTheoryData("Valid_ConfurationReturnsKeyThatMatches") { @@ -170,7 +170,7 @@ public static TheoryData JsonWeb Configuration = new OpenIdConnectConfiguration(), KeyToAddToConfiguration = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, ValidationParameters = new ValidationParameters(), - OperationResult = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, + ValidationResult = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, }, new JsonWebTokenHandlerValidateSignatureTheoryData("Valid_NoKeyId_TryAllKeys") { @@ -180,7 +180,7 @@ public static TheoryData JsonWeb TryAllSigningKeys = true }, KeyToAddToValidationParameters = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2_NoKeyId.Key, - OperationResult = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2_NoKeyId.Key, + ValidationResult = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2_NoKeyId.Key, }, new JsonWebTokenHandlerValidateSignatureTheoryData("Invalid_NoKeyId_DontTryAllKeys") { @@ -188,7 +188,7 @@ public static TheoryData JsonWeb ValidationParameters = new ValidationParameters(), KeyToAddToValidationParameters = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2_NoKeyId.Key, ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10526:"), - OperationResult = new ValidationError( + ValidationResult = new ValidationError( new MessageDetail(TokenLogMessages.IDX10526), SignatureValidationFailure.SigningKeyNotFound, null) @@ -198,7 +198,7 @@ public static TheoryData JsonWeb JWT = new JsonWebToken(EncodedJwts.LiveJwt), ValidationParameters = new ValidationParameters(), ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10527:"), - OperationResult = new ValidationError( + ValidationResult = new ValidationError( new MessageDetail(TokenLogMessages.IDX10500), SignatureValidationFailure.SigningKeyNotFound, null) @@ -216,7 +216,7 @@ public JsonWebTokenHandlerValidateSignatureTheoryData(string testId) : base(test public SigningCredentials SigningCredentials { get; internal set; } public SecurityKey KeyToAddToConfiguration { get; internal set; } public SecurityKey KeyToAddToValidationParameters { get; internal set; } - internal ValidationResult OperationResult { get; set; } + internal ValidationResult ValidationResult { get; set; } internal ValidationParameters ValidationParameters { get; set; } } } diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Common.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Common.cs deleted file mode 100644 index bea8919bed..0000000000 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Common.cs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#nullable enable -using System.Threading.Tasks; -using System.Threading; -using Microsoft.IdentityModel.TestUtils; -using Microsoft.IdentityModel.Tokens; -using Microsoft.IdentityModel.Tokens.Experimental; - -namespace Microsoft.IdentityModel.JsonWebTokens.Tests -{ - public partial class JwtValidationTests - { - internal static async Task ValidateAndCompareResults( - string jwtString, - ValidateTokenTheoryData theoryData, - CompareContext context) - { - JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); - - // Validate the token using TokenValidationParameters - TokenValidationResult legacyTokenValidationParametersResult = - await jsonWebTokenHandler.ValidateTokenAsync(jwtString, theoryData.TokenValidationParameters); - - // Validate the token using ValidationParameters - ValidationResult validationResult = - await jsonWebTokenHandler.ValidateTokenAsync( - jwtString, theoryData.ValidationParameters!, theoryData.CallContext, CancellationToken.None); - - // Ensure the validity of the results match the expected result - if (legacyTokenValidationParametersResult.IsValid != theoryData.ExpectedIsValid) - context.AddDiff($"tokenValidationParametersResult.IsValid != theoryData.ExpectedIsValid"); - - if (validationResult.Succeeded != theoryData.ExpectedIsValid) - context.AddDiff($"validationResult.Succeeded != theoryData.ExpectedIsValid"); - - if (theoryData.ExpectedIsValid && - legacyTokenValidationParametersResult.IsValid && - validationResult.Succeeded) - { - // Compare the ClaimsPrincipal and ClaimsIdentity from one result against the other - IdentityComparer.AreEqual( - legacyTokenValidationParametersResult.ClaimsIdentity, - validationResult.Result!.ClaimsIdentity, - context); - IdentityComparer.AreEqual( - legacyTokenValidationParametersResult.Claims, - validationResult.Result!.Claims, - context); - } - else - { - // Verify the exception provided by the TokenValidationParameters path - theoryData.ExpectedException.ProcessException(legacyTokenValidationParametersResult.Exception, context); - - if (!validationResult.Succeeded) - { - // Verify the exception provided by the ValidationParameters path - if (theoryData.ExpectedExceptionValidationParameters is not null) - { - // If there is a special case for the ValidationParameters path, use that. - theoryData.ExpectedExceptionValidationParameters - .ProcessException(validationResult.Error!.GetException(), context); - } - else - { - theoryData.ExpectedException - .ProcessException(validationResult.Error!.GetException(), context); - - // If the expected exception is the same in both paths, verify the message matches - IdentityComparer.AreStringsEqual( - legacyTokenValidationParametersResult.Exception.Message, - validationResult.Error!.GetException().Message, - context); - } - } - - // Verify that the exceptions are of the same type. - IdentityComparer.AreEqual( - legacyTokenValidationParametersResult.Exception.GetType(), - validationResult.Error!.GetException().GetType(), - context); - - if (legacyTokenValidationParametersResult.Exception is SecurityTokenException) - { - // Verify that the custom properties are the same. - IdentityComparer.AreSecurityTokenExceptionsEqual( - legacyTokenValidationParametersResult.Exception, - validationResult.Error!.GetException(), - context); - } - } - } - - internal async Task RunValidTest(ValidateTokenTheoryData theoryData) - { - var context = TestUtilities.WriteHeader($"{this}.RunValidTest", theoryData); - - // Validate the token using TokenValidationParameters - TokenValidationResult tokenValidationResult = - await theoryData.TokenHandler!.ValidateTokenAsync( - theoryData.Token, - theoryData.TokenValidationParameters); - - // Validate the token using ValidationParameters. - ValidationResult validationResult = - await theoryData.TokenHandler.ValidateTokenAsync( - theoryData.Token, - theoryData.ValidationParameters!, - theoryData.CallContext, - CancellationToken.None); - - // Ensure the validity of the results match the expected result. - if (tokenValidationResult.IsValid != validationResult.Succeeded) - context.AddDiff($"tokenValidationResult.IsValid: '{tokenValidationResult.IsValid}' != OperationResult.Succeeded: '{validationResult.Succeeded}'"); - - if (!validationResult.Succeeded) - context.AddDiff($"Expected test to succeed, test failed: {theoryData.TestId}"); - - ValidatedToken validatedToken = validationResult.Result!; - IdentityComparer.AreEqual(validatedToken.SecurityToken, tokenValidationResult.SecurityToken, context); - - TestUtilities.AssertFailIfErrors(context); - } - } -} -#nullable restore diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Decryption.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Decryption.cs deleted file mode 100644 index 2d6c54066c..0000000000 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Decryption.cs +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#nullable enable -#if NET472 || NET6_0_OR_GREATER -using System; -using Newtonsoft.Json.Linq; -#endif -using System.Collections.Generic; -using System.Threading.Tasks; -using Microsoft.IdentityModel.TestUtils; -using Microsoft.IdentityModel.Tokens; -using Microsoft.IdentityModel.Tokens.Experimental; -using Xunit; -using Microsoft.IdentityModel.Protocols.OpenIdConnect; - -namespace Microsoft.IdentityModel.JsonWebTokens.Tests -{ - public partial class JwtValidationTests - { - [Theory, MemberData(nameof(ValidateTokenAsync_DecryptionTestCases), DisableDiscoveryEnumeration = true)] - public async Task ValidateTokenAsync_Decryption(ValidateTokenAsyncDecryptionTheoryData theoryData) - { - var context = TestUtilities.WriteHeader($"{this}.ValidateTokenAsync_Decryption", theoryData); - - string jwtString = CreateEncryptedToken(theoryData.EncryptingCredentials, theoryData.AdditionalHeaderClaims); - - await ValidateAndCompareResults(jwtString, theoryData, context); - - TestUtilities.AssertFailIfErrors(context); - } - - public static TheoryData ValidateTokenAsync_DecryptionTestCases - { - get - { - var theoryData = new TheoryData - { - new ValidateTokenAsyncDecryptionTheoryData("Valid_JWE_Aes128Cbc_HmacSha256") - { - EncryptingCredentials = new EncryptingCredentials( - KeyingMaterial.DefaultX509Key_2048, - SecurityAlgorithms.RsaPKCS1, - SecurityAlgorithms.Aes128CbcHmacSha256), - TokenValidationParameters = CreateTokenValidationParameters(KeyingMaterial.DefaultX509Key_2048), - ValidationParameters = CreateValidationParameters(KeyingMaterial.DefaultX509Key_2048), - }, -#if NET472 || NET6_0_OR_GREATER - new ValidateTokenAsyncDecryptionTheoryData("Valid_JWE_EcdhEs") - { - EncryptingCredentials = new EncryptingCredentials( - new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true), - SecurityAlgorithms.EcdhEsA256kw, - SecurityAlgorithms.Aes128CbcHmacSha256) - { - KeyExchangePublicKey = KeyingMaterial.JsonWebKeyP521_Public - }, - AdditionalHeaderClaims = AdditionalEcdhEsHeaderParameters(KeyingMaterial.JsonWebKeyP521_Public), - TokenValidationParameters = CreateTokenValidationParameters(new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true)), - ValidationParameters = CreateValidationParameters(new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true)), - }, -#endif - new ValidateTokenAsyncDecryptionTheoryData("Invalid_JWE_NoDecryptionKeys") - { - EncryptingCredentials = new EncryptingCredentials( - KeyingMaterial.DefaultX509Key_2048, - SecurityAlgorithms.RsaPKCS1, - SecurityAlgorithms.Aes128CbcHmacSha256), - TokenValidationParameters = CreateTokenValidationParameters(), - ValidationParameters = CreateValidationParameters(), - ExpectedIsValid = false, - ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10609:"), - ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenDecryptionFailedException("IDX10609:"), - }, - new ValidateTokenAsyncDecryptionTheoryData("Invalid_JWE_WrongDecryptionKey") - { - EncryptingCredentials = new EncryptingCredentials( - KeyingMaterial.DefaultX509Key_2048, - SecurityAlgorithms.RsaPKCS1, - SecurityAlgorithms.Aes128CbcHmacSha256), - TokenValidationParameters = CreateTokenValidationParameters(KeyingMaterial.DefaultRsaSecurityKey1), - ValidationParameters = CreateValidationParameters(KeyingMaterial.DefaultRsaSecurityKey1), - ExpectedIsValid = false, - ExpectedException = ExpectedException.SecurityTokenKeyWrapException("IDX10618:"), - // Avoid comparing the full exception message as the stack traces for the inner exceptions are different. - ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenKeyWrapException("IDX10618:"), - }, - new ValidateTokenAsyncDecryptionTheoryData("JWE_KeyWithKeyId_OnTokenDecryptFailure_KeysInConfig_SuccessOnRetry") - { - EncryptingCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes128_Sha2, - TokenValidationParameters = CreateTokenValidationParameters(configurationManager: CreateConfigurationManager(true)), - ValidationParameters = CreateValidationParameters(configurationManager: CreateConfigurationManager(true)), - ExpectedIsValid = true, - }, - new ValidateTokenAsyncDecryptionTheoryData("JWE_KeyWithoutKeyId_OnTokenDecryptFailure_KeysInConfig_SuccessOnRetry") - { - EncryptingCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes128_Sha2, - TokenValidationParameters = CreateTokenValidationParameters(configurationManager: CreateConfigurationManager(false)), - ValidationParameters = CreateValidationParameters(configurationManager: CreateConfigurationManager(false)), - ExpectedIsValid = true, - }, - new ValidateTokenAsyncDecryptionTheoryData("JWE_KeyWithKeyId_OnTokenDecryptFailure_KeysOnlyInTvp_ThrowsException") - { - EncryptingCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes128_Sha2, - TokenValidationParameters = CreateTokenValidationParameters( - tokenDecryptionKey: KeyingMaterial.DefaultSymmetricSecurityKey_128, - configurationManager: new MockConfigurationManager(new OpenIdConnectConfiguration())), - ValidationParameters = CreateValidationParameters( - tokenDecryptionKey: KeyingMaterial.DefaultSymmetricSecurityKey_128, - configurationManager: new MockConfigurationManager(new OpenIdConnectConfiguration())), - ExpectedIsValid = false, - ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10603:"), - ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenDecryptionFailedException("IDX10603:"), - }, - new ValidateTokenAsyncDecryptionTheoryData("JWE_KeyWithoutKeyId_OnTokenDecryptFailure_KeysOnlyInTvp_ThrowsException") - { - EncryptingCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes128_Sha2, - TokenValidationParameters = CreateTokenValidationParameters( - tokenDecryptionKey: new SymmetricSecurityKey(KeyingMaterial.DefaultSymmetricKeyBytes_128) { KeyId = null }, - configurationManager: new MockConfigurationManager(new OpenIdConnectConfiguration())), - ValidationParameters = CreateValidationParameters( - tokenDecryptionKey: new SymmetricSecurityKey(KeyingMaterial.DefaultSymmetricKeyBytes_128) { KeyId = null }, - configurationManager: new MockConfigurationManager(new OpenIdConnectConfiguration())), - ExpectedIsValid = false, - ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10603:"), - ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenDecryptionFailedException("IDX10603:"), - }, - }; - - return theoryData; - - static TokenValidationParameters CreateTokenValidationParameters( - SecurityKey? tokenDecryptionKey = null, - bool tryAllKeys = false, - BaseConfigurationManager? configurationManager = null) - { - // Skip all validations. We just want to decrypt the JWE. - var tokenValidationParameters = new TokenValidationParameters - { - ConfigurationManager = configurationManager, - ValidateAudience = false, - ValidateIssuer = false, - ValidateLifetime = false, - ValidateTokenReplay = false, - ValidateIssuerSigningKey = false, - RequireSignedTokens = false, - TokenDecryptionKey = tokenDecryptionKey, - }; - - return tokenValidationParameters; - } - - static ValidationParameters CreateValidationParameters( - SecurityKey? tokenDecryptionKey = null, - BaseConfigurationManager? configurationManager = null) - { - ValidationParameters validationParameters = new ValidationParameters(); - - if (tokenDecryptionKey != null) - validationParameters.DecryptionKeys.Add(tokenDecryptionKey); - - validationParameters.ConfigurationManager = configurationManager; - - // Skip all validations. We just want to decrypt the JWE - validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation; - validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; - validationParameters.SignatureKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation; - validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation; - validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation; - validationParameters.SignatureValidator = SkipValidationDelegates.SkipSignatureValidation; - validationParameters.TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation; - validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation; - - return validationParameters; - } - - static BaseConfigurationManager CreateConfigurationManager(bool invalidKeyHasKeyId) - { - var configWrongDecryptKeys = new OpenIdConnectConfiguration(); - if (invalidKeyHasKeyId) - configWrongDecryptKeys.TokenDecryptionKeys.Add(KeyingMaterial.DefaultSymmetricSecurityKey_128); - else - configWrongDecryptKeys.TokenDecryptionKeys.Add(new SymmetricSecurityKey(KeyingMaterial.DefaultSymmetricKeyBytes_128) { KeyId = null }); - - var configWithDecryptKeys = new OpenIdConnectConfiguration(); - configWithDecryptKeys.TokenDecryptionKeys.Add(KeyingMaterial.DefaultSymmetricSecurityKey_256); - - var configManager = new MockConfigurationManager(configWrongDecryptKeys); - configManager.RefreshedConfiguration = configWithDecryptKeys; - - return configManager; - } - -#if NET472 || NET6_0_OR_GREATER - static Dictionary AdditionalEcdhEsHeaderParameters(JsonWebKey publicKeySender) - { - // Create the Ephemeral Public Key (Epk) header parameter as a JWK. - var epkJObject = new JObject(); - epkJObject.Add(JsonWebKeyParameterNames.Kty, publicKeySender.Kty); - epkJObject.Add(JsonWebKeyParameterNames.Crv, publicKeySender.Crv); - epkJObject.Add(JsonWebKeyParameterNames.X, publicKeySender.X); - epkJObject.Add(JsonWebKeyParameterNames.Y, publicKeySender.Y); - - // Set the Ephemeral Public Key (Epk) header parameter, along with the - // Agreement PartyUInfo (Apu) and Agreement PartyVInfo (Apv) header parameters - // to ensure that the ECDH-ES key agreement is successful. - Dictionary additionalHeaderParams = new Dictionary() - { - { JsonWebTokens.JwtHeaderParameterNames.Apu, Guid.NewGuid().ToString() }, - { JsonWebTokens.JwtHeaderParameterNames.Apv, Guid.NewGuid().ToString() }, - { JsonWebTokens.JwtHeaderParameterNames.Epk, epkJObject.ToString(Newtonsoft.Json.Formatting.None) } - }; - - return additionalHeaderParams; - } -#endif - } - } - - public class ValidateTokenAsyncDecryptionTheoryData : ValidateTokenTheoryData - { - public ValidateTokenAsyncDecryptionTheoryData(string testId) : base(testId) { } - - public EncryptingCredentials? EncryptingCredentials { get; set; } - - public Dictionary? AdditionalHeaderClaims { get; set; } = null; - } - - private static string CreateEncryptedToken( - EncryptingCredentials? encryptingCredentials, - Dictionary? additionalHeaderClaims = null) - { - JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); - - SecurityTokenDescriptor securityTokenDescriptor = new SecurityTokenDescriptor - { - Subject = Default.ClaimsIdentity, - EncryptingCredentials = encryptingCredentials, - AdditionalHeaderClaims = additionalHeaderClaims, - }; - - return jsonWebTokenHandler.CreateToken(securityTokenDescriptor); - } - } -} -#nullable restore diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests_e2e.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests_e2e.cs index 37f10e821f..867ab22329 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests_e2e.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests_e2e.cs @@ -36,7 +36,7 @@ public async Task TestNotYetValidToken() Assert.Null(validationResult.Result); Assert.NotNull(validationResult.Error); Assert.IsType(validationResult.Error); - Assert.Contains("IDX10222:", validationResult.Error.Message); + Assert.Contains("IDX10222:", validationResult.Error.MessageDetail.Message); // IDX10222: Lifetime validation failed. The token is not yet valid. ValidFrom (UTC): '1/6/2025 9:03:26 PM', Current time (UTC): '1/6/2025 5:03:26 PM'. } @@ -56,8 +56,6 @@ public async Task TestTokenWithFutureIssuedAt() Assert.True(validationResult.Succeeded); Assert.NotNull(validationResult.Result); Assert.Null(validationResult.Error); - // TODO: Define potentially adding a setting to reject tokens issued in the future. - // As it is not part of the specification, it should be optional. } [Fact] diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JweDecryptionComparisonTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JweDecryptionComparisonTests.cs new file mode 100644 index 0000000000..76ef410c4f --- /dev/null +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JweDecryptionComparisonTests.cs @@ -0,0 +1,374 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Tokens.Experimental; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using Xunit; + +#if NET472 || NET6_0_OR_GREATER +using Newtonsoft.Json.Linq; +#endif + +namespace Microsoft.IdentityModel.JsonWebTokens.Tests +{ + /// + /// These tests validate the decryption of JWE's using both TokenValidationParameters and ValidationParameters. + /// We want to make sure that they succeed or fail in the same way, and that the exceptions thrown are consistent. + /// ValidationParameters path specific tests are written in DecryptTokenTests. + /// The tests in these files could be merged however it is presumed that the TokenValidationParameters will remain fixed while + /// ValidationParameters scenarios will evolve in the future, so it was chosen to keep them separate. + /// + public class JweDecryptionComparisonTests + { + [Theory, MemberData(nameof(InvalidDecryptionTestCases), DisableDiscoveryEnumeration = true)] + public async Task InvalidDecryption(ValidateTokenTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.InvalidDecryption", theoryData); + JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); + + // Validate the token using TokenValidationParameters + TokenValidationResult tokenValidationResult = + await jsonWebTokenHandler.ValidateTokenAsync( + theoryData.Token, + theoryData.TokenValidationParameters); + + try + { + // Validate the token using ValidationParameters +#pragma warning disable CS8604 // Possible null reference argument. + ValidationResult validationResult = + await jsonWebTokenHandler.ValidateTokenAsync( + theoryData.Token, + theoryData.ValidationParameters!, + theoryData.CallContext, + CancellationToken.None); +#pragma warning restore CS8604 // Possible null reference argument. + + if (tokenValidationResult.IsValid != validationResult.Succeeded) + context.AddDiff($"tokenValidationResult.IsValid: '{tokenValidationResult.IsValid}' != validationResult.Succeeded: '{validationResult.Succeeded}'."); + + if (tokenValidationResult.IsValid) + context.AddDiff($"Expected test to fail, test succeeded (TokenValidationResult): {theoryData.TestId}."); + + if (validationResult.Succeeded) + context.AddDiff($"Expected test to fail, test succeeded (ValidationResult): {theoryData.TestId}."); + + if (!tokenValidationResult.IsValid && !validationResult.Succeeded) + { + // Verify the exception provided by the TokenValidationParameters path + theoryData.ExpectedException.ProcessException(tokenValidationResult.Exception, context); + + // Verify the exception provided by the ValidationParameters path + theoryData.ExpectedExceptionValidationParameters.ProcessException(validationResult.Error!.GetException(), context); + + // Exceptions + IdentityComparer.AreSecurityTokenExceptionsEqual( + tokenValidationResult.Exception, + validationResult.Error!.GetException(), + context); + } + } + catch (Exception ex) + { + // If we get here, the test failed. + context.AddDiff($"ValidateTokenAsync returning ValidationResult, should not throw exception caught: {ex}, TestId: {theoryData.TestId}."); + } + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData InvalidDecryptionTestCases + { + get + { + var theoryData = new TheoryData(); + + theoryData.Add( + new ValidateTokenTheoryData("NoDecryptionKeys") + { + Token = CreateEncryptedToken( + new EncryptingCredentials( + KeyingMaterial.DefaultX509Key_2048, + SecurityAlgorithms.RsaPKCS1, + SecurityAlgorithms.Aes128CbcHmacSha256)), + TokenValidationParameters = CreateTokenValidationParameters(), + ValidationParameters = CreateValidationParameters(), + ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10609:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenDecryptionFailedException("IDX10609:"), + }); + + theoryData.Add( + new ValidateTokenTheoryData("WrongDecryptionKey") + { + Token = CreateEncryptedToken( + new EncryptingCredentials( + KeyingMaterial.DefaultX509Key_2048, + SecurityAlgorithms.RsaPKCS1, + SecurityAlgorithms.Aes128CbcHmacSha256)), + TokenValidationParameters = CreateTokenValidationParameters(KeyingMaterial.DefaultRsaSecurityKey1), + ValidationParameters = CreateValidationParameters(KeyingMaterial.DefaultRsaSecurityKey1), + ExpectedException = ExpectedException.SecurityTokenKeyWrapException("IDX10618:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenKeyWrapException("IDX10618:"), + }); + + theoryData.Add( + new ValidateTokenTheoryData("OnTokenDecryptFailure_KeysOnlyInTvp_ThrowsException") + { + Token = CreateEncryptedToken(KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes128_Sha2), + TokenValidationParameters = CreateTokenValidationParameters( + tokenDecryptionKey: KeyingMaterial.DefaultSymmetricSecurityKey_128, + configurationManager: new MockConfigurationManager( + new OpenIdConnectConfiguration())), + ValidationParameters = CreateValidationParameters( + tokenDecryptionKey: KeyingMaterial.DefaultSymmetricSecurityKey_128, + configurationManager: new MockConfigurationManager( + new OpenIdConnectConfiguration())), + ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10603:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenDecryptionFailedException("IDX10603:"), + }); + theoryData.Add( + new ValidateTokenTheoryData("OnTokenDecryptFailure_KeysOnlyInTvp_ThrowsException") + { + Token = CreateEncryptedToken(KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes128_Sha2), + TokenValidationParameters = CreateTokenValidationParameters( + tokenDecryptionKey: new SymmetricSecurityKey(KeyingMaterial.DefaultSymmetricKeyBytes_128), + configurationManager: new MockConfigurationManager( + new OpenIdConnectConfiguration())), + ValidationParameters = CreateValidationParameters( + tokenDecryptionKey: new SymmetricSecurityKey(KeyingMaterial.DefaultSymmetricKeyBytes_128), + configurationManager: new MockConfigurationManager( + new OpenIdConnectConfiguration())), + ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10603:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenDecryptionFailedException("IDX10603:"), + }); + + return theoryData; + } + } + + [Theory, MemberData(nameof(ValidDecryptionTestCases), DisableDiscoveryEnumeration = true)] + public async Task ValidDecryption(ValidateTokenTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.ValidDecryption", theoryData); + + JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); + + // Validate the token using TokenValidationParameters + TokenValidationResult tokenValidationResult = + await jsonWebTokenHandler.ValidateTokenAsync( + theoryData.Token, + theoryData.TokenValidationParameters); + + // Validate the token using ValidationParameters +#pragma warning disable CS8604 // Possible null reference argument. + ValidationResult validationResult = + await jsonWebTokenHandler.ValidateTokenAsync( + theoryData.Token, + theoryData.ValidationParameters!, + theoryData.CallContext, + CancellationToken.None); +#pragma warning restore CS8604 // Possible null reference argument. + + if (tokenValidationResult.IsValid != validationResult.Succeeded) + context.AddDiff($"tokenValidationResult.IsValid: '{tokenValidationResult.IsValid}' != ValidationResult.Succeeded: '{validationResult.Succeeded}', TestId: {theoryData.TestId}."); + + if (!tokenValidationResult.IsValid) + context.AddDiff($"Expected test to succeeded, test failed (TokenValidationResult): TestId: {theoryData.TestId}."); + + if (!validationResult.Succeeded) + { + context.AddDiff($"Expected test to succeeded, test failed (ValidationResult): TestId: {theoryData.TestId}, Error: {validationResult.Error!.GetException()}."); + } + + if (tokenValidationResult.IsValid && validationResult.Succeeded) + { + // ClaimsIdentity + IdentityComparer.AreEqual( + tokenValidationResult.ClaimsIdentity, + validationResult.Result!.ClaimsIdentity, + context); + + // Claims + IdentityComparer.AreEqual( + tokenValidationResult.Claims, + validationResult.Result!.Claims, + context); + } + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData ValidDecryptionTestCases + { + get + { + var theoryData = new TheoryData(); + + theoryData.Add( + new ValidateTokenTheoryData("Aes128Cbc_HmacSha256") + { + Token = CreateEncryptedToken( + new EncryptingCredentials( + KeyingMaterial.DefaultX509Key_2048, + SecurityAlgorithms.RsaPKCS1, + SecurityAlgorithms.Aes128CbcHmacSha256)), + TokenValidationParameters = CreateTokenValidationParameters(KeyingMaterial.DefaultX509Key_2048), + ValidationParameters = CreateValidationParameters(KeyingMaterial.DefaultX509Key_2048), + }); + +#if NET472 || NET6_0_OR_GREATER + theoryData.Add( + new ValidateTokenTheoryData("EcdhEs") + { + Token = CreateEncryptedToken( + new EncryptingCredentials( + new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true), + SecurityAlgorithms.EcdhEsA256kw, + SecurityAlgorithms.Aes128CbcHmacSha256) + { + KeyExchangePublicKey = KeyingMaterial.JsonWebKeyP521_Public + }, + AdditionalEcdhEsHeaderParameters(KeyingMaterial.JsonWebKeyP521_Public)), + TokenValidationParameters = CreateTokenValidationParameters( + new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, + true)), + ValidationParameters = CreateValidationParameters( + new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, + true)), + }); +#endif + theoryData.Add( + new ValidateTokenTheoryData("KeysInConfig_SuccessOnRetry") + { + Token = CreateEncryptedToken(KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes128_Sha2), + TokenValidationParameters = CreateTokenValidationParameters( + configurationManager: CreateConfigurationManager(true)), + ValidationParameters = CreateValidationParameters( + configurationManager: CreateConfigurationManager(true)), + }); + + theoryData.Add( + new ValidateTokenTheoryData("KeyWithoutKeyId_OnTokenDecryptFailure_KeysInConfig_SuccessOnRetry") + { + Token = CreateEncryptedToken(KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes128_Sha2), + TokenValidationParameters = CreateTokenValidationParameters( + configurationManager: CreateConfigurationManager(false)), + ValidationParameters = CreateValidationParameters( + configurationManager: CreateConfigurationManager(false)) + }); + + return theoryData; + } + } + + private static string CreateEncryptedToken( + EncryptingCredentials? encryptingCredentials, + IDictionary? additionalHeaderClaims = null) + { + JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); + + SecurityTokenDescriptor securityTokenDescriptor = new SecurityTokenDescriptor + { + Subject = Default.ClaimsIdentity, + EncryptingCredentials = encryptingCredentials, + AdditionalHeaderClaims = additionalHeaderClaims, + }; + + return jsonWebTokenHandler.CreateToken(securityTokenDescriptor); + } + + private static TokenValidationParameters CreateTokenValidationParameters( + SecurityKey? tokenDecryptionKey = null, + bool tryAllKeys = false, + BaseConfigurationManager? configurationManager = null) + { + // Skip all validations. We just want to decrypt the JWE. + var tokenValidationParameters = new TokenValidationParameters + { + ConfigurationManager = configurationManager, + ValidateAudience = false, + ValidateIssuer = false, + ValidateLifetime = false, + ValidateTokenReplay = false, + ValidateIssuerSigningKey = false, + RequireSignedTokens = false, + TokenDecryptionKey = tokenDecryptionKey, + }; + + return tokenValidationParameters; + } + + private static ValidationParameters CreateValidationParameters( + SecurityKey? tokenDecryptionKey = null, + BaseConfigurationManager? configurationManager = null) + { + ValidationParameters validationParameters = new ValidationParameters(); + + if (tokenDecryptionKey != null) + validationParameters.DecryptionKeys.Add(tokenDecryptionKey); + + validationParameters.ConfigurationManager = configurationManager; + + // Skip all validations. We just want to decrypt the JWE + validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation; + validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; + validationParameters.SignatureKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation; + validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation; + validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation; + validationParameters.SignatureValidator = SkipValidationDelegates.SkipSignatureValidation; + validationParameters.TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation; + validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation; + + return validationParameters; + } + + private static BaseConfigurationManager CreateConfigurationManager(bool invalidKeyHasKeyId) + { + var configWrongDecryptKeys = new OpenIdConnectConfiguration(); + if (invalidKeyHasKeyId) + configWrongDecryptKeys.TokenDecryptionKeys.Add(KeyingMaterial.DefaultSymmetricSecurityKey_128); + else + configWrongDecryptKeys.TokenDecryptionKeys.Add(new SymmetricSecurityKey(KeyingMaterial.DefaultSymmetricKeyBytes_128) { KeyId = null }); + + var configWithDecryptKeys = new OpenIdConnectConfiguration(); + configWithDecryptKeys.TokenDecryptionKeys.Add(KeyingMaterial.DefaultSymmetricSecurityKey_256); + + var configManager = new MockConfigurationManager(configWrongDecryptKeys); + configManager.RefreshedConfiguration = configWithDecryptKeys; + + return configManager; + } + +#if NET472 || NET6_0_OR_GREATER + private static Dictionary AdditionalEcdhEsHeaderParameters(JsonWebKey publicKeySender) + { + // Create the Ephemeral Public Key (Epk) header parameter as a JWK. + var epkJObject = new JObject(); + epkJObject.Add(JsonWebKeyParameterNames.Kty, publicKeySender.Kty); + epkJObject.Add(JsonWebKeyParameterNames.Crv, publicKeySender.Crv); + epkJObject.Add(JsonWebKeyParameterNames.X, publicKeySender.X); + epkJObject.Add(JsonWebKeyParameterNames.Y, publicKeySender.Y); + + // Set the Ephemeral Public Key (Epk) header parameter, along with the + // Agreement PartyUInfo (Apu) and Agreement PartyVInfo (Apv) header parameters + // to ensure that the ECDH-ES key agreement is successful. + Dictionary additionalHeaderParams = new Dictionary() + { + { JsonWebTokens.JwtHeaderParameterNames.Apu, Guid.NewGuid().ToString() }, + { JsonWebTokens.JwtHeaderParameterNames.Apv, Guid.NewGuid().ToString() }, + { JsonWebTokens.JwtHeaderParameterNames.Epk, epkJObject.ToString(Newtonsoft.Json.Formatting.None) } + }; + + return additionalHeaderParams; + } +#endif + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JweDecryptionTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JweDecryptionTests.cs new file mode 100644 index 0000000000..12c30521d8 --- /dev/null +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JweDecryptionTests.cs @@ -0,0 +1,405 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IdentityModel.Tokens.Jwt.Tests; +using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.Protocols; +using Microsoft.IdentityModel.TestUtils; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Tokens.Experimental; +using Xunit; + +#if NET472_OR_GREATER || NET6_0_OR_GREATER +using System.Collections.Generic; +#endif +using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; + +namespace Microsoft.IdentityModel.JsonWebTokens.Tests +{ + public class JweDecryptionTests + { + [Theory, MemberData(nameof(InvalidDecryptTestCases), DisableDiscoveryEnumeration = false)] + public void InvalidDecryptToken(ValidateEncryptionTheoryData theoryData) + { + CompareContext context = TestUtilities.WriteHeader($"{this}.InvalidDecryptToken", theoryData); + + JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); + + try + { + ValidationResult validationResult = + jsonWebTokenHandler.DecryptToken( + theoryData.JwtToken, + theoryData.ValidationParameters, + theoryData.Configuration, + theoryData.CallContext); + + if (validationResult.Succeeded) + { + context.AddDiff($"Decryption succeeded, expected to fail: {theoryData.TestId}."); + } + else + { + ValidationError validationError = validationResult.Error; + IdentityComparer.AreStringsEqual( + validationError.FailureType.Name, + theoryData.ValidationResult.Error.FailureType.Name, + context); + + Exception exception = validationError.GetException(); + theoryData.ExpectedException.ProcessException(exception, context); + } + } + catch (Exception ex) + { + context.AddDiff($"Decryption threw: {ex.Message}, Methods returning ValidationResult should not throw: {theoryData.TestId}."); + } + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData InvalidDecryptTestCases + { + get + { + TheoryData theoryData = new TheoryData(); + + JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); + + theoryData.Add( + new ValidateEncryptionTheoryData("AlgorithmMismatch_DecryptionFails") + { + JwtToken = new JsonWebToken(jsonWebTokenHandler.CreateToken( + new SecurityTokenDescriptor + { + SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, + EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.DefaultX509Key_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256), + Claims = Default.PayloadDictionary + })), + ExpectedException = ExpectedException.SecurityTokenKeyWrapException("IDX10618:"), + ValidationParameters = ValidationUtils.CreateValidationParameters( + decryptionKeys: [KeyingMaterial.RsaSecurityKey_2048]), + ValidationResult = new ValidationError( + new MessageDetail( + TokenLogMessages.IDX10609, + LogHelper.MarkAsSecurityArtifact( + new JsonWebToken(ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims), + JwtTokenUtilities.SafeLogJwtToken)), + ValidationFailureType.KeyWrapFailed, + null), + }); + + theoryData.Add( + new ValidateEncryptionTheoryData("KeyIdMismatch_TryAllDecryptionKeysFalse_DecryptionFails") + { + JwtToken = new JsonWebToken(jsonWebTokenHandler.CreateToken( + new SecurityTokenDescriptor + { + SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, + EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256), + Claims = Default.PayloadDictionary + })), + ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10609:"), + ValidationParameters = new ValidationParameters + { + TryAllDecryptionKeys = false, + }, + Configuration = new CustomConfiguration( + new RsaSecurityKey(KeyingMaterial.RsaParameters_2048) + { + KeyId = "CustomRsaSecurityKey_2048" + }), + ValidationResult = new ValidationError( + new MessageDetail( + TokenLogMessages.IDX10609, + LogHelper.MarkAsSecurityArtifact( + new JsonWebToken(ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims), + JwtTokenUtilities.SafeLogJwtToken)), + ValidationFailureType.TokenDecryptionFailed, + null), + }); + + return theoryData; + } + } + + [Theory, MemberData(nameof(ParameterChecksTestCases), DisableDiscoveryEnumeration = false)] + public void ParameterChecks(ValidateEncryptionTheoryData theoryData) + { + CompareContext context = TestUtilities.WriteHeader($"{this}.ParameterChecks", theoryData); + + JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); + + try + { + ValidationResult validationResult = jsonWebTokenHandler.DecryptToken( + theoryData.JwtToken, + theoryData.ValidationParameters, + theoryData.Configuration, + theoryData.CallContext); + + if (validationResult.Succeeded) + { + context.AddDiff($"Decryption succeeded, expected to fail: {theoryData.TestId}."); + } + else + { + ValidationError validationError = validationResult.Error; + IdentityComparer.AreStringsEqual( + validationError.FailureType.Name, + theoryData.ValidationResult.Error.FailureType.Name, + context); + + Exception exception = validationError.GetException(); + theoryData.ExpectedException.ProcessException(exception, context); + } + } + catch (Exception ex) + { + context.AddDiff($"Decryption threw: {ex.Message}, Should not throw: {theoryData.TestId}."); + } + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData ParameterChecksTestCases + { + get + { + TheoryData theoryData = new TheoryData(); + + JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); + + theoryData.Add( + new ValidateEncryptionTheoryData("SecurityTokenIsNull") + { + JwtToken = null, + ValidationParameters = new ValidationParameters(), + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), + ValidationResult = new ValidationError( + new MessageDetail(TokenLogMessages.IDX10000, "jwtToken"), + ValidationFailureType.NullArgument, + null), + }); + + theoryData.Add( + new ValidateEncryptionTheoryData("ValidationParametersIsNull") + { + JwtToken = new JsonWebToken(EncodedJwts.LiveJwt), + ValidationParameters = null, + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), + ValidationResult = new ValidationError( + new MessageDetail(TokenLogMessages.IDX10000, "validationParameters"), + ValidationFailureType.NullArgument, + null), + }); + + theoryData.Add( + new ValidateEncryptionTheoryData("TokenIsNotEncrypted") + { + JwtToken = new JsonWebToken(EncodedJwts.LiveJwt), + ValidationParameters = new ValidationParameters(), + ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10612:"), + ValidationResult = new ValidationError( + new MessageDetail(TokenLogMessages.IDX10612), + ValidationFailureType.TokenDecryptionFailed, + null), + }); + + return theoryData; + } + } + + [Theory, MemberData(nameof(ValidDecryptTestCases), DisableDiscoveryEnumeration = false)] + public void ValidDecryptToken(ValidateEncryptionTheoryData theoryData) + { + CompareContext context = TestUtilities.WriteHeader($"{this}.ValidDecryptToken", theoryData); + + JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); + ValidationResult validationResult = jsonWebTokenHandler.DecryptToken( + theoryData.JwtToken, + theoryData.ValidationParameters, + theoryData.Configuration, + theoryData.CallContext); + + try + { + if (!validationResult.Succeeded) + { + context.AddDiff($"Decryption failed: {validationResult.Error}, Expected to succeed."); + } + else + { + IdentityComparer.AreStringsEqual( + validationResult.Result, + theoryData.ValidationResult.Result, + context); + + theoryData.ExpectedException.ProcessNoException(context); + } + } + catch (Exception ex) + { + context.AddDiff($"Decryption threw: {ex.Message}, Expected to succeed."); + } + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData ValidDecryptTestCases + { + get + { + TheoryData theoryData = new TheoryData(); + var validToken = EncodedJwts.LiveJwt; + var token = new JsonWebToken(validToken); + var jsonWebTokenHandler = new JsonWebTokenHandler(); + + var rsaKey = new RsaSecurityKey(KeyingMaterial.RsaParameters_2048) { KeyId = "CustomRsaSecurityKey_2048" }; + var configurationThatThrows = CreateCustomConfigurationThatThrows(rsaKey); + + var configurationWithMismatchedKeys = new CustomConfiguration(rsaKey); + + theoryData.Add( + new ValidateEncryptionTheoryData("FromValidationParameters") + { + JwtToken = new JsonWebToken(ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims), + ValidationParameters = ValidationUtils.CreateValidationParameters( + decryptionKeys: [Default.SymmetricEncryptingCredentials.Key]), + ValidationResult = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.", + }); + + theoryData.Add( + new ValidateEncryptionTheoryData("FromKeyResolver") + { + JwtToken = new JsonWebToken(ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims), + ValidationParameters = new ValidationParameters + { + DecryptionKeyResolver = (tokenString, token, kid, validationParameters, callContext) => [Default.SymmetricEncryptingCredentials.Key] + }, + ValidationResult = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.", + }); + + theoryData.Add( + new ValidateEncryptionTheoryData("FromConfiguration") + { + JwtToken = new JsonWebToken(ReferenceTokens.JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims), + ValidationParameters = new ValidationParameters + { + ConfigurationManager = new StaticConfigurationManager(new CustomConfiguration(Default.SymmetricEncryptingCredentials.Key)) + }, + Configuration = new CustomConfiguration(Default.SymmetricEncryptingCredentials.Key), + ValidationResult = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.", + }); + + theoryData.Add( + new ValidateEncryptionTheoryData("OneKeyThrowsOnUnwrap") + { + JwtToken = new JsonWebToken(jsonWebTokenHandler.CreateToken( + new SecurityTokenDescriptor + { + SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, + EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256), + Claims = Default.PayloadDictionary + })), + ValidationParameters = new ValidationParameters + { + ConfigurationManager = new StaticConfigurationManager(configurationThatThrows) + }, + Configuration = configurationThatThrows, + ValidationResult = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikpzb25XZWJLZXlSc2FfMjA0OCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwOi8vRGVmYXVsdC5BdWRpZW5jZS5jb20iLCJhenAiOiJodHRwOi8vRGVmYXVsdC5BenAuY29tIiwiZW1haWwiOiJCb2JAY29udG9zby5jb20iLCJleHAiOiIyNTM0MDIzMDA3OTkiLCJnaXZlbl9uYW1lIjoiQm9iIiwiaXNzIjoiaHR0cDovL0RlZmF1bHQuSXNzdWVyLmNvbSIsImlhdCI6IjE0ODk3NzU2MTciLCJqdGkiOiJKdGkiLCJuYmYiOiIxNDg5Nzc1NjE3In0.Et69LAC4sn6nNm_HNz_AnJ8siLT6LRTjDSb1aY8APcwJmPn-TxU-8GG5_bmNkoVukR7hkYG2JuWPxJKbjDd73BlmelaiyZBoPUyU0S-GX3XgyC2v_CkOq4yYbtD-kq5s7kNNj5QJjZDq0oJeqcUMrq4xRWATPtUMkIZ0GpEhO_C5MFxT8jAWe_a2gyUA4KoibalKtkYgFvgLcvyZJhUx7AERbli6b7OkUksFp9zIwmc_jZZCXJ_F_wASyj9KgHQKN9VHER3bB2zQeWHR0q32ODYC4ggsan-Nkm-jIsATi2tgkKzROzK55dy8ZdFArXUYJRpI_raYkTUHRK_wP3GqtQ", + }); + + theoryData.Add( + new ValidateEncryptionTheoryData("TryAllDecryptionKeys") + { + JwtToken = new JsonWebToken(jsonWebTokenHandler.CreateToken( + new SecurityTokenDescriptor + { + SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, + EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256), + Claims = Default.PayloadDictionary + })), + ValidationParameters = new ValidationParameters // TryAllDecryptionKeys is true by default + { + ConfigurationManager = new StaticConfigurationManager(configurationWithMismatchedKeys) + }, + Configuration = configurationWithMismatchedKeys, + ValidationResult = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikpzb25XZWJLZXlSc2FfMjA0OCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwOi8vRGVmYXVsdC5BdWRpZW5jZS5jb20iLCJhenAiOiJodHRwOi8vRGVmYXVsdC5BenAuY29tIiwiZW1haWwiOiJCb2JAY29udG9zby5jb20iLCJleHAiOiIyNTM0MDIzMDA3OTkiLCJnaXZlbl9uYW1lIjoiQm9iIiwiaXNzIjoiaHR0cDovL0RlZmF1bHQuSXNzdWVyLmNvbSIsImlhdCI6IjE0ODk3NzU2MTciLCJqdGkiOiJKdGkiLCJuYmYiOiIxNDg5Nzc1NjE3In0.Et69LAC4sn6nNm_HNz_AnJ8siLT6LRTjDSb1aY8APcwJmPn-TxU-8GG5_bmNkoVukR7hkYG2JuWPxJKbjDd73BlmelaiyZBoPUyU0S-GX3XgyC2v_CkOq4yYbtD-kq5s7kNNj5QJjZDq0oJeqcUMrq4xRWATPtUMkIZ0GpEhO_C5MFxT8jAWe_a2gyUA4KoibalKtkYgFvgLcvyZJhUx7AERbli6b7OkUksFp9zIwmc_jZZCXJ_F_wASyj9KgHQKN9VHER3bB2zQeWHR0q32ODYC4ggsan-Nkm-jIsATi2tgkKzROzK55dy8ZdFArXUYJRpI_raYkTUHRK_wP3GqtQ", + }); + +#if NET472 || NET6_0_OR_GREATER + var ecdsaEncryptingCredentials = new EncryptingCredentials( + new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true), + SecurityAlgorithms.EcdhEsA256kw, + SecurityAlgorithms.Aes128CbcHmacSha256) + { + KeyExchangePublicKey = KeyingMaterial.JsonWebKeyP256_Public + }; + + static IDictionary AdditionalEcdhEsHeaderParameters(JsonWebKey publicKeySender) + { + Dictionary additionalHeaderParams = new Dictionary() + { + { JwtHeaderParameterNames.Apu, Guid.NewGuid().ToString() }, + { JwtHeaderParameterNames.Apv, Guid.NewGuid().ToString() }, + { JwtHeaderParameterNames.Epk, + $"{{\"{JsonWebKeyParameterNames.Kty}\":\"{publicKeySender.Kty}\"," + + $"\"{JsonWebKeyParameterNames.Crv}\":\"{publicKeySender.Crv}\"," + + $"\"{JsonWebKeyParameterNames.X}\":\"{publicKeySender.X}\"," + + $"\"{JsonWebKeyParameterNames.Y}\":\"{publicKeySender.Y}\"}}" } + }; + + return additionalHeaderParams; + } + + theoryData.Add( + new ValidateEncryptionTheoryData("Ecdh") + { + JwtToken = new JsonWebToken(jsonWebTokenHandler.CreateToken( + new SecurityTokenDescriptor + { + EncryptingCredentials = ecdsaEncryptingCredentials, + Expires = DateTime.MaxValue, + NotBefore = DateTime.MinValue, + IssuedAt = DateTime.MinValue, + AdditionalHeaderClaims = AdditionalEcdhEsHeaderParameters(KeyingMaterial.JsonWebKeyP256_Public), + })), + ValidationParameters = ValidationUtils.CreateValidationParameters( + decryptionKeys: [new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true)] + ), + ValidationResult = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJleHAiOjI1MzQwMjMwMDgwMCwiaWF0IjowLCJuYmYiOjB9." + }); + +#endif + return theoryData; + } + } + + private static CustomConfiguration CreateCustomConfigurationThatThrows(SecurityKey rsaKey) + { + var customCryptoProviderFactory = new DerivedCryptoProviderFactory + { + IsSupportedAlgImpl = (alg, key) => true, + CreateKeyWrapProviderForUnwrapImpl = (key, alg) => throw new InvalidOperationException("Test exception") + }; + + var sym512Hey = new SymmetricSecurityKey(KeyingMaterial.DefaultSymmetricKeyBytes_512) { KeyId = "CustomSymmetricSecurityKey_512" }; + sym512Hey.CryptoProviderFactory = customCryptoProviderFactory; + + var configurationWithCustomCryptoProviderFactory = new CustomConfiguration(rsaKey); + configurationWithCustomCryptoProviderFactory.TokenDecryptionKeys.Add(sym512Hey); + + return configurationWithCustomCryptoProviderFactory; + } + } + + public class CustomConfiguration : BaseConfiguration + { + public CustomConfiguration(SecurityKey tokenDecryptionKey) : base() + { + TokenDecryptionKeys.Add(tokenDecryptionKey); + } + } +} diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JwsSignatureTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JwsSignatureTests.cs new file mode 100644 index 0000000000..c861773bf4 --- /dev/null +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JwsSignatureTests.cs @@ -0,0 +1,251 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IdentityModel.Tokens.Jwt.Tests; +using Microsoft.IdentityModel.TestUtils; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Tokens.Experimental; +using Xunit; +using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; + +namespace Microsoft.IdentityModel.JsonWebTokens.Tests +{ + public class JwsSignatureTests + { + [Theory, MemberData(nameof(InvalidSignatureTestCases), DisableDiscoveryEnumeration = true)] + public void InvalidSignature(ValidateSignatureTheoryData theoryData) + { + CompareContext context = TestUtilities.WriteHeader($"{this}.InvalidSignature", theoryData); + + try + { + ValidationResult validationResult = + JsonWebTokenHandler.ValidateSignature( + theoryData.JwtToken, + theoryData.ValidationParameters, + theoryData.Configuration, + theoryData.CallContext); + + if (validationResult.Succeeded) + { + context.AddDiff($"Expected test to fail, test succeeded (ValidationResult): TestId: {theoryData.TestId}, Result: {validationResult.Result}."); + } + else + { + ValidationError validationError = validationResult.Error; + IdentityComparer.AreStringsEqual( + validationError.FailureType.Name, + theoryData.ValidationResult.Error.FailureType.Name, + context); + + Exception exception = validationError.GetException(); + theoryData.ExpectedException.ProcessException(exception, context); + } + } + catch (Exception ex) + { + // We should never throw out of ValidateSignature. + context.AddDiff($"JsonWebTokenHandler.ValidateSignature returning ValidationResult, should not throw exception caught: {ex}, TestId: {theoryData.TestId}."); + } + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData InvalidSignatureTestCases + { + get + { + var unsignedToken = new JsonWebToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ."); + + TheoryData theoryData = new TheoryData(); + + theoryData.Add( + new ValidateSignatureTheoryData("Null_JWT") + { + JwtToken = null, + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), + ValidationResult = new ValidationError( + new MessageDetail( + TokenLogMessages.IDX10000, + "jwtToken"), + ValidationFailureType.NullArgument, + null) + }); + + theoryData.Add( + new ValidateSignatureTheoryData("Null_ValidationParameters") + { + JwtToken = new JsonWebToken(EncodedJwts.LiveJwt), + ValidationParameters = null, + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), + ValidationResult = new ValidationError( + new MessageDetail( + TokenLogMessages.IDX10000, + "validationParameters"), + ValidationFailureType.NullArgument, + null) + }); + + theoryData.Add( + new ValidateSignatureTheoryData("DelegateReturnsFailure") + { + JwtToken = new JsonWebToken(EncodedJwts.LiveJwt), + ValidationParameters = new ValidationParameters + { + SignatureValidator = (token, parameters, configuration, callContext) => new SignatureValidationError( + new MessageDetail("IDX10000: NullArgument", null), + ValidationFailureType.NullArgument, + ValidationError.GetCurrentStackFrame()) + }, + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), + ValidationResult = new ValidationError( + new MessageDetail( + TokenLogMessages.IDX10000, + "NullArgument"), + ValidationFailureType.NullArgument, + null) + }); + + theoryData.Add( + new ValidateSignatureTheoryData("NoKeys") + { + JwtToken = new JsonWebToken(EncodedJwts.LiveJwt), + ValidationParameters = new ValidationParameters(), + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10527:"), + ValidationResult = new ValidationError( + new MessageDetail(TokenLogMessages.IDX10500), + SignatureValidationFailure.SigningKeyNotFound, + null) + }); + + return theoryData; + } + } + + [Theory, MemberData(nameof(ValidSignatureTestCases), DisableDiscoveryEnumeration = true)] + public void ValidSignature(ValidateSignatureTheoryData theoryData) + { + CompareContext context = TestUtilities.WriteHeader($"{this}.ValidSignature", theoryData); + + try + { + ValidationResult validationResult = + JsonWebTokenHandler.ValidateSignature( + theoryData.JwtToken, + theoryData.ValidationParameters, + theoryData.Configuration, + theoryData.CallContext); + + if (!validationResult.Succeeded) + { + context.AddDiff($"Expected test to succeeded, test failed (ValidationResult): TestId: {theoryData.TestId}, Error: {validationResult.Error!.GetException()}."); + } + else + { + IdentityComparer.AreSecurityKeysEqual( + validationResult.Result, + theoryData.ValidationResult.Result, + context); + } + } + catch (Exception ex) + { + // We should never throw out of ValidateSignature. + context.AddDiff($"JsonWebTokenHandler.ValidateSignature returning ValidationResult, should not throw exception caught: {ex}, TestId: {theoryData.TestId}."); + } + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData ValidSignatureTestCases + { + get + { + TheoryData theoryData = new TheoryData(); + + var unsignedToken = new JsonWebToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ."); + + JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); + theoryData.Add( + new ValidateSignatureTheoryData("DelegateReturnsToken") + { + JwtToken = new JsonWebToken(EncodedJwts.LiveJwt), + ValidationParameters = new ValidationParameters + { + SignatureValidator = (token, parameters, configuration, callContext) => KeyingMaterial.JsonWebKeyRsa256PublicSigningCredentials.Key + }, + + ValidationResult = KeyingMaterial.JsonWebKeyRsa256PublicSigningCredentials.Key + }); + + theoryData.Add( + new ValidateSignatureTheoryData("KidMatches") + { + JwtToken = new JsonWebToken(jsonWebTokenHandler.CreateToken( + new SecurityTokenDescriptor + { + SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials + })), + ValidationParameters = ValidationUtils.CreateValidationParameters(signingKeys: [KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key]), + ValidationResult = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, + }); + + theoryData.Add( + new ValidateSignatureTheoryData("X5tMatches") + { + JwtToken = new JsonWebToken(jsonWebTokenHandler.CreateToken( + new SecurityTokenDescriptor + { + SigningCredentials = KeyingMaterial.X509SigningCreds_1024_RsaSha2_Sha2 + })), + ValidationParameters = ValidationUtils.CreateValidationParameters(signingKeys: [KeyingMaterial.X509SigningCreds_1024_RsaSha2_Sha2.Key]), + ValidationResult = KeyingMaterial.X509SigningCreds_1024_RsaSha2_Sha2.Key, + }); + + theoryData.Add( + new ValidateSignatureTheoryData("SigningKeyResolverKeyMatches") + { + JwtToken = new JsonWebToken(jsonWebTokenHandler.CreateToken( + new SecurityTokenDescriptor + { + SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials + })), + ValidationParameters = new ValidationParameters + { + SignatureKeyResolver = (token, securityToken, kid, validationParameters, configuration, callContext) => + KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key + }, + ValidationResult = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key + }); + + theoryData.Add( + new ValidateSignatureTheoryData("ConfurationKeyMatches") + { + JwtToken = new JsonWebToken(jsonWebTokenHandler.CreateToken( + new SecurityTokenDescriptor + { + SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials + })), + Configuration = ValidationUtils.CreateOpenIdConntectConfiguration(signingKeys: [KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key]), + ValidationParameters = new ValidationParameters(), + ValidationResult = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, + }); + + theoryData.Add( + new ValidateSignatureTheoryData("NoKeyId_TryAllKeys") + { + JwtToken = new JsonWebToken(jsonWebTokenHandler.CreateToken( + new SecurityTokenDescriptor + { + SigningCredentials = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2_NoKeyId + })), + ValidationParameters = ValidationUtils.CreateValidationParameters(signingKeys: [KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2_NoKeyId.Key]), + ValidationResult = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2_NoKeyId.Key, + }); + + return theoryData; + } + } + } +} diff --git a/test/Microsoft.IdentityModel.TestUtils/ITestingTokenHandler.cs b/test/Microsoft.IdentityModel.TestUtils/ITestingTokenHandler.cs index e136d07088..3650a25cda 100644 --- a/test/Microsoft.IdentityModel.TestUtils/ITestingTokenHandler.cs +++ b/test/Microsoft.IdentityModel.TestUtils/ITestingTokenHandler.cs @@ -27,7 +27,7 @@ Task> ValidateTokenAsync( CancellationToken cancellationToken); Task ValidateTokenAsync( - string token, + string? token, TokenValidationParameters validationParameters); Task> ValidateTokenAsync( @@ -36,6 +36,10 @@ Task> ValidateTokenAsync( CallContext callContext, CancellationToken cancellationToken); + Task ValidateTokenAsync( + SecurityToken token, + TokenValidationParameters validationParameters); + SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor); string CreateStringToken(SecurityTokenDescriptor tokenDescriptor); @@ -82,12 +86,20 @@ public async Task> ValidateTok } public async Task ValidateTokenAsync( - string token, - TokenValidationParameters validationParameters) + string? token, + TokenValidationParameters? validationParameters) { return await _handler.ValidateTokenAsync(token, validationParameters); } + public async Task ValidateTokenAsync( + SecurityToken securityToken, + TokenValidationParameters? validationParameters) + { + return await _handler.ValidateTokenAsync(securityToken, validationParameters); + } + + public SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor) { _handler.SetDefaultTimesOnTokenCreation = SetDefaultTimesOnTokenCreation; @@ -202,12 +214,19 @@ public async Task> ValidateTok } public async Task ValidateTokenAsync( - string token, - TokenValidationParameters validationParameters) + string? token, + TokenValidationParameters? validationParameters) { return await _handler.ValidateTokenAsync(token, validationParameters); } + public async Task ValidateTokenAsync( + SecurityToken securityToken, + TokenValidationParameters? validationParameters) + { + return await _handler.ValidateTokenAsync(securityToken, validationParameters); + } + public SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor) { _handler.SetDefaultTimesOnTokenCreation = SetDefaultTimesOnTokenCreation; @@ -320,12 +339,19 @@ public async Task> ValidateTok } public async Task ValidateTokenAsync( - string token, + string? token, TokenValidationParameters validationParameters) { return await _handler.ValidateTokenAsync(token, validationParameters); } + public async Task ValidateTokenAsync( + SecurityToken securityToken, + TokenValidationParameters validationParameters) + { + return await _handler.ValidateTokenAsync(securityToken, validationParameters); + } + public SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor) { _handler.SetDefaultTimesOnTokenCreation = SetDefaultTimesOnTokenCreation; diff --git a/test/Microsoft.IdentityModel.TestUtils/TestCaseProvider.cs b/test/Microsoft.IdentityModel.TestUtils/TestCaseProvider.cs index c805fc4b1b..407d73fb68 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TestCaseProvider.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TestCaseProvider.cs @@ -12,6 +12,87 @@ namespace Microsoft.IdentityModel.TestUtils { public class TestCaseProvider { + #region Parameters + /// + /// Tests are generated for invalid parameters for ValidateTokenAsync that takes a SecurityToken. + /// + /// + /// + internal static TheoryData GenerateInvalidSecurityTokenParameterTestCases(ITestingTokenHandler testingTokenHandler) + { + TheoryData testCases = new(); + TokenValidationParameters tokenValidationParameters = ValidationUtils.CreateTokenValidationParameters(algorithms: [SecurityAlgorithms.EcdsaSha256]); + ValidationParameters validationParameters = ValidationUtils.CreateValidationParameters(algorithms: [SecurityAlgorithms.EcdsaSha256]); + + testCases.Add(new ValidateTokenTheoryData("SecurityTokenNull") + { + ExpectedExceptionValidationParameters = ExpectedException.ArgumentNullException("IDX10000:"), + TestingTokenHandler = testingTokenHandler, + ValidationParameters = validationParameters, + }); + + testCases.Add(new ValidateTokenTheoryData("ValidationParametersNull") + { + ExpectedExceptionValidationParameters = ExpectedException.ArgumentNullException("IDX10000:"), + SecurityToken = new DerivedSecurityToken(), + TestingTokenHandler = testingTokenHandler, + ValidationParameters = null + }); + + testCases.Add(new ValidateTokenTheoryData("SecurityTokenTypeUnknown") + { + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenValidationException("IDX10001:"), + SecurityToken = new DerivedSecurityToken(), + TestingTokenHandler = testingTokenHandler, + ValidationParameters = validationParameters, + }); + + return testCases; + } + + /// + /// Tests are generated for invalid parameters for ValidateTokenAsync that takes a string. + /// + /// + /// + internal static TheoryData GenerateInvalidTokenParameterTestCases(ITestingTokenHandler testingTokenHandler) + { + TheoryData testCases = new(); + TokenValidationParameters tokenValidationParameters = ValidationUtils.CreateTokenValidationParameters(algorithms: [SecurityAlgorithms.EcdsaSha256]); + ValidationParameters validationParameters = ValidationUtils.CreateValidationParameters(algorithms: [SecurityAlgorithms.EcdsaSha256]); + + testCases.Add(new ValidateTokenTheoryData("TokenNull") + { + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), + ExpectedExceptionValidationParameters = ExpectedException.ArgumentNullException("IDX10000:"), + TestingTokenHandler = testingTokenHandler, + Token = null, + }); + + testCases.Add(new ValidateTokenTheoryData("ValidationParametersNull") + { + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), + ExpectedExceptionValidationParameters = ExpectedException.ArgumentNullException("IDX10000:"), + TestingTokenHandler = testingTokenHandler, + Token = "SecurityToken", + TokenValidationParameters = null, + ValidationParameters = null + }); + + testCases.Add(new ValidateTokenTheoryData("TokenTooLarge") + { + ExpectedException = ExpectedException.ArgumentException("IDX10209:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenValidationException("IDX10209:"), + TestingTokenHandler = testingTokenHandler, + Token = new string('a', 1000000), // 1 million characters + TokenValidationParameters = tokenValidationParameters, + ValidationParameters = validationParameters, + }); + + return testCases; + } + #endregion + #region Algorithm internal static TheoryData GenerateInvalidAlgorithmTestCases(ITestingTokenHandler testingTokenHandler) { @@ -31,7 +112,8 @@ internal static TheoryData GenerateInvalidAlgorithmTest { ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10511:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidAlgorithmException("IDX10696:"), - RelaxTVPException = true, + FailureType = AlgorithmValidationFailure.NotSupported, + RelaxTVPException = testingTokenHandler is JsonWebTestingTokenHandler ? false : true, Token = testingTokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = testingTokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -44,7 +126,8 @@ internal static TheoryData GenerateInvalidAlgorithmTest { ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10511:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidAlgorithmException("IDX10696:"), - RelaxTVPException = true, + FailureType = AlgorithmValidationFailure.NotSupported, + RelaxTVPException = testingTokenHandler is JsonWebTestingTokenHandler ? false : true, Token = testingTokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = testingTokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -101,7 +184,6 @@ internal static TheoryData GenerateValidAlgorithmTestCa ValidationParameters = validationParameters }); - return testCases; } #endregion @@ -129,6 +211,7 @@ internal static TheoryData GenerateInvalidAudienceTestC { ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), + FailureType = AudienceValidationFailure.DidNotMatch, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -144,6 +227,7 @@ internal static TheoryData GenerateInvalidAudienceTestC { ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10208:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidAudienceException("IDX10268:"), + FailureType = AudienceValidationFailure.NoAudiencesProvided, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -157,6 +241,7 @@ internal static TheoryData GenerateInvalidAudienceTestC { ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidAudienceException("IDX10268:"), + FailureType = AudienceValidationFailure.NoAudiencesProvided, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -178,6 +263,7 @@ internal static TheoryData GenerateInvalidAudienceTestC { ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10206:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidAudienceException("IDX10206:"), + FailureType = AudienceValidationFailure.NoAudienceInToken, RelaxTVPException = true, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, @@ -283,6 +369,7 @@ internal static TheoryData GenerateInvalidIssuerTestCas { ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10205:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidIssuerException("IDX10212:"), + FailureType = IssuerValidationFailure.IssuerDidNotMatch, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -291,7 +378,6 @@ internal static TheoryData GenerateInvalidIssuerTestCas // Token with null issuer, should fail validation // SAML and SAML2 tokens do not allow setting a null or empty issuer, so this test is only for JWT tokens. - // TODO add a specific test for SAML/SAML2 tokens. if (tokenHandler is JsonWebTestingTokenHandler) { // Token with issuer, but no valid issuers in parameters @@ -311,6 +397,7 @@ internal static TheoryData GenerateInvalidIssuerTestCas { ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10211:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidIssuerException("IDX10211:"), + FailureType = IssuerValidationFailure.NoIssuerInToken, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -333,6 +420,7 @@ internal static TheoryData GenerateInvalidIssuerTestCas { ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10211:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidIssuerException("IDX10211:"), + FailureType = IssuerValidationFailure.NoIssuerInToken, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -358,6 +446,7 @@ internal static TheoryData GenerateInvalidIssuerTestCas { ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10204:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidIssuerException("IDX10212:"), + FailureType = IssuerValidationFailure.NoIssuersProvided, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -372,6 +461,7 @@ internal static TheoryData GenerateInvalidIssuerTestCas { ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10204:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidIssuerException("IDX10212:"), + FailureType = IssuerValidationFailure.NoIssuersProvided, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -386,7 +476,6 @@ internal static TheoryData GenerateValidIssuerTestCases TheoryData theoryData = new TheoryData(); // TODO - add tests for configuration - // Valid_IssuerIsConfigurationIssuer string guid = Guid.NewGuid().ToString(); // Token with issuer matching valid issuer @@ -461,6 +550,7 @@ internal static TheoryData GenerateInvalidLifetimeTestC ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidLifetimeException("IDX10223:"), Token = tokenHandler.CreateStringToken(securityTokenDescriptor), + FailureType = LifetimeValidationFailure.Expired, TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, ValidationParameters = validationParameters, @@ -483,6 +573,7 @@ internal static TheoryData GenerateInvalidLifetimeTestC { ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidLifetimeException("IDX10223:"), + FailureType = LifetimeValidationFailure.Expired, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -507,6 +598,7 @@ internal static TheoryData GenerateInvalidLifetimeTestC { ExpectedException = ExpectedException.SecurityTokenNotYetValidException("IDX10222:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidLifetimeException("IDX10222:"), + FailureType = LifetimeValidationFailure.NotYetValid, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -532,6 +624,7 @@ internal static TheoryData GenerateInvalidLifetimeTestC { ExpectedException = ExpectedException.SecurityTokenNoExpirationException("IDX10225:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidLifetimeException("IDX10225:"), + FailureType = LifetimeValidationFailure.NoExpirationTime, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -679,6 +772,7 @@ internal static TheoryData GenerateInvalidReadTokenTest typeof(SecurityTokenValidationException), "IDX14107:", typeof(SecurityTokenMalformedException)), + FailureType = ValidationFailureType.TokenReadingFailed, Token = "malformed_token", TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -739,6 +833,7 @@ internal static TheoryData GenerateInvalidSignatureTest ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10511:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidSignatureException("IDX10520:"), RelaxTVPException = true, + FailureType = SignatureValidationFailure.ValidationFailed, Token = tokenHandler.CreateTamperedSignature(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -752,6 +847,7 @@ internal static TheoryData GenerateInvalidSignatureTest ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10504:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidSignatureException("IDX10504:"), RelaxTVPException = true, + FailureType = SignatureValidationFailure.TokenIsNotSigned, Token = tokenHandler.CreateWithoutSignature(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -766,6 +862,7 @@ internal static TheoryData GenerateInvalidSignatureTest { ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10511:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidSignatureException("IDX10520:"), + FailureType = SignatureValidationFailure.ValidationFailed, Token = tokenHandler.CreateWithTamperedPayload(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -780,6 +877,7 @@ internal static TheoryData GenerateInvalidSignatureTest { ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10514:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidSignatureException("IDX30201:"), + FailureType = SignatureValidationFailure.ReferenceDigestValidationFailed, Token = tokenHandler.CreateWithTamperedPayload(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -796,6 +894,7 @@ internal static TheoryData GenerateInvalidSignatureTest { ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10511:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidSignatureException("IDX10520:"), + FailureType = SignatureValidationFailure.ValidationFailed, Token = tokenHandler.CreateWithTamperedHeader(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -820,8 +919,9 @@ internal static TheoryData GenerateInvalidSignatureTest new ValidateTokenTheoryData("KeyNotAvailable") { ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10503:"), - ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10527"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10522"), RelaxTVPException = true, + FailureType = SignatureValidationFailure.SigningKeyNotFound, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -829,7 +929,7 @@ internal static TheoryData GenerateInvalidSignatureTest }); // Token signed with a key that is not available, multiple siging signingKeys, KeyId present - tokenValidationParameters = ValidationUtils.CreateTokenValidationParameters(keys: [Default.AsymmetricSigningKey, Default.AsymmetricSigningKey]); + tokenValidationParameters = ValidationUtils.CreateTokenValidationParameters(signingKeys: [Default.AsymmetricSigningKey, Default.AsymmetricSigningKey]); validationParameters = ValidationUtils.CreateValidationParameters(signingKeys: [Default.AsymmetricSigningKey, Default.AsymmetricSigningKey]); ExpectedException expectedException; @@ -842,7 +942,8 @@ internal static TheoryData GenerateInvalidSignatureTest new ValidateTokenTheoryData("KeyNotAvailableMultipleSigningKeys") { ExpectedException = expectedException, - ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10527"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10522"), + FailureType = SignatureValidationFailure.SigningKeyNotFound, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -871,7 +972,8 @@ internal static TheoryData GenerateInvalidSignatureTest new ValidateTokenTheoryData("KeyNotAvailableNoKid") { ExpectedException = expectedException, - ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10526:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10523:"), + FailureType = SignatureValidationFailure.SigningKeyNotFound, Token = tokenHandler.CreateStringTokenNoKid(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -879,7 +981,7 @@ internal static TheoryData GenerateInvalidSignatureTest }); // Token signed with a key that is not available, multiple siging signingKeys, KeyId NOT present, TryAllSigningKeys false - tokenValidationParameters = ValidationUtils.CreateTokenValidationParameters(keys: [Default.AsymmetricSigningKey, Default.AsymmetricSigningKey]); + tokenValidationParameters = ValidationUtils.CreateTokenValidationParameters(signingKeys: [Default.AsymmetricSigningKey, Default.AsymmetricSigningKey]); tokenValidationParameters.TryAllIssuerSigningKeys = false; validationParameters = ValidationUtils.CreateValidationParameters(signingKeys: [Default.AsymmetricSigningKey, Default.AsymmetricSigningKey]); validationParameters.TryAllSigningKeys = false; @@ -894,6 +996,7 @@ internal static TheoryData GenerateInvalidSignatureTest { ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10526:"), + FailureType = SignatureValidationFailure.SigningKeyNotFound, Token = tokenHandler.CreateStringTokenNoKid(securityTokenDescriptor), TestingTokenHandler = tokenHandler, ValidationParameters = validationParameters, @@ -901,7 +1004,7 @@ internal static TheoryData GenerateInvalidSignatureTest }); // Token signed with a key that is not available, multiple siging signingKeys, KeyId NOT present, TryAllSigningKeys true - tokenValidationParameters = ValidationUtils.CreateTokenValidationParameters(keys: [Default.AsymmetricSigningKey, Default.AsymmetricSigningKey]); + tokenValidationParameters = ValidationUtils.CreateTokenValidationParameters(signingKeys: [Default.AsymmetricSigningKey, Default.AsymmetricSigningKey]); tokenValidationParameters.TryAllIssuerSigningKeys = true; validationParameters = ValidationUtils.CreateValidationParameters(signingKeys: [Default.AsymmetricSigningKey, Default.AsymmetricSigningKey]); validationParameters.TryAllSigningKeys = true; @@ -912,10 +1015,12 @@ internal static TheoryData GenerateInvalidSignatureTest expectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10517:"); theoryData.Add( - new ValidateTokenTheoryData("NoMatchingKeys_NoKid_TryAllKeys") + new ValidateTokenTheoryData("" + + "NoMatchingKeys_NoKid_TryAllKeys") { ExpectedException = expectedException, ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10523:"), + FailureType = SignatureValidationFailure.SigningKeyNotFound, Token = tokenHandler.CreateStringTokenNoKid(securityTokenDescriptor), TestingTokenHandler = tokenHandler, ValidationParameters = validationParameters, @@ -923,13 +1028,14 @@ internal static TheoryData GenerateInvalidSignatureTest }); // No signingKeys available, KeyId NOT present - tokenValidationParameters = ValidationUtils.CreateTokenValidationParameters(keys: []); + tokenValidationParameters = ValidationUtils.CreateTokenValidationParameters(signingKeys: []); validationParameters = ValidationUtils.CreateValidationParameters(signingKeys: []); theoryData.Add( new ValidateTokenTheoryData("NoValidationKeys_NoKid_DontTryAllKeys") { ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), - ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10526:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10525:"), + FailureType = SignatureValidationFailure.SigningKeyNotFound, Token = tokenHandler.CreateStringTokenNoKid(securityTokenDescriptor), TestingTokenHandler = tokenHandler, ValidationParameters = validationParameters, @@ -940,17 +1046,14 @@ internal static TheoryData GenerateInvalidSignatureTest new ValidateTokenTheoryData("NoKeyId_NoKeys") { ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), - ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10526:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10525:"), + FailureType = SignatureValidationFailure.SigningKeyNotFound, Token = tokenHandler.CreateStringTokenNoKid(securityTokenDescriptor), TestingTokenHandler = tokenHandler, ValidationParameters = validationParameters, TokenValidationParameters = tokenValidationParameters }); - //Invalid_TokenSignedWithDifferentKey_KeyIdPresent_TryAllKeysFalse - //Invalid_TokenSignedWithDifferentKey_KeyIdPresent_TryAllKeysTrue - // Token signed with different key, KeyId present, TryAllKeys true - // TODO need tests with config. return theoryData; @@ -981,7 +1084,7 @@ internal static TheoryData GenerateValidSignatureTestCa ValidationParameters = validationParameters, }); - tokenValidationParameters = ValidationUtils.CreateTokenValidationParameters(keys: []); + tokenValidationParameters = ValidationUtils.CreateTokenValidationParameters(signingKeys: []); tokenValidationParameters.ConfigurationManager = new MockConfigurationManager(ValidationUtils.CreateDeaultOpenIdConntectConfiguration()); validationParameters = ValidationUtils.CreateValidationParameters(signingKeys: []); validationParameters.ConfigurationManager = new MockConfigurationManager(ValidationUtils.CreateDeaultOpenIdConntectConfiguration()); @@ -1028,6 +1131,7 @@ internal static TheoryData GenerateInvalidIssuerSigning { ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException("IDX10249:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidSigningKeyException("IDX10249:"), + FailureType = SignatureKeyValidationFailure.KeyExpired, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -1044,6 +1148,7 @@ internal static TheoryData GenerateInvalidIssuerSigning { ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException("IDX10248:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidSigningKeyException("IDX10248:"), + FailureType = SignatureKeyValidationFailure.NotYetValid, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -1111,6 +1216,7 @@ internal static TheoryData GenerateInvalidTokenReplayTe { ExpectedException = ExpectedException.SecurityTokenNoExpirationException("IDX10227:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenReplayDetectedException("IDX10227:"), + FailureType = TokenReplayValidationFailure.NoExpiration, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -1135,6 +1241,7 @@ internal static TheoryData GenerateInvalidTokenReplayTe { ExpectedException = ExpectedException.SecurityTokenReplayDetectedException("IDX10228:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenReplayDetectedException("IDX10228:"), + FailureType = TokenReplayValidationFailure.TokenFoundInCache, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, @@ -1150,6 +1257,7 @@ internal static TheoryData GenerateInvalidTokenReplayTe { ExpectedException = ExpectedException.SecurityTokenReplayAddFailedException("IDX10229:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenReplayDetectedException("IDX10229:"), + FailureType = TokenReplayValidationFailure.AddToCacheFailed, Token = tokenHandler.CreateStringToken(securityTokenDescriptor), TestingTokenHandler = tokenHandler, TokenValidationParameters = tokenValidationParameters, diff --git a/test/Microsoft.IdentityModel.TestUtils/TestTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/TestTheoryData.cs index da6a462fc4..007b173814 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TestTheoryData.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TestTheoryData.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System.IdentityModel.Tokens.Jwt; -using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens.Saml; using Microsoft.IdentityModel.Tokens.Saml2; using Xunit; @@ -11,54 +10,46 @@ namespace Microsoft.IdentityModel.TestUtils { public class TestTheoryData { - public static TheoryData TokenReplayValidationTheoryData + public static TheoryData TokenReplayValidationTheoryData { get { - return new TheoryData + return new TheoryData { - new TokenReplayTheoryData + new ValidateTokenReplayTheoryData("ValidateTokenReplay: false, TokenReplayValidator: null") { - TestId = "ValidateTokenReplay: false, TokenReplayValidator: null", }, - new TokenReplayTheoryData + new ValidateTokenReplayTheoryData($"ValidateTokenReplay: false, {nameof(ValidationDelegates.TokenReplayValidatorReturnsTrue)}") { - TestId = $"ValidateTokenReplay: false, {nameof(ValidationDelegates.TokenReplayValidatorReturnsTrue)}", TokenReplayValidator = ValidationDelegates.TokenReplayValidatorReturnsTrue }, - new TokenReplayTheoryData + new ValidateTokenReplayTheoryData($"ValidateTokenReplay: false, {nameof(ValidationDelegates.TokenReplayValidatorReturnsFalse)}") { - TestId = $"ValidateTokenReplay: false, {nameof(ValidationDelegates.TokenReplayValidatorReturnsFalse)}", TokenReplayValidator = ValidationDelegates.TokenReplayValidatorReturnsFalse, ExpectedException = ExpectedException.SecurityTokenReplayDetected("IDX10228:") }, - new TokenReplayTheoryData + new ValidateTokenReplayTheoryData($"ValidateTokenReplay: false, {nameof(ValidationDelegates.TokenReplayValidatorThrows)}") { - TestId = $"ValidateTokenReplay: false, {nameof(ValidationDelegates.TokenReplayValidatorThrows)}", TokenReplayValidator = ValidationDelegates.TokenReplayValidatorThrows, ExpectedException = ExpectedException.SecurityTokenReplayDetected("TokenReplayValidatorThrows") }, - new TokenReplayTheoryData + new ValidateTokenReplayTheoryData($"ValidateTokenReplay: true, TokenReplayValidator: null") { - TestId = $"ValidateTokenReplay: true, TokenReplayValidator: null", ValidateTokenReplay = true }, - new TokenReplayTheoryData + new ValidateTokenReplayTheoryData($"ValidateTokenReplay: true, {nameof(ValidationDelegates.TokenReplayValidatorReturnsTrue)}") { - TestId = $"ValidateTokenReplay: true, {nameof(ValidationDelegates.TokenReplayValidatorReturnsTrue)}", TokenReplayValidator = ValidationDelegates.TokenReplayValidatorReturnsTrue, ValidateTokenReplay = true }, - new TokenReplayTheoryData + new ValidateTokenReplayTheoryData($"ValidateTokenReplay: true, {nameof(ValidationDelegates.TokenReplayValidatorReturnsFalse)}") { - TestId = $"ValidateTokenReplay: true, {nameof(ValidationDelegates.TokenReplayValidatorReturnsFalse)}", TokenReplayValidator = ValidationDelegates.TokenReplayValidatorReturnsFalse, ValidateTokenReplay = true, ExpectedException = ExpectedException.SecurityTokenReplayDetected("IDX10228:") }, - new TokenReplayTheoryData + new ValidateTokenReplayTheoryData($"ValidateTokenReplay: true, {nameof(ValidationDelegates.TokenReplayValidatorThrows)}") { - TestId = $"ValidateTokenReplay: true, {nameof(ValidationDelegates.TokenReplayValidatorThrows)}", TokenReplayValidator = ValidationDelegates.TokenReplayValidatorThrows, ValidateTokenReplay = true, ExpectedException = ExpectedException.SecurityTokenReplayDetected("TokenReplayValidatorThrows") @@ -67,35 +58,31 @@ public static TheoryData TokenReplayValidationTheoryData } } - public static TheoryData CheckParametersForTokenReplayTheoryData + public static TheoryData CheckParametersForTokenReplayTheoryData { get { - return new TheoryData + return new TheoryData { - new TokenReplayTheoryData + new ValidateTokenReplayTheoryData($"ValidateTokenReplay: true, {nameof(ValidationDelegates.TokenReplayValidatorChecksExpirationTimeJwt)}") { - First = true, - TestId = $"ValidateTokenReplay: true, {nameof(ValidationDelegates.TokenReplayValidatorChecksExpirationTimeJwt)}", - SecurityToken = Default.AsymmetricJwt, + Token = Default.AsymmetricJwt, SecurityTokenHandler = new JwtSecurityTokenHandler(), SigningKey = Default.AsymmetricSigningKey, TokenReplayValidator = ValidationDelegates.TokenReplayValidatorChecksExpirationTimeJwt, ValidateTokenReplay = true }, - new TokenReplayTheoryData + new ValidateTokenReplayTheoryData($"ValidateTokenReplay: true, {nameof(ValidationDelegates.TokenReplayValidatorChecksExpirationTimeSaml)}") { - TestId = $"ValidateTokenReplay: true, {nameof(ValidationDelegates.TokenReplayValidatorChecksExpirationTimeSaml)}", - SecurityToken = ReferenceTokens.SamlToken_Valid, + Token = ReferenceTokens.SamlToken_Valid, SecurityTokenHandler = new SamlSecurityTokenHandler(), SigningKey = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, TokenReplayValidator = ValidationDelegates.TokenReplayValidatorChecksExpirationTimeSaml, ValidateTokenReplay = true, }, - new TokenReplayTheoryData + new ValidateTokenReplayTheoryData($"ValidateTokenReplay: true, {nameof(ValidationDelegates.TokenReplayValidatorChecksExpirationTimeSaml2)}") { - TestId = $"ValidateTokenReplay: true, {nameof(ValidationDelegates.TokenReplayValidatorChecksExpirationTimeSaml2)}", - SecurityToken = ReferenceTokens.Saml2Token_Valid, + Token = ReferenceTokens.Saml2Token_Valid, SecurityTokenHandler = new Saml2SecurityTokenHandler(), SigningKey = KeyingMaterial.DefaultAADSigningKey, TokenReplayValidator = ValidationDelegates.TokenReplayValidatorChecksExpirationTimeSaml2, @@ -105,36 +92,4 @@ public static TheoryData CheckParametersForTokenReplayThe } } } - - public class TokenReplayTheoryData : TheoryDataBase - { - public TokenReplayValidator TokenReplayValidator - { - get; - set; - } - public bool ValidateTokenReplay - { - get; - set; - } - - public string SecurityToken - { - get; - set; - } - - public SecurityTokenHandler SecurityTokenHandler - { - get; - set; - } - - public SecurityKey SigningKey - { - get; - set; - } - } } diff --git a/test/Microsoft.IdentityModel.TestUtils/TestUtilities.cs b/test/Microsoft.IdentityModel.TestUtils/TestUtilities.cs index 65d67a8018..24df51f6e2 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TestUtilities.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TestUtilities.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Security.Claims; @@ -526,6 +527,21 @@ public static void WriteHeader(string testcase, bool first) Console.WriteLine(">>>> " + testcase); } + + public static void RecordIfMoveNextFound(CompareContext context, ValidationError validationError) + { + foreach (StackFrame stackFrame in validationError.StackFrames) + { + // the runtime will create a StackFrame with the name 'movenext' in an asnyc method. + if (stackFrame!.ToString().ToLowerInvariant().Contains("movenext")) + context.AddDiff($"StackFrame contains 'movenext': {stackFrame}"); + } + } + + public static void RecordUnexpectedException(CompareContext context, TheoryDataBase theoryData, Exception ex) + { + context.Diffs.Add($"Unexpected exception thrown: {ex.Message}. TestId {theoryData.TestId}."); + } } public class TokenReplayCache : ITokenReplayCache diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomAlgorithmValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomAlgorithmValidationDelegates.cs index c215d0c470..e8d1474e9d 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomAlgorithmValidationDelegates.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomAlgorithmValidationDelegates.cs @@ -31,7 +31,7 @@ internal static ValidationResult UnknownValidationFailu { return new CustomAlgorithmValidationError( new MessageDetail(nameof(UnknownValidationFailure)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, Default.GetStackFrame(), algorithm); } @@ -113,7 +113,7 @@ internal static ValidationResult AlgorithmValidatorNotS { return new AlgorithmValidationError( new MessageDetail(nameof(AlgorithmValidatorNotSupportedFailureDelegate)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame(), algorithm); } diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomAudienceValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomAudienceValidationDelegates.cs index b17ac8a94f..29c9fe46cd 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomAudienceValidationDelegates.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomAudienceValidationDelegates.cs @@ -32,7 +32,7 @@ internal static ValidationResult AudienceDidNotMatch( { return new CustomAudienceValidationError( new MessageDetail(nameof(AudienceDidNotMatch)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, Default.GetStackFrame(), tokenAudiences, null); @@ -46,7 +46,7 @@ internal static ValidationResult UnknownValidationFailu { return new CustomAudienceValidationError( new MessageDetail(nameof(UnknownValidationFailure)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame(), tokenAudiences, null); @@ -74,7 +74,7 @@ internal static ValidationResult AudienceValidatorDeleg { return new AudienceValidationError( new MessageDetail(nameof(AudienceValidatorDelegate)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, Default.GetStackFrame(), tokenAudiences, null); @@ -90,7 +90,7 @@ internal static ValidationResult AudienceValidatorThrow nameof(AudienceValidatorThrows), new AudienceValidationError( new MessageDetail(nameof(AudienceValidatorDelegate)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, Default.GetStackFrame(), tokenAudiences, null), diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerSigningKeyValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerSigningKeyValidationDelegates.cs index 17740cb1a5..e574ac4715 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerSigningKeyValidationDelegates.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerSigningKeyValidationDelegates.cs @@ -45,7 +45,7 @@ internal static ValidationResult Unknown { return new CustomIssuerSigningKeyValidationError( new MessageDetail(nameof(UnknownValidationFailure)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame(), signingKey); } diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerValidationDelegates.cs index fb209578ca..6dd5097356 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerValidationDelegates.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerValidationDelegates.cs @@ -51,7 +51,7 @@ internal async static Task> U return await Task.FromResult(new ValidationResult( new CustomIssuerValidationError( new MessageDetail(nameof(UnknownValidationFailure)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame(), issuer))); } diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomLifetimeValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomLifetimeValidationDelegates.cs index 6d10fdf62a..e09ab4ba5a 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomLifetimeValidationDelegates.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomLifetimeValidationDelegates.cs @@ -66,7 +66,7 @@ internal static ValidationResult CustomUnkno { return new CustomLifetimeValidationError( new MessageDetail(nameof(CustomUnknownValidationFailure)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame(), notBefore, expires, diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomSignatureValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomSignatureValidationDelegates.cs index 1d17b39384..be3036ad53 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomSignatureValidationDelegates.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomSignatureValidationDelegates.cs @@ -42,7 +42,7 @@ internal static ValidationResult UnknownValidation { return new CustomSignatureValidationError( new MessageDetail(nameof(UnknownValidationFailure)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame()); } diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomTokenReplayValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomTokenReplayValidationDelegates.cs index 949fecdfa9..698ccd88c1 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomTokenReplayValidationDelegates.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomTokenReplayValidationDelegates.cs @@ -44,7 +44,7 @@ internal class CustomTokenReplayValidationDelegates { return new CustomTokenReplayValidationError( new MessageDetail(nameof(UnknownValidationFailure)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame(), expirationTime); } diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomTokenTypeValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomTokenTypeValidationDelegates.cs index 78b2d9287c..9a76861ee9 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomTokenTypeValidationDelegates.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomTokenTypeValidationDelegates.cs @@ -45,7 +45,7 @@ internal static ValidationResult UnknownVal { return new CustomTokenTypeValidationError( new MessageDetail(nameof(UnknownValidationFailure)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame(), tokenType); } diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs index bd6c1d48d5..346c630fc4 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs @@ -141,7 +141,7 @@ public CustomAudienceWithoutGetExceptionValidationOverrideError( IList? tokenAudiences, IList? validAudiences, Exception? innerException = null) : - base(messageDetail, AudienceValidationFailure.AudienceDidNotMatch, stackFrame, tokenAudiences, validAudiences, innerException) + base(messageDetail, AudienceValidationFailure.DidNotMatch, stackFrame, tokenAudiences, validAudiences, innerException) { } } diff --git a/test/Microsoft.IdentityModel.TestUtils/ValidateTokenTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/ValidateTokenTheoryData.cs index 33af23b97d..ac31000314 100644 --- a/test/Microsoft.IdentityModel.TestUtils/ValidateTokenTheoryData.cs +++ b/test/Microsoft.IdentityModel.TestUtils/ValidateTokenTheoryData.cs @@ -1,26 +1,31 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; +using System.Collections.Generic; +using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens.Experimental; #nullable enable namespace Microsoft.IdentityModel.TestUtils { +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. public class ValidateTokenTheoryData : TheoryDataBase { -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. public ValidateTokenTheoryData(string testId) : base(testId) { } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. - public ValidateTokenTheoryData(TokenHandler tokenHandler, string testId) : base(testId) - { - TokenHandler = tokenHandler; - } + public BaseConfiguration Configuration { get; internal set; } + + public bool DoNotScrubErrorMessages { get; set; } - internal bool ExpectedIsValid { get; set; } = true; + public ExpectedException ExpectedExceptionValidationParameters { get; set; } = ExpectedException.NoExceptionExpected; - internal ExpectedException ExpectedExceptionValidationParameters { get; set; } = ExpectedException.NoExceptionExpected; + public bool IncludeInStackFrameCountTest { get; set; } = true; + + public JsonWebToken JwtToken { get; set; } + + public SecurityToken? SecurityToken { get; set; } /// /// The TokenValidation results for the exception vary between JWT and SAML tokens. @@ -29,16 +34,98 @@ public ValidateTokenTheoryData(TokenHandler tokenHandler, string testId) : base( /// public bool RelaxTVPException { get; set; } = false; - public SecurityTokenDescriptor? SecurityTokenDescriptor { get; set; } + public string? Token { get; set; } = string.Empty; - public string Token { get; set; } = string.Empty; - - public TokenHandler TokenHandler { get; set; } + internal TokenValidationParameters? TokenValidationParameters { get; set; } internal ITestingTokenHandler? TestingTokenHandler { get; set; } - internal TokenValidationParameters? TokenValidationParameters { get; set; } - + internal ValidationFailureType FailureType { get; set; } = ValidationFailureType.NullArgument; internal ValidationParameters? ValidationParameters { get; set; } } + + public class ValidateAlgorithmTheoryData : ValidateTokenTheoryData + { + public ValidateAlgorithmTheoryData(string testId) : base(testId) { } + + public string Algorithm { get; set; } + + public SecurityKey SecurityKey { get; set; } + + internal ValidationResult ValidationResult { get; set; } + } + + public class ValidateAudienceTheoryData : ValidateTokenTheoryData + { + public ValidateAudienceTheoryData(string testId) : base(testId) { } + + public List TokenAudiences { get; set; } + + public List ValidAudiences { get; set; } + + internal ValidationResult ValidationResult { get; set; } + } + + public class ValidateEncryptionTheoryData : ValidateTokenTheoryData + { + public ValidateEncryptionTheoryData(string testId) : base(testId) { } + + internal ValidationResult ValidationResult { get; set; } + } + + public class ValidateIssuerTheoryData : ValidateTokenTheoryData + { + public ValidateIssuerTheoryData(string testId) : base(testId) { } + + public string Issuer { get; set; } + + internal ValidationResult ValidationResult { get; set; } + + public string ValidIssuerToAdd { get; internal set; } + } + + public class ValidateLifetimeTheoryData : ValidateTokenTheoryData + { + public ValidateLifetimeTheoryData(string testId) : base(testId) { } + + public DateTime? NotBefore { get; set; } + + public DateTime? Expires { get; set; } + + internal ValidationResult ValidationResult { get; set; } + } + + public class ValidateTokenReplayTheoryData : ValidateTokenTheoryData + { + public ValidateTokenReplayTheoryData(string testId) : base(testId) { } + + public DateTime? ExpirationTime { get; set; } + + public SecurityTokenHandler SecurityTokenHandler { get; set; } + + public SecurityKey SigningKey { get; set; } + + public TokenReplayValidator TokenReplayValidator { get; set; } + + internal ValidationResult ValidationResult { get; set; } + + public bool ValidateTokenReplay { get; set; } + } + + public class ValidateSignatureTheoryData : ValidateTokenTheoryData + { + public ValidateSignatureTheoryData(string testId) : base(testId) { } + + internal ValidationResult ValidationResult { get; set; } + } + + public class ValidateSigningKeyTheoryData : ValidateTokenTheoryData + { + public ValidateSigningKeyTheoryData(string testId) : base(testId) { } + + public SecurityKey SecurityKey { get; set; } + + internal ValidationResult ValidationResult { get; set; } + } +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. } diff --git a/test/Microsoft.IdentityModel.TestUtils/ValidationUtils.cs b/test/Microsoft.IdentityModel.TestUtils/ValidationUtils.cs index 8bd70c248e..2c0bebced0 100644 --- a/test/Microsoft.IdentityModel.TestUtils/ValidationUtils.cs +++ b/test/Microsoft.IdentityModel.TestUtils/ValidationUtils.cs @@ -23,24 +23,34 @@ await theoryData.TestingTokenHandler.ValidateTokenAsync( theoryData.Token, theoryData.TokenValidationParameters!); - ValidationResult validationResult = - await theoryData.TestingTokenHandler.ValidateTokenAsync( - theoryData.Token, - theoryData.ValidationParameters!, - theoryData.CallContext, - CancellationToken.None); - - if (tokenValidationResult.IsValid != validationResult.Succeeded) - context.AddDiff($"tokenValidationResult.IsValid: '{tokenValidationResult.IsValid}' != validationResult.Succeeded: '{validationResult.Succeeded}'."); - - if (!validationResult.Succeeded) + try { - context.AddDiff($"Expected test to succeed, test failed: {theoryData.TestId}."); - context.AddDiff($"Message: {validationResult.Error!.Message}"); +#pragma warning disable CS8604 // Possible null reference argument. + ValidationResult validationResult = + await theoryData.TestingTokenHandler.ValidateTokenAsync( + theoryData.Token, + theoryData.ValidationParameters!, + theoryData.CallContext, + CancellationToken.None); +#pragma warning restore CS8604 // Possible null reference argument. + + if (tokenValidationResult.IsValid != validationResult.Succeeded) + context.AddDiff($"tokenValidationResult.IsValid: '{tokenValidationResult.IsValid}' != validationResult.Succeeded: '{validationResult.Succeeded}'."); + + if (!validationResult.Succeeded) + { + context.AddDiff($"Expected test to succeed, test failed: {theoryData.TestId}."); + context.AddDiff($"Message: {validationResult.Error!.MessageDetail.Message}"); + } + else + { + IdentityComparer.AreEqual(validationResult.Result!.SecurityToken, tokenValidationResult.SecurityToken, context); + } } - else + catch (Exception ex) { - IdentityComparer.AreEqual(validationResult.Result!.SecurityToken, tokenValidationResult.SecurityToken, context); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); + return; } } else @@ -51,6 +61,8 @@ await theoryData.TestingTokenHandler.ValidateTokenAsync( internal static async Task RunInvalidTest(ValidateTokenTheoryData theoryData, CompareContext context) { + // TokenHandler.ValidateTokenAsync only checks FailureType and Exception. + // Indivdual validators (Algorithm, Audience, Issuer, etc.) the ValidationError is validated. if (theoryData.TestingTokenHandler != null) { TokenValidationResult tokenValidationResult = @@ -58,44 +70,149 @@ internal static async Task RunInvalidTest(ValidateTokenTheoryData theoryData, Co theoryData.Token, theoryData.TokenValidationParameters!); - ValidationResult validationResult = - await theoryData.TestingTokenHandler.ValidateTokenAsync( - theoryData.Token, - theoryData.ValidationParameters!, - theoryData.CallContext, - CancellationToken.None); - - // TODO add check for ValidationErrorType in the validationResult.Error + try + { +#pragma warning disable CS8604 // Possible null reference argument. + ValidationResult validationResult = + await theoryData.TestingTokenHandler.ValidateTokenAsync( + theoryData.Token, + theoryData.ValidationParameters!, + theoryData.CallContext, + CancellationToken.None); +#pragma warning restore CS8604 // Possible null reference argument. + + if (tokenValidationResult.IsValid != validationResult.Succeeded) + context.AddDiff($"tokenValidationResult.IsValid: '{tokenValidationResult.IsValid}'\n!=\nValidationResult.Succeeded: '{validationResult.Succeeded}'."); + + if (tokenValidationResult.IsValid) + context.AddDiff($"Expected test to fail, test succeeded (TokenValidationResult): {theoryData.TestId}."); + + if (validationResult.Succeeded) + { + context.AddDiff($"Expected test to fail, test succeeded (ValidationResult): {theoryData.TestId}."); + } + else + { + // The default value is set for JWT tokens as these are the most commonly used tokens. + // RelaxTVPException property is used to accommodate exception differences TVP validation errors between JWT, SAML and SAML2 tokens. + // The model using ValidationParameters, all tokens will provide the same exception for validation errors. + if (!theoryData.RelaxTVPException) + { + theoryData.ExpectedException.ProcessException(tokenValidationResult.Exception, context, "TokenValidationResult"); + } + else + { + // check that the exception is derived from SecurityTokenException + if (tokenValidationResult.Exception is not SecurityTokenException securityTokenException) + { + context.AddDiff($"TokenValidationResult: Expected SecurityTokenException, got: {tokenValidationResult.Exception.GetType().FullName}."); + } + } - if (tokenValidationResult.IsValid != validationResult.Succeeded) - context.AddDiff($"tokenValidationResult.IsValid: '{tokenValidationResult.IsValid}' != OperationResult.Succeeded: '{validationResult.Succeeded}'."); + theoryData.ExpectedExceptionValidationParameters!.ProcessException(validationResult.Error!.GetException(), context, "ValidationResult"); + ValidationError validationError = validationResult.Error!; + TestUtilities.RecordIfMoveNextFound(context, validationError); - if (tokenValidationResult.IsValid) - context.AddDiff($"Expected test to fail, test succeeded (TokenValidationResult): {theoryData.TestId}."); + theoryData.ExpectedExceptionValidationParameters!.ProcessException(validationError.GetException(), context, "ValidationResult"); - if (validationResult.Succeeded) + if (theoryData.FailureType != validationError.FailureType) + context.AddDiff($"ValidationError.FailureType: {validationError.FailureType}\n!=\ntheoryData.FailureType: {theoryData.FailureType}"); + } + } + catch (Exception ex) { - context.AddDiff($"Expected test to fail, test succeeded (OperationResult): {theoryData.TestId}."); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } - else + } + else + { + context.AddDiff("TestingTokenHandler is null, cannot run test."); + } + } + + internal static async Task RunTokenParameterTest(ValidateTokenTheoryData theoryData, CompareContext context) + { + if (theoryData.TestingTokenHandler != null) + { + TokenValidationResult tokenValidationResult = + await theoryData.TestingTokenHandler!.ValidateTokenAsync( + theoryData.Token, + theoryData.TokenValidationParameters!); + + try { - // The default value is set for JWT tokens as these are the most commonly used tokens. - // RelaxTVPException property is used to accommodate exception differences TVP validation errors between JWT, SAML and SAML2 tokens. - // The model using ValidationParameters, all tokens will provide the same exception for validation errors. - if (!theoryData.RelaxTVPException) +#pragma warning disable CS8604 // Possible null reference argument. + ValidationResult validationResult = + await theoryData.TestingTokenHandler.ValidateTokenAsync( + theoryData.Token, + theoryData.ValidationParameters!, + theoryData.CallContext, + CancellationToken.None); +#pragma warning restore CS8604 // Possible null reference argument. + + if (tokenValidationResult.IsValid != validationResult.Succeeded) + context.AddDiff($"tokenValidationResult.IsValid: '{tokenValidationResult.IsValid}' != ValidationResult.Succeeded: '{validationResult.Succeeded}'."); + + if (tokenValidationResult.IsValid) + context.AddDiff($"Expected test to fail, test succeeded (TokenValidationResult): {theoryData.TestId}."); + + if (validationResult.Succeeded) { - theoryData.ExpectedException.ProcessException(tokenValidationResult.Exception, context, "TokenValidationResult"); + context.AddDiff($"Expected test to fail, test succeeded (ValidationResult): {theoryData.TestId}."); } else { - // check that the exception is derived from SecurityTokenException - if (tokenValidationResult.Exception is not SecurityTokenException securityTokenException) - { - context.AddDiff($"Expected SecurityTokenException, got: {tokenValidationResult.Exception.GetType().FullName}."); - } + theoryData.ExpectedException.ProcessException(tokenValidationResult.Exception, context, "TokenValidationResult"); + theoryData.ExpectedExceptionValidationParameters!.ProcessException(validationResult.Error!.GetException(), context, "ValidationResult"); + ValidationError validationError = validationResult.Error!; + TestUtilities.RecordIfMoveNextFound(context, validationError); + + theoryData.ExpectedExceptionValidationParameters!.ProcessException(validationError.GetException(), context, "ValidationResult"); + } + } + catch (Exception ex) + { + TestUtilities.RecordUnexpectedException(context, theoryData, ex); + } + } + else + { + context.AddDiff("TestingTokenHandler is null, cannot run test."); + } + } + + internal static async Task RunSecurityTokenParameterTest(ValidateTokenTheoryData theoryData, CompareContext context) + { + if (theoryData.TestingTokenHandler != null) + { + try + { +#pragma warning disable CS8604 // Possible null reference argument. + ValidationResult validationResult = + await theoryData.TestingTokenHandler.ValidateTokenAsync( + theoryData.SecurityToken, + theoryData.ValidationParameters!, + theoryData.CallContext, + CancellationToken.None); +#pragma warning restore CS8604 // Possible null reference argument. + + if (validationResult.Succeeded) + { + context.AddDiff($"Expected test to fail, test succeeded (ValidationResult): {theoryData.TestId}."); } + else + { + Exception exception = validationResult.Error!.GetException(); + theoryData.ExpectedExceptionValidationParameters!.ProcessException(exception, context, "ValidationResult"); + ValidationError validationError = validationResult.Error!; + TestUtilities.RecordIfMoveNextFound(context, validationError); - theoryData.ExpectedExceptionValidationParameters!.ProcessException(validationResult.Error!.GetException(), context, "OperationResult"); + theoryData.ExpectedExceptionValidationParameters!.ProcessException(validationError.GetException(), context, "ValidationResult"); + } + } + catch (Exception ex) + { + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } } else @@ -113,16 +230,29 @@ public static OpenIdConnectConfiguration CreateDeaultOpenIdConntectConfiguration return openIdConnectConfiguration; } + public static OpenIdConnectConfiguration CreateOpenIdConntectConfiguration( + List? signingKeys = null, + string? issuer = null) + { + OpenIdConnectConfiguration openIdConnectConfiguration = new OpenIdConnectConfiguration(); + if (signingKeys != null) + signingKeys.ForEach(openIdConnectConfiguration.SigningKeys.Add); + + if (issuer != null) + openIdConnectConfiguration.Issuer = issuer; + + return openIdConnectConfiguration; + } + public static TokenValidationParameters CreateTokenValidationParameters( IList? algorithms = null, IList? audiences = null, TimeSpan? clockSkew = null, IList? issuers = null, - IList? keys = null, + IList? signingKeys = null, TimeProvider? timeProvider = null, TokenReplayCache? tokenReplayCache = null) { - TokenValidationParameters tokenValidationParameters = new TokenValidationParameters(); if (algorithms != null) @@ -142,8 +272,8 @@ public static TokenValidationParameters CreateTokenValidationParameters( tokenValidationParameters.ValidIssuer = Default.Issuer; - if (keys != null) - tokenValidationParameters.IssuerSigningKeys = keys; + if (signingKeys != null) + tokenValidationParameters.IssuerSigningKeys = signingKeys; else tokenValidationParameters.IssuerSigningKey = Default.AsymmetricSigningCredentials.Key; @@ -168,7 +298,8 @@ public static ValidationParameters CreateValidationParameters( List? signingKeys = null, TimeSpan? clockSkew = null, TimeProvider? timeProvider = null, - TokenReplayCache? tokenReplayCache = null) + TokenReplayCache? tokenReplayCache = null, + bool TryAllSigningKeys = true) { ValidationParameters validationParameters = new ValidationParameters(); @@ -202,6 +333,8 @@ public static ValidationParameters CreateValidationParameters( if (timeProvider != null) validationParameters.TimeProvider = timeProvider; + validationParameters.TryAllSigningKeys = TryAllSigningKeys; + return validationParameters; } } diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs index 406ba420de..7280a94d3e 100644 --- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs @@ -6,7 +6,6 @@ using System.IdentityModel.Tokens.Jwt; using System.IO; using System.Security.Claims; -using System.Security.Cryptography; using System.Threading.Tasks; using System.Xml; using Microsoft.IdentityModel.Protocols; diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/AbstractVirtualsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/AbstractVirtualsTests.cs index 4ba9587475..2194ddd6a7 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/AbstractVirtualsTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/AbstractVirtualsTests.cs @@ -180,7 +180,7 @@ public async Task TokenHandler_ValidationParameters_ValidateTokenAsyncString() } catch (Exception ex) { - Assert.Contains("internal virtual Task> " + + Assert.Contains("internal virtual Task> " + "ValidateTokenAsync(string token, ValidationParameters validationParameters, CallContext callContext, CancellationToken cancellationToken)", ex.Message); @@ -203,7 +203,7 @@ public async Task TokenHandler_ValidationParameters_ValidateTokenAsyncToken() } catch (Exception ex) { - Assert.Contains("internal virtual Task> " + + Assert.Contains("internal virtual Task> " + "ValidateTokenAsync(SecurityToken token, ValidationParameters validationParameters, CallContext callContext, CancellationToken cancellationToken)", ex.Message); diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Extensibility/ExtensibilityTestProvider.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Extensibility/ExtensibilityTestProvider.cs index 58af4db000..bcb30a94d6 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Extensibility/ExtensibilityTestProvider.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Extensibility/ExtensibilityTestProvider.cs @@ -14,9 +14,9 @@ namespace Microsoft.IdentityModel.Tokens.Extensibility.Tests /// These extensibility tests are designed to show consistency across different token handlers for the different validation steps. /// Coverage is provided for the following: /// 1. The delegates throwing - /// 2. The delegates return a xxxValidationError, with a ValidationFailureType that is known to the xxxValidationError and one that is not known. - /// 3. The delegates return a CustomxxxValidationError, with a ValidationFailureType that is known to the CustomxxxValidationError and one that is not known. - /// 4. The delegates sets a ValidationFailureType that is NOT known to the xxxValidationError or the CustomxxxValidationError. + /// 2. The delegates return a xxxValidationError, with a FailureType that is known to the xxxValidationError and one that is not known. + /// 3. The delegates return a CustomxxxValidationError, with a FailureType that is known to the CustomxxxValidationError and one that is not known. + /// 4. The delegates sets a FailureType that is NOT known to the xxxValidationError or the CustomxxxValidationError. /// public class ExtensibilityTestProvider { @@ -30,7 +30,7 @@ public static TheoryData GenerateInvalidAlgorithmTestCa CallContext callContext = new CallContext(); #region CustomAlgorithmValidationError - // Delegate sets ValidationFailureType to a value that is known to CustomAlgorithmValidationError + // Delegate sets FailureType to a value that is known to CustomAlgorithmValidationError // ValidationError: CustomAlgorithmValidationError // FailureType: CustomValidationFailure.ValidationFailed // Exception: CustomSecurityTokenInvalidAlgorithmException @@ -50,7 +50,7 @@ public static TheoryData GenerateInvalidAlgorithmTestCa $"{algorithm}") }); - // Delegate sets ValidationFailureType to a value that is known to AlgorithmValidationError + // Delegate sets FailureType to a value that is known to AlgorithmValidationError // ValidationError: AlgorithmValidationError // FailureType: AlgorithmValidationFailure.ValidationFailed // Exception: SecurityTokenInvalidAlgorithmException @@ -69,9 +69,9 @@ public static TheoryData GenerateInvalidAlgorithmTestCa $"{algorithm}"), }); - // Delegate sets ValidationFailureType to a value that is NOT known to AlgorithmValidationError or CustomAlgorithnmValidationError + // Delegate sets FailureType to a value that is NOT known to AlgorithmValidationError or CustomAlgorithnmValidationError // ValidationError: CustomAlgorithmValidationError - // FailureType: AudienceValidationFailure.AudienceDidNotMatch + // FailureType: AudienceValidationFailure.IssuerDidNotMatch // Exception: SecurityTokenValidationException theoryData.Add(new ExtensibilityTheoryData( "AlgorithmUnknownValidationFailure", @@ -83,14 +83,14 @@ public static TheoryData GenerateInvalidAlgorithmTestCa nameof(CustomAlgorithmValidationDelegates.UnknownValidationFailure)), ValidationError = new CustomAlgorithmValidationError( new MessageDetail(nameof(CustomAlgorithmValidationDelegates.UnknownValidationFailure)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, Default.GetStackFrame(), $"{algorithm}"), }); #endregion #region AlgorithmValidationError - // Delegate returns AlgorithmValidationError and sets ValidationFailureType to a value that is known to AlgorithmValidationError + // Delegate returns AlgorithmValidationError and sets FailureType to a value that is known to AlgorithmValidationError // ValidationError: AlgorithmValidationError // FailureType: AlgorithmValidationFailure.ValidationFailed // Exception: SecurityTokenInvalidAlgorithmException @@ -151,7 +151,7 @@ public static TheoryData GenerateAudienceTestCases( List tokenAudiences = [audience]; #region CustomAudienceValidationError - // Delegate sets ValidationFailureType to a value that is known to CustomAudienceValidationError + // Delegate sets FailureType to a value that is known to CustomAudienceValidationError // ValidationError: CustomAudienceValidationError // FailureType: CustomValidationFailure.AudienceValidationFailed // Exception: CustomSecurityTokenInvalidAudienceException @@ -172,9 +172,9 @@ public static TheoryData GenerateAudienceTestCases( null) }); - // Delegate sets ValidationFailureType to a value that is known to AudienceValidationError + // Delegate sets FailureType to a value that is known to AudienceValidationError // ValidationError: CustomAudienceValidationError - // FailureType: AudienceValidationFailure.AudienceDidNotMatch + // FailureType: AudienceValidationFailure.IssuerDidNotMatch // Exception: SecurityTokenInvalidAudienceException theoryData.Add(new ExtensibilityTheoryData( "AudienceDidNotMatch", @@ -186,15 +186,15 @@ public static TheoryData GenerateAudienceTestCases( nameof(CustomAudienceValidationDelegates.AudienceDidNotMatch)), ValidationError = new CustomAudienceValidationError( new MessageDetail(nameof(CustomAudienceValidationDelegates.AudienceDidNotMatch)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, Default.GetStackFrame(), tokenAudiences, null), }); - // Delegate sets ValidationFailureType to a value that is NOT known to AudienceValidationError or CustomAudienceValidationError + // Delegate sets FailureType to a value that is NOT known to AudienceValidationError or CustomAudienceValidationError // ValidationError: CustomAudienceValidationError - // FailureType: ValidationFailureType.AlgorithmIsNotSupported + // FailureType: FailureType.NotSupported // Exception: SecurityTokenValidationException theoryData.Add(new ExtensibilityTheoryData( "AudienceUnknownAudienceValidationFailure", @@ -206,7 +206,7 @@ public static TheoryData GenerateAudienceTestCases( nameof(CustomAudienceValidationDelegates.UnknownValidationFailure)), ValidationError = new CustomAudienceValidationError( new MessageDetail(nameof(CustomAudienceValidationDelegates.UnknownValidationFailure)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame(), tokenAudiences, null), @@ -214,9 +214,9 @@ public static TheoryData GenerateAudienceTestCases( #endregion #region AudienceValidationError - // Delegate returns AudienceValidationError and sets ValidationFailureType to a value that is known to AudienceValidationError + // Delegate returns AudienceValidationError and sets FailureType to a value that is known to AudienceValidationError // ValidationError: AudienceValidationError - // FailureType: AudienceValidationFailure.AudienceDidNotMatch + // FailureType: AudienceValidationFailure.IssuerDidNotMatch // Exception: SecurityTokenInvalidAudienceException theoryData.Add(new ExtensibilityTheoryData( "AudienceDidNotMatch", @@ -228,7 +228,7 @@ public static TheoryData GenerateAudienceTestCases( nameof(CustomAudienceValidationDelegates.AudienceValidatorDelegate)), ValidationError = new AudienceValidationError( new MessageDetail(nameof(CustomAudienceValidationDelegates.AudienceValidatorDelegate)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, Default.GetStackFrame(), tokenAudiences, null) @@ -277,7 +277,7 @@ public static TheoryData GenerateInvalidIssuerTestCases string issuerGuid = Guid.NewGuid().ToString(); #region return CustomIssuerValidationError - // Delegate sets ValidationFailureType to a value that is known to CustomIssuerValidationError + // Delegate sets FailureType to a value that is known to CustomIssuerValidationError // ValidationError: CustomIssuerValidationError // FailureType: CustomValidationError.ValidationFailed // Exception: CustomSecurityTokenInvalidIssuerException @@ -297,7 +297,7 @@ public static TheoryData GenerateInvalidIssuerTestCases issuerGuid) }); - // Delegate sets ValidationFailureType to a value that is known to IssuerValidationError + // Delegate sets FailureType to a value that is known to IssuerValidationError // ValidationError: CustomIssuerValidationError // FailureType: IssuerValidationFailure.ValidationFailed // Exception: SecurityTokenInvalidIssuerException @@ -316,9 +316,9 @@ public static TheoryData GenerateInvalidIssuerTestCases issuerGuid), }); - // Delegate sets ValidationFailureType to a value that is not known to IssuerValidationError or CustomIssuerValidationError + // Delegate sets FailureType to a value that is not known to IssuerValidationError or CustomIssuerValidationError // ValidationError: CustomIssuerValidationError - // FailureType: AlgorithmValidationFailure.AlgorithmIsNotSupported + // FailureType: AlgorithmValidationFailure.NotSupported // Exception: SecurityTokenValidationException theoryData.Add(new ExtensibilityTheoryData( "IssuerUnknownValidationFailure", @@ -330,14 +330,14 @@ public static TheoryData GenerateInvalidIssuerTestCases nameof(CustomIssuerValidationDelegates.UnknownValidationFailure)), ValidationError = new CustomIssuerValidationError( new MessageDetail(nameof(CustomIssuerValidationDelegates.UnknownValidationFailure)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame(), issuerGuid), }); #endregion #region return IssuerValidationError - // Delegate returns IssuerValidationError and sets ValidationFailureType to a value that is known to IssuerValidationError + // Delegate returns IssuerValidationError and sets FailureType to a value that is known to IssuerValidationError // ValidationError: IssuerValidationError // FailureType: IssuerValidationFailure.ValidationFailed // Exception: SecurityTokenInvalidIssuerException @@ -396,7 +396,7 @@ public static TheoryData GenerateInvalidIssuerSigningKe string issuerGuid = Guid.NewGuid().ToString(); #region return CustomIssuerSigningKeyValidationError - // Delegate sets ValidationFailureType to a value that is known to CustomIssuerSigningKeyValidationError + // Delegate sets FailureType to a value that is known to CustomIssuerSigningKeyValidationError // ValidationError: CustomIssuerSigningKeyValidationError // FailureType: CustomValidationFailure.ValidationFailed // Exception: CustomSecurityTokenInvalidSigningKeyException @@ -416,9 +416,9 @@ public static TheoryData GenerateInvalidIssuerSigningKe null) }); - // Delegate sets ValidationFailureType to a value that is known to SignatureKeyValidationError + // Delegate sets FailureType to a value that is known to SignatureKeyValidationError // ValidationError: CustomIssuerSigningKeyValidationError - // FailureType: ValidationFailureType.ValidationFailed + // FailureType: FailureType.ValidationFailed // Exception: SecurityTokenInvalidIssuerSigningKeyException theoryData.Add(new ExtensibilityTheoryData( "IssuerSigningKeyValidationFailed", @@ -435,9 +435,9 @@ public static TheoryData GenerateInvalidIssuerSigningKe null) }); - // Delegate sets ValidationFailureType to a value that is NOT known to SignatureKeyValidationError or CustomIssuerSigningKeyValidationError + // Delegate sets FailureType to a value that is NOT known to SignatureKeyValidationError or CustomIssuerSigningKeyValidationError // ValidationError: CustomIssuerSigningKeyValidationError - // FailureType: ValidationFailureType.AlgorithmIsNotSupported + // FailureType: FailureType.NotSupported // Exception: SecurityTokenValidationException theoryData.Add(new ExtensibilityTheoryData( "IssuerSigningKeyUnknownValidationFailure", @@ -448,16 +448,16 @@ public static TheoryData GenerateInvalidIssuerSigningKe ExpectedException = ExpectedException.SecurityTokenValidationException("UnknownValidationFailure"), ValidationError = new CustomIssuerSigningKeyValidationError( new MessageDetail(nameof(CustomIssuerSigningKeyValidationDelegates.UnknownValidationFailure)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame(), null) }); #endregion #region SignatureKeyValidationError - // Delegate returns SignatureKeyValidationError and sets ValidationFailureType to a value that is known to SignatureKeyValidationError + // Delegate returns SignatureKeyValidationError and sets FailureType to a value that is known to SignatureKeyValidationError // ValidationError: SignatureKeyValidationError - // FailureType: ValidationFailureType.ValidationFailed + // FailureType: FailureType.ValidationFailed // Exception: SecurityTokenInvalidIssuerSigningKeyException theoryData.Add(new ExtensibilityTheoryData( "IssuerSigningKeyDelegate", @@ -477,7 +477,7 @@ public static TheoryData GenerateInvalidIssuerSigningKe // Delegate throws CustomSecurityTokenInvalidIssuerSigningKeyException // ValidationError: SignatureKeyValidationError // Exception: SecurityTokenInvalidIssuerSigningKeyException - // FailureType: ValidationFailureType.ValidatorThrew + // FailureType: FailureType.ValidatorThrew // InnerException: CustomSecurityTokenInvalidSigningKeyException ExtensibilityTheoryData testCase = new ExtensibilityTheoryData( "IssuerSigningKeyValidatorThrows", @@ -517,7 +517,7 @@ public static TheoryData GenerateInvalidLifetimeTestCas DateTime utcPlusOneHour = utcNow.AddHours(1); #region return CustomLifetimeValidationError - // Delegate sets ValidationFailureType to a value that is known to CustomLifetimeValidationError + // Delegate sets FailureType to a value that is known to CustomLifetimeValidationError // ValidationError: CustomLifetimeValidationError // FailureType: CustomValidationFailure.LifetimeValidationFailed // Exception: CustomSecurityTokenInvalidLifetimeException @@ -539,7 +539,7 @@ public static TheoryData GenerateInvalidLifetimeTestCas utcPlusOneHour) }); - // Delegate sets ValidationFailureType to a value that is known to LifetimeValidationError + // Delegate sets FailureType to a value that is known to LifetimeValidationError // ValidationError: LifetimeValidationError // FailureType: LifetimeValidationFailure.ValidationFailed // Exception: SecurityTokenInvalidLifetimeException @@ -560,9 +560,9 @@ public static TheoryData GenerateInvalidLifetimeTestCas utcPlusOneHour), }); - // Delegate sets ValidationFailureType to a value that is NOT known to LifetimeValidationError or CustomLifetimeValidationError + // Delegate sets FailureType to a value that is NOT known to LifetimeValidationError or CustomLifetimeValidationError // ValidationError: CustomLifetimeValidationError - // FailureType: ValidationFailureType.AlgorithmIsNotSupported + // FailureType: FailureType.NotSupported // Exception: SecurityTokenValidationException theoryData.Add(new ExtensibilityTheoryData( "LifetimeUnknownValidationFailure", @@ -575,7 +575,7 @@ public static TheoryData GenerateInvalidLifetimeTestCas ValidationError = new CustomLifetimeValidationError( new MessageDetail( nameof(CustomLifetimeValidationDelegates.CustomUnknownValidationFailure)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame(), utcNow, utcPlusOneHour), @@ -583,7 +583,7 @@ public static TheoryData GenerateInvalidLifetimeTestCas #endregion #region return IssuerSigningKeyValidationError - // Delegate returns LifetimeValidationError and sets ValidationFailureType to a value that is known to LifetimeValidationError + // Delegate returns LifetimeValidationError and sets FailureType to a value that is known to LifetimeValidationError // ValidationError: LifetimeValidationError // FailureType: LifetimeValidationFailure.ValidationFailed // Exception: SecurityTokenLifetimeSigningKeyException @@ -648,7 +648,7 @@ public static TheoryData GenerateInvalidSignatureTestCa string issuerGuid = Guid.NewGuid().ToString(); #region return CustomSignatureValidationError - // Delegate sets ValidationFailureType to a value that is known to CustomSignatureValidationError + // Delegate sets FailureType to a value that is known to CustomSignatureValidationError // ValidationError: CustomSignatureValidationError // FailureType: CustomValidationFailures.ValidationFailed // Exception: CustomSecurityTokenInvalidSignatureException @@ -667,9 +667,9 @@ public static TheoryData GenerateInvalidSignatureTestCa Default.GetStackFrame()) }); - // Delegate sets ValidationFailureType to a value that is known to SignatureValidationError + // Delegate sets FailureType to a value that is known to SignatureValidationError // ValidationError: SignatureValidationError - // FailureType: ValidationFailureType.ValidationFailed + // FailureType: FailureType.ValidationFailed // Exception: SecurityTokenInvalidSignatureException theoryData.Add(new ExtensibilityTheoryData( "SignatureValidationFailed", @@ -685,9 +685,9 @@ public static TheoryData GenerateInvalidSignatureTestCa Default.GetStackFrame()), }); - // Delegate sets ValidationFailureType to a value that is NOT known to SignatureValidationError or CustomSignatureValidationError + // Delegate sets FailureType to a value that is NOT known to SignatureValidationError or CustomSignatureValidationError // ValidationError: CustomLifetimeValidationError - // FailureType: ValidationFailureType.AlgorithmIsNotSupported + // FailureType: FailureType.NotSupported // Exception: SecurityTokenValidationException theoryData.Add(new ExtensibilityTheoryData( "SignatureUnknownValidationFailure", @@ -699,13 +699,13 @@ public static TheoryData GenerateInvalidSignatureTestCa nameof(CustomSignatureValidationDelegates.UnknownValidationFailure)), ValidationError = new CustomSignatureValidationError( new MessageDetail(nameof(CustomSignatureValidationDelegates.UnknownValidationFailure)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame()), }); #endregion #region return SignatureValidationError - // Delegate returns SignatureValidationError and sets ValidationFailureType to a value that is known to SignatureValidationError + // Delegate returns SignatureValidationError and sets FailureType to a value that is known to SignatureValidationError // ValidationError: SignatureValidationError // FailureType: SignatureValidationFailure.ValidationFailed // Exception: SecurityTokenInvalidSignatureException @@ -726,7 +726,7 @@ public static TheoryData GenerateInvalidSignatureTestCa // Delegate throws CustomSecurityTokenInvalidSignatureException // ValidationError: SignatureValidationError // Exception: SecurityTokenInvalidSignatureException - // FailureType: ValidationFailureType.ValidatorThrew + // FailureType: FailureType.ValidatorThrew // InnerException: CustomSecurityTokenInvalidSignatureException ExtensibilityTheoryData testCase = new ExtensibilityTheoryData( "SignatureValidatorThrows", @@ -763,7 +763,7 @@ public static TheoryData GenerateInvalidTokenReplayTest DateTime expirationTime = DateTime.UtcNow + TimeSpan.FromHours(1); #region return CustomTokenReplayValidationError - // Delegate sets ValidationFailureType to a value that is known to CustomTokenReplayValidationError + // Delegate sets FailureType to a value that is known to CustomTokenReplayValidationError // ValidationError: CustomTokenReplayValidationError // FailureType: CustomValidationFailure.ValidationFailed // Exception: CustomSecurityTokenReplayDetectedException @@ -783,7 +783,7 @@ public static TheoryData GenerateInvalidTokenReplayTest expirationTime) }); - // Delegate sets ValidationFailureType to a value that is known to TokenReplayDetectedError + // Delegate sets FailureType to a value that is known to TokenReplayDetectedError // ValidationError: TokenReplayValidationError // FailureType: TokenReplayValidationFailure.ValidationFailed // Exception: SecurityTokenReplayDetectedException @@ -802,10 +802,10 @@ public static TheoryData GenerateInvalidTokenReplayTest expirationTime), }); - // Delegate sets ValidationFailureType to a value that is NOT known to + // Delegate sets FailureType to a value that is NOT known to // TokenReplayValidationError or CustomTokenReplayValidationError // ValidationError: CustomTokenReplayValidationError - // FailureType: ValidationFailureType.AlgorithmIsNotSupported + // FailureType: FailureType.NotSupported // Exception: SecurityTokenValidationException theoryData.Add(new ExtensibilityTheoryData( "TokenReplayUnknownValidationFailure", @@ -817,14 +817,14 @@ public static TheoryData GenerateInvalidTokenReplayTest nameof(CustomTokenReplayValidationDelegates.UnknownValidationFailure)), ValidationError = new CustomTokenReplayValidationError( new MessageDetail(nameof(CustomTokenReplayValidationDelegates.UnknownValidationFailure)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame(), expirationTime) }); #endregion #region return TokenReplayValidationError - // Delegate returns TokenReplayValidationError and sets ValidationFailureType to a value that is known to TokenReplayValidationError + // Delegate returns TokenReplayValidationError and sets FailureType to a value that is known to TokenReplayValidationError // ValidationError: TokenReplayValidationError // FailureType: TokenReplayValidationFailure.ValidationFailed // Exception: SecurityTokenReplayDetectedException @@ -894,7 +894,7 @@ public static TheoryData GenerateInvalidTokenTypeTestCa string tokenType = "NOTJWT"; #region return CustomTokenTypeValidationError - // Delegate sets ValidationFailureType to a value that is known to CustomTokenTypeValidationError + // Delegate sets FailureType to a value that is known to CustomTokenTypeValidationError // ValidationError: CustomTokenTypeValidationError // FailureType: CustomValidationFailure.ValidationFailed // Exception: CustomSecurityTokenInvalidTypeException @@ -914,7 +914,7 @@ public static TheoryData GenerateInvalidTokenTypeTestCa tokenType) }); - // Delegate sets ValidationFailureType to a value that is known to TokenTypeValidationError + // Delegate sets FailureType to a value that is known to TokenTypeValidationError // ValidationError: TokenTypeValidationError // FailureType: TokenTypeValidationFailure.ValidationFailed // Exception: SecurityTokenInvalidTypeException @@ -933,9 +933,9 @@ public static TheoryData GenerateInvalidTokenTypeTestCa tokenType) }); - // Delegate sets ValidationFailureType to a value that is NOT known to TokenTypeValidationError or CustomTokenTypeValidationError + // Delegate sets FailureType to a value that is NOT known to TokenTypeValidationError or CustomTokenTypeValidationError // ValidationError: CustomTokenTypeValidationError - // FailureType: ValidationFailureType.AlgorithmIsNotSupported + // FailureType: FailureType.NotSupported // Exception: SecurityTokenValidationException theoryData.Add(new ExtensibilityTheoryData( "TokenTypeUnknownValidationFailure", @@ -947,14 +947,14 @@ public static TheoryData GenerateInvalidTokenTypeTestCa nameof(CustomTokenTypeValidationDelegates.UnknownValidationFailure)), ValidationError = new CustomTokenTypeValidationError( new MessageDetail(nameof(CustomTokenTypeValidationDelegates.UnknownValidationFailure)), - AlgorithmValidationFailure.AlgorithmIsNotSupported, + AlgorithmValidationFailure.NotSupported, Default.GetStackFrame(), tokenType), }); #endregion #region return TokenTypeValidationError - // Delegate returns TokenTypeValidationError and sets ValidationFailureType to a value that is known to TokenTypeValidationError + // Delegate returns TokenTypeValidationError and sets FailureType to a value that is known to TokenTypeValidationError // ValidationError: TokenTypeValidationError // FailureType: TokenTypeValidationFailure.ValidationFailed // Exception: SecurityTokenInvalidTokenTypeException diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidation/JwtValidation.cs b/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidation/JwtValidation.cs index 55c3c110a9..9ce01a4206 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidation/JwtValidation.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidation/JwtValidation.cs @@ -10,6 +10,39 @@ namespace Microsoft.IdentityModel.Tokens.TokenValidation.Tests { public class JwtValidation { + #region Parameters + [Theory, MemberData(nameof(InvalidSecurityTokenParameterTestCases), DisableDiscoveryEnumeration = true)] + public async Task InvalidSecurityTokenParameters(ValidateTokenTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.InvalidSecurityTokenParameters", theoryData); + + await ValidationUtils.RunSecurityTokenParameterTest(theoryData, context); + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData InvalidSecurityTokenParameterTestCases() + { + return TestCaseProvider.GenerateInvalidSecurityTokenParameterTestCases(new JsonWebTestingTokenHandler()); + } + + [Theory, MemberData(nameof(InvalidTokenParametersTestCases), DisableDiscoveryEnumeration = true)] + public async Task InvalidTokenParameters(ValidateTokenTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.InvalidTokenParameters", theoryData); + + await ValidationUtils.RunTokenParameterTest(theoryData, context); + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData InvalidTokenParametersTestCases() + { + return TestCaseProvider.GenerateInvalidTokenParameterTestCases(new JsonWebTestingTokenHandler()); + } + #endregion + + #region Algorithm [Theory, MemberData(nameof(InvalidAlgorithmTestCases), DisableDiscoveryEnumeration = true)] public async Task InvalidAlgorithm(ValidateTokenTheoryData theoryData) diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidation/Saml2Validation.cs b/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidation/Saml2Validation.cs index e7cbdf450e..c948d3ebf1 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidation/Saml2Validation.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidation/Saml2Validation.cs @@ -10,6 +10,38 @@ namespace Microsoft.IdentityModel.Tokens.TokenValidation.Tests #nullable enable public class Saml2Validation { + #region Parameters + [Theory, MemberData(nameof(InvalidSecurityTokenParameterTestCases), DisableDiscoveryEnumeration = true)] + public async Task InvalidSecurityTokenParameters(ValidateTokenTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.InvalidSecurityTokenParameters", theoryData); + + await ValidationUtils.RunSecurityTokenParameterTest(theoryData, context); + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData InvalidSecurityTokenParameterTestCases() + { + return TestCaseProvider.GenerateInvalidSecurityTokenParameterTestCases(new Saml2SecurityTestingTokenHandler()); + } + + [Theory, MemberData(nameof(InvalidTokenParametersTestCases), DisableDiscoveryEnumeration = true)] + public async Task InvalidTokenParameters(ValidateTokenTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.InvalidTokenParameters", theoryData); + + await ValidationUtils.RunTokenParameterTest(theoryData, context); + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData InvalidTokenParametersTestCases() + { + return TestCaseProvider.GenerateInvalidTokenParameterTestCases(new Saml2SecurityTestingTokenHandler()); + } + #endregion + #region Algorithm [Theory, MemberData(nameof(InvalidAlgorithmTestCases), DisableDiscoveryEnumeration = true)] public async Task InvalidAlgorithm(ValidateTokenTheoryData theoryData) diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidation/SamlValidation.cs b/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidation/SamlValidation.cs index bd3c894d61..9dcb228aa2 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidation/SamlValidation.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidation/SamlValidation.cs @@ -10,6 +10,38 @@ namespace Microsoft.IdentityModel.Tokens.TokenValidation.Tests { public class SamlValidation { + #region Parameters + [Theory, MemberData(nameof(InvalidSecurityTokenParameterTestCases), DisableDiscoveryEnumeration = true)] + public async Task InvalidSecurityTokenParameters(ValidateTokenTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.InvalidSecurityTokenParameters", theoryData); + + await ValidationUtils.RunSecurityTokenParameterTest(theoryData, context); + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData InvalidSecurityTokenParameterTestCases() + { + return TestCaseProvider.GenerateInvalidSecurityTokenParameterTestCases(new SamlSecurityTestingTokenHandler()); + } + + [Theory, MemberData(nameof(InvalidTokenParametersTestCases), DisableDiscoveryEnumeration = true)] + public async Task InvalidTokenParameters(ValidateTokenTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.InvalidTokenParameters", theoryData); + + await ValidationUtils.RunTokenParameterTest(theoryData, context); + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData InvalidTokenParametersTestCases() + { + return TestCaseProvider.GenerateInvalidTokenParameterTestCases(new SamlSecurityTestingTokenHandler()); + } + #endregion + #region Algorithm [Theory, MemberData(nameof(InvalidAlgorithmTestCases), DisableDiscoveryEnumeration = true)] public async Task InvalidAlgorithm(ValidateTokenTheoryData theoryData) diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AlgorithmValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AlgorithmValidationResultTests.cs index df6595e948..f0ddefd78a 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AlgorithmValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AlgorithmValidationResultTests.cs @@ -1,18 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using Microsoft.IdentityModel.TestUtils; -using Microsoft.IdentityModel.Logging; -using Xunit; using System; +using System.Diagnostics; +using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.TestUtils; using Microsoft.IdentityModel.Tokens.Experimental; +using Xunit; namespace Microsoft.IdentityModel.Tokens.Validation.Tests { public class AlgorithmValidationTests { [Theory, MemberData(nameof(InvalidTestCases), DisableDiscoveryEnumeration = true)] - public void InvalidAlgorithms(AlgorithmTheoryData theoryData) + public void InvalidAlgorithms(ValidateAlgorithmTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.InvalidAlgorithms", theoryData); @@ -31,10 +32,12 @@ public void InvalidAlgorithms(AlgorithmTheoryData theoryData) } else { + ValidationError validationError = validationResult.Error; + TestUtilities.RecordIfMoveNextFound(context, validationError); IdentityComparer.AreStringsEqual( validationError.FailureType.Name, - theoryData.OperationResult.Error.FailureType.Name, + theoryData.ValidationResult.Error.FailureType.Name, context); theoryData.ExpectedException.ProcessException(validationError.GetException(), context); @@ -42,28 +45,28 @@ public void InvalidAlgorithms(AlgorithmTheoryData theoryData) } catch (Exception ex) { - context.AddDiff($"Did not expect an exception: {ex}."); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } TestUtilities.AssertFailIfErrors(context); } - public static TheoryData InvalidTestCases + public static TheoryData InvalidTestCases { get { SecurityKey securityKey = new SymmetricSecurityKey(new byte[256]); - return new TheoryData + return new TheoryData { - new AlgorithmTheoryData("ValidationParametersNull") + new ValidateAlgorithmTheoryData("ValidationParametersNull") { Algorithm = null, ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), SecurityKey = null, SecurityToken = null, ValidationParameters = null, - OperationResult = new AlgorithmValidationError( + ValidationResult = new AlgorithmValidationError( new MessageDetail( LogMessages.IDX10000, LogHelper.MarkAsNonPII("validationParameters")), @@ -72,7 +75,7 @@ public static TheoryData InvalidTestCases null, null) // InvalidAlgorithm }, - new AlgorithmTheoryData("InvalidAlgorithm") + new ValidateAlgorithmTheoryData("InvalidAlgorithm") { Algorithm = SecurityAlgorithms.Sha256, ExpectedException = ExpectedException.SecurityTokenInvalidAlgorithmException("IDX10696:"), @@ -80,11 +83,11 @@ public static TheoryData InvalidTestCases SecurityToken = null, ValidationParameters = ValidationUtils.CreateValidationParameters( algorithms: [SecurityAlgorithms.HmacSha256]), - OperationResult = new AlgorithmValidationError( + ValidationResult = new AlgorithmValidationError( new MessageDetail( LogMessages.IDX10696, LogHelper.MarkAsNonPII(SecurityAlgorithms.Sha256)), - AlgorithmValidationFailure.ValidationFailed, + AlgorithmValidationFailure.NotSupported, null, // StackFrame SecurityAlgorithms.Sha256, null) // InvalidAlgorithm @@ -94,7 +97,7 @@ public static TheoryData InvalidTestCases } [Theory, MemberData(nameof(ValidTestCases), DisableDiscoveryEnumeration = true)] - public void ValidAlgorithms(AlgorithmTheoryData theoryData) + public void ValidAlgorithms(ValidateAlgorithmTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.ValidAlgorithms", theoryData); @@ -111,7 +114,7 @@ public void ValidAlgorithms(AlgorithmTheoryData theoryData) { IdentityComparer.AreStringsEqual( validationResult.Result, - theoryData.OperationResult.Result, + theoryData.ValidationResult.Result, context); } else @@ -121,54 +124,40 @@ public void ValidAlgorithms(AlgorithmTheoryData theoryData) } catch (Exception ex) { - context.AddDiff($"Did not expect an exception: {ex}"); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } TestUtilities.AssertFailIfErrors(context); } - public static TheoryData ValidTestCases + public static TheoryData ValidTestCases { get { SecurityKey securityKey = new SymmetricSecurityKey(new byte[256]); - return new TheoryData + return new TheoryData { - new AlgorithmTheoryData("ValidateAlgorithmWhenValidAlgorithmsIsEmpty") + new ValidateAlgorithmTheoryData("ValidateAlgorithmWhenValidAlgorithmsIsEmpty") { Algorithm = SecurityAlgorithms.Sha256, SecurityKey = securityKey, SecurityToken = null, ValidationParameters = ValidationUtils.CreateValidationParameters( algorithms: []), - OperationResult = SecurityAlgorithms.Sha256 + ValidationResult = SecurityAlgorithms.Sha256 }, - new AlgorithmTheoryData("ValidateAlgorithmDefaultAlgorithmValidation") + new ValidateAlgorithmTheoryData("ValidateAlgorithmDefaultAlgorithmValidation") { Algorithm = SecurityAlgorithms.Sha256, SecurityKey = securityKey, SecurityToken = null, ValidationParameters = ValidationUtils.CreateValidationParameters( algorithms: [SecurityAlgorithms.HmacSha256, SecurityAlgorithms.Sha256]), - OperationResult = SecurityAlgorithms.Sha256 + ValidationResult = SecurityAlgorithms.Sha256 } }; } } - public class AlgorithmTheoryData : TheoryDataBase - { - public AlgorithmTheoryData(string testId) : base(testId) { } - - public string Algorithm { get; set; } - - public SecurityKey SecurityKey { get; set; } - - public SecurityToken SecurityToken { get; set; } - - internal ValidationParameters ValidationParameters { get; set; } - - internal ValidationResult OperationResult { get; set; } - } } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AudienceValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AudienceValidationResultTests.cs index 1e3dd84f48..9085a9adab 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AudienceValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AudienceValidationResultTests.cs @@ -12,14 +12,14 @@ namespace Microsoft.IdentityModel.Tokens.Validation.Tests { + [CollectionDefinition("AudienceValidationTests", DisableParallelization = true)] public class AudienceValidationTests { [Theory, MemberData(nameof(InvalidParameterTestCases), DisableDiscoveryEnumeration = true)] - public void InvalidAudienceParameters(AudienceValidationTheoryData theoryData) + public void InvalidAudienceParameters(ValidateAudienceTheoryData theoryData) { - // TODO should not run in sequential - AppContext.SetSwitch(AppContextSwitches.DoNotScrubExceptionsSwitch, theoryData.DoNotScrubErrorMessages); CompareContext context = TestUtilities.WriteHeader($"{this}.InvalidAudienceParameters", theoryData); + AppContext.SetSwitch(AppContextSwitches.DoNotScrubExceptionsSwitch, theoryData.DoNotScrubErrorMessages); try { @@ -43,12 +43,12 @@ public void InvalidAudienceParameters(AudienceValidationTheoryData theoryData) ValidationError validationError = validationResult.Error; IdentityComparer.AreStringsEqual( validationError.FailureType.Name, - theoryData.OperationResult.Error.FailureType.Name, + theoryData.ValidationResult.Error.FailureType.Name, context); IdentityComparer.AreStringsEqual( validationError.MessageDetail.Message, - theoryData.OperationResult.Error.MessageDetail.Message, + theoryData.ValidationResult.Error.MessageDetail.Message, context); theoryData.ExpectedException.ProcessException(validationError.GetException(), context); @@ -56,7 +56,7 @@ public void InvalidAudienceParameters(AudienceValidationTheoryData theoryData) } catch (Exception ex) { - context.Diffs.Add($"Unexpected exception thrown: {ex.Message}. TestId {theoryData.TestId}."); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } finally { @@ -66,41 +66,42 @@ public void InvalidAudienceParameters(AudienceValidationTheoryData theoryData) TestUtilities.AssertFailIfErrors(context); } - public static TheoryData InvalidParameterTestCases + public static TheoryData InvalidParameterTestCases { get { - return new TheoryData + return new TheoryData { - new AudienceValidationTheoryData("ValidationParametersNull") + new ValidateAudienceTheoryData("ValidationParametersNull") { TokenAudiences = new List { "audience1" }, ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), ValidationParameters = null, - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( MessageDetail.NullParameter("validationParameters"), ValidationFailureType.NullArgument, null, null, null) }, - new AudienceValidationTheoryData("AudiencesNull") + new ValidateAudienceTheoryData("AudiencesNull") { TokenAudiences = null, ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - OperationResult = new AudienceValidationError( + ValidationParameters = new ValidationParameters(), + ValidationResult = new AudienceValidationError( MessageDetail.NullParameter("tokenAudiences"), ValidationFailureType.NullArgument, null, null, null) }, - new AudienceValidationTheoryData("AudiencesEmptyList") + new ValidateAudienceTheoryData("AudiencesEmptyList") { TokenAudiences = new List { }, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10206:"), ValidationParameters = new ValidationParameters(), - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10206, null), @@ -109,64 +110,64 @@ public static TheoryData InvalidParameterTestCases null, null) }, - new AudienceValidationTheoryData("AudiencesEmptyString_ScrubbedMessage") + new ValidateAudienceTheoryData("AudiencesEmptyString_ScrubbedMessage") { TokenAudiences = new List { string.Empty }, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters(), ValidAudiences = ["audience1"], - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215S), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, null, null) }, - new AudienceValidationTheoryData("AudiencesWhiteSpace_ScrubbedMessage") + new ValidateAudienceTheoryData("AudiencesWhiteSpace_ScrubbedMessage") { TokenAudiences = new List { " " }, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters(), ValidAudiences = ["audience1"], - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215S), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, null, null) }, - new AudienceValidationTheoryData("AudiencesEmptyString") + new ValidateAudienceTheoryData("AudiencesEmptyString") { TokenAudiences = new List { string.Empty }, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters(), ValidAudiences = ["audience1"], - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(string.Empty), LogHelper.MarkAsNonPII("audience1")), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, null, null), DoNotScrubErrorMessages = true }, - new AudienceValidationTheoryData("AudiencesWhiteSpace") + new ValidateAudienceTheoryData("AudiencesWhiteSpace") { TokenAudiences = new List { " " }, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters(), ValidAudiences = ["audience1"], - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(" "), LogHelper.MarkAsNonPII("audience1")), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, null, null), @@ -177,7 +178,7 @@ public static TheoryData InvalidParameterTestCases } [Theory, MemberData(nameof(InValidTestCases), DisableDiscoveryEnumeration = true)] - public void InvalidAudiences(AudienceValidationTheoryData theoryData) + public void InvalidAudiences(ValidateAudienceTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.InvalidAudiences", theoryData); @@ -205,7 +206,7 @@ public void InvalidAudiences(AudienceValidationTheoryData theoryData) ValidationError validationError = validationResult.Error; IdentityComparer.AreStringsEqual( validationError.FailureType.Name, - theoryData.OperationResult.Error.FailureType.Name, + theoryData.ValidationResult.Error.FailureType.Name, context); theoryData.ExpectedException.ProcessException(validationError.GetException(), context); @@ -213,13 +214,13 @@ public void InvalidAudiences(AudienceValidationTheoryData theoryData) } catch (Exception ex) { - context.Diffs.Add($"Unexpected exception thrown: {ex.Message}. TestId {theoryData.TestId}."); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } TestUtilities.AssertFailIfErrors(context); } - public static TheoryData InValidTestCases + public static TheoryData InValidTestCases { get { @@ -238,214 +239,214 @@ public static TheoryData InValidTestCases var commaAudience1Slash = commaAudience1 + "/"; var commaAudience2Slash = commaAudience2 + "/"; - return new TheoryData + return new TheoryData { - new AudienceValidationTheoryData("SameLengthNotMatched") + new ValidateAudienceTheoryData("SameLengthNotMatched") { TokenAudiences = audiences1, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters(), ValidAudiences = [audience2], SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, "Issuer"), - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(commaAudience1), LogHelper.MarkAsNonPII(audience2)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, audiences1, [audience2]) }, - new AudienceValidationTheoryData("AudiencesValidAudienceWithSlashNotMatched") + new ValidateAudienceTheoryData("AudiencesValidAudienceWithSlashNotMatched") { TokenAudiences = audiences1, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters(), ValidAudiences = [audience2 + "/"], SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, "Issuer"), - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(commaAudience1), LogHelper.MarkAsNonPII(audience2Slash)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, audiences1, [audience2 + "/"]) }, - new AudienceValidationTheoryData("AudiencesWithSlashValidAudienceSameLengthNotMatched") + new ValidateAudienceTheoryData("AudiencesWithSlashValidAudienceSameLengthNotMatched") { TokenAudiences = audiences2WithSlash, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters(), ValidAudiences = [audience1], - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(commaAudience2Slash), LogHelper.MarkAsNonPII(audience1)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, audiences1WithSlash, [audience1]) }, - new AudienceValidationTheoryData("ValidAudienceWithSlash_IgnoreTrailingSlashFalse") + new ValidateAudienceTheoryData("ValidAudienceWithSlash_IgnoreTrailingSlashFalse") { TokenAudiences = audiences1, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters{ IgnoreTrailingSlashWhenValidatingAudience = false }, ValidAudiences = [audience1 + "/"], - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(commaAudience1), LogHelper.MarkAsNonPII(audience1Slash)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, audiences1, [audience1 + "/"]) }, - new AudienceValidationTheoryData("ValidAudiencesWithSlash_IgnoreTrailingSlashFalse") + new ValidateAudienceTheoryData("ValidAudiencesWithSlash_IgnoreTrailingSlashFalse") { TokenAudiences = audiences1, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters{ IgnoreTrailingSlashWhenValidatingAudience = false }, ValidAudiences = audiences1WithSlash, - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(commaAudience1), LogHelper.MarkAsNonPII(commaAudience1Slash)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, audiences1, audiences1WithSlash) }, - new AudienceValidationTheoryData("ValidAudienceWithExtraChar") + new ValidateAudienceTheoryData("ValidAudienceWithExtraChar") { TokenAudiences = audiences1, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters(), ValidAudiences = [audience1 + "A"], - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(commaAudience1), LogHelper.MarkAsNonPII(audience1 + "A")), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, audiences1, [audience1 + "A"]) }, - new AudienceValidationTheoryData("AudienceWithDoubleSlash_IgnoreTrailingSlashTrue") + new ValidateAudienceTheoryData("AudienceWithDoubleSlash_IgnoreTrailingSlashTrue") { TokenAudiences = audiences1, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters(), ValidAudiences = [audience1 + "//"], - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(commaAudience1), LogHelper.MarkAsNonPII(audience1 + "//")), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, audiences1, [audience1 + "//"]) }, - new AudienceValidationTheoryData("AudiencesWithDoubleSlash_IgnoreTrailingSlashTrue") + new ValidateAudienceTheoryData("AudiencesWithDoubleSlash_IgnoreTrailingSlashTrue") { TokenAudiences = audiences1, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters(), ValidAudiences = audiences1WithTwoSlashes, - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(commaAudience1), LogHelper.MarkAsNonPII(commaAudience1 + "//")), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, audiences1, audiences1WithTwoSlashes) }, - new AudienceValidationTheoryData("TokenAudienceWithSlash_IgnoreTrailingSlashFalse") + new ValidateAudienceTheoryData("TokenAudienceWithSlash_IgnoreTrailingSlashFalse") { TokenAudiences = audiences1WithSlash, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters{ IgnoreTrailingSlashWhenValidatingAudience = false }, ValidAudiences = [audience1], - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(commaAudience1Slash), LogHelper.MarkAsNonPII(audience1)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, audiences1WithSlash, [audience1]) }, - new AudienceValidationTheoryData("TokenAudienceWithSlashNotEqual") + new ValidateAudienceTheoryData("TokenAudienceWithSlashNotEqual") { TokenAudiences = audiences2WithSlash, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters(), ValidAudiences = [audience1], - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(commaAudience2Slash), LogHelper.MarkAsNonPII(audience1)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, audiences2WithSlash, [audience1]) }, - new AudienceValidationTheoryData("TokenAudiencesWithSlash_IgnoreTrailingSlashFalse") + new ValidateAudienceTheoryData("TokenAudiencesWithSlash_IgnoreTrailingSlashFalse") { TokenAudiences = audiences1WithSlash, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters{ IgnoreTrailingSlashWhenValidatingAudience = false }, ValidAudiences = [audience1], - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(commaAudience1Slash), LogHelper.MarkAsNonPII(audience1)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, audiences1WithSlash, [audience1]) }, - new AudienceValidationTheoryData("TokenAudiencesWithSlashValidAudiencesNotMatched_IgnoreTrailingSlashTrue") + new ValidateAudienceTheoryData("TokenAudiencesWithSlashValidAudiencesNotMatched_IgnoreTrailingSlashTrue") { TokenAudiences = audiences1WithSlash, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters(), ValidAudiences = audiences2, - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(commaAudience1Slash), LogHelper.MarkAsNonPII(commaAudience2)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, audiences1WithSlash, audiences2) }, - new AudienceValidationTheoryData("TokenAudienceWithTwoSlashesVPTrue") + new ValidateAudienceTheoryData("TokenAudienceWithTwoSlashesVPTrue") { TokenAudiences = audiences1WithTwoSlashes, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), ValidationParameters = new ValidationParameters(), ValidAudiences = [audience1], - OperationResult = new AudienceValidationError( + ValidationResult = new AudienceValidationError( new MessageDetail( LogMessages.IDX10215, LogHelper.MarkAsNonPII(commaAudience1 + "//"), LogHelper.MarkAsNonPII(audience1)), - AudienceValidationFailure.AudienceDidNotMatch, + AudienceValidationFailure.DidNotMatch, null, audiences1WithTwoSlashes, [audience1]) @@ -455,7 +456,7 @@ public static TheoryData InValidTestCases } [Theory, MemberData(nameof(ValidTestCases), DisableDiscoveryEnumeration = true)] - public void ValidAudiences(AudienceValidationTheoryData theoryData) + public void ValidAudiences(ValidateAudienceTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.ValidAudiences", theoryData); @@ -478,7 +479,7 @@ public void ValidAudiences(AudienceValidationTheoryData theoryData) { IdentityComparer.AreStringsEqual( validationResult.Result, - theoryData.OperationResult.Result, + theoryData.ValidationResult.Result, context); } else @@ -488,13 +489,13 @@ public void ValidAudiences(AudienceValidationTheoryData theoryData) } catch (Exception ex) { - context.Diffs.Add($"Unexpected exception thrown: {ex.Message}. TestId {theoryData.TestId}."); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } TestUtilities.AssertFailIfErrors(context); } - public static TheoryData ValidTestCases + public static TheoryData ValidTestCases { get { @@ -503,58 +504,47 @@ public static TheoryData ValidTestCases List audiences1 = new List { "", audience1 }; List audiences1WithSlash = new List { "", audience1 + "/" }; - return new TheoryData + return new TheoryData { - new AudienceValidationTheoryData("SameLengthMatched") + new ValidateAudienceTheoryData("SameLengthMatched") { TokenAudiences = audiences1, ValidationParameters = new ValidationParameters(), ValidAudiences = [audience1], SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, "Issuer"), - OperationResult = audience1 + ValidationResult = audience1 }, - new AudienceValidationTheoryData("AudienceWithSlash_IgnoreTrailingSlashTrue") + new ValidateAudienceTheoryData("AudienceWithSlash_IgnoreTrailingSlashTrue") { TokenAudiences = audiences1, ValidationParameters = new ValidationParameters(), ValidAudiences = [audience1 + "/"], - OperationResult = audience1 + ValidationResult = audience1 }, - new AudienceValidationTheoryData("AudiencesWithSlash_IgnoreTrailingSlashTrue") + new ValidateAudienceTheoryData("AudiencesWithSlash_IgnoreTrailingSlashTrue") { TokenAudiences = audiences1, ValidationParameters = new ValidationParameters(), ValidAudiences = audiences1WithSlash, - OperationResult = audience1 + ValidationResult = audience1 }, - new AudienceValidationTheoryData("TokenAudienceWithSlash_IgnoreTrailingSlashTrue") + new ValidateAudienceTheoryData("TokenAudienceWithSlash_IgnoreTrailingSlashTrue") { TokenAudiences = audiences1WithSlash, ValidationParameters = new ValidationParameters(), ValidAudiences = [audience1], - OperationResult = audience1Slash + ValidationResult = audience1Slash }, - new AudienceValidationTheoryData("TokenAudiencesWithSlash_IgnoreTrailingSlashTrue") + new ValidateAudienceTheoryData("TokenAudiencesWithSlash_IgnoreTrailingSlashTrue") { TokenAudiences = audiences1WithSlash, ValidationParameters = new ValidationParameters(), ValidAudiences = [audience1], - OperationResult = audience1Slash + ValidationResult = audience1Slash } }; } } - public class AudienceValidationTheoryData : TheoryDataBase - { - public AudienceValidationTheoryData(string testId) : base(testId) { } - public List TokenAudiences { get; set; } - public SecurityToken SecurityToken { get; set; } - internal ValidationParameters ValidationParameters { get; set; } = new ValidationParameters(); - internal ValidationFailureType ValidationFailure { get; set; } - public List ValidAudiences { get; set; } - internal ValidationResult OperationResult { get; set; } - internal bool DoNotScrubErrorMessages { get; set; } - } } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/IssuerValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/IssuerValidationResultTests.cs index 8c9caf7332..b5b63d93ee 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/IssuerValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/IssuerValidationResultTests.cs @@ -11,13 +11,14 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Xunit; using Microsoft.IdentityModel.Tokens.Experimental; +using System.Diagnostics; namespace Microsoft.IdentityModel.Tokens.Validation.Tests { public class IssuerValidationTests { [Theory, MemberData(nameof(InvalidIssuerTestCases), DisableDiscoveryEnumeration = true)] - public async Task InvalidIssuers(IssuerValidationTheoryData theoryData) + public async Task InvalidIssuers(ValidateIssuerTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.InvalidIssuers", theoryData); @@ -41,9 +42,10 @@ await Validators.ValidateIssuerAsync( else { ValidationError validationError = validationResult.Error; + TestUtilities.RecordIfMoveNextFound(context, validationError); IdentityComparer.AreStringsEqual( validationError.FailureType.Name, - theoryData.OperationResult.Error.FailureType.Name, + theoryData.ValidationResult.Error.FailureType.Name, context); theoryData.ExpectedException.ProcessException(validationError.GetException(), context); @@ -51,27 +53,27 @@ await Validators.ValidateIssuerAsync( } catch (Exception ex) { - context.AddDiff($"Did not expect an exception: {ex}."); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } TestUtilities.AssertFailIfErrors(context); } - public static TheoryData InvalidIssuerTestCases + public static TheoryData InvalidIssuerTestCases { get { - TheoryData theoryData = new(); + TheoryData theoryData = new(); string validIssuer = Guid.NewGuid().ToString(); string issClaim = Guid.NewGuid().ToString(); //var validConfig = new OpenIdConnectConfiguration() { Issuer = issClaim }; string[] validIssuers = new string[] { validIssuer }; - theoryData.Add(new IssuerValidationTheoryData("NULL_Issuer") + theoryData.Add(new ValidateIssuerTheoryData("NULL_Issuer") { ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10211:"), - OperationResult = new IssuerValidationError( + ValidationResult = new IssuerValidationError( new MessageDetail( LogMessages.IDX10211, LogHelper.MarkAsNonPII(null), @@ -85,11 +87,11 @@ public static TheoryData InvalidIssuerTestCases ValidationParameters = new ValidationParameters() }); - theoryData.Add(new IssuerValidationTheoryData("NULL_ValidationParameters") + theoryData.Add(new ValidateIssuerTheoryData("NULL_ValidationParameters") { ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), Issuer = issClaim, - OperationResult = new IssuerValidationError( + ValidationResult = new IssuerValidationError( new MessageDetail( LogMessages.IDX10000, LogHelper.MarkAsNonPII("validationParameters")), @@ -100,17 +102,17 @@ public static TheoryData InvalidIssuerTestCases ValidationParameters = null }); - theoryData.Add(new IssuerValidationTheoryData("Invalid_Issuer") + theoryData.Add(new ValidateIssuerTheoryData("Invalid_Issuer") { ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10212:"), Issuer = issClaim, - OperationResult = new IssuerValidationError( + ValidationResult = new IssuerValidationError( new MessageDetail( LogMessages.IDX10212, LogHelper.MarkAsNonPII(issClaim), LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validIssuers)), LogHelper.MarkAsNonPII(null)), - IssuerValidationFailure.ValidationFailed, + IssuerValidationFailure.IssuerDidNotMatch, null, issClaim), SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim), @@ -123,7 +125,7 @@ public static TheoryData InvalidIssuerTestCases } [Theory, MemberData(nameof(ValidIssuerTestCases), DisableDiscoveryEnumeration = true)] - public async Task ValidIssuers(IssuerValidationTheoryData theoryData) + public async Task ValidIssuers(ValidateIssuerTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.ValidIssuers", theoryData); @@ -143,7 +145,7 @@ await Validators.ValidateIssuerAsync( if (validationResult.Succeeded) { IdentityComparer.AreValidatedIssuersEqual( - theoryData.OperationResult.Result, + theoryData.ValidationResult.Result, validationResult.Result, context); } @@ -154,27 +156,27 @@ await Validators.ValidateIssuerAsync( } catch (Exception ex) { - context.AddDiff($"Did not expect an exception: {ex}."); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } TestUtilities.AssertFailIfErrors(context); } - public static TheoryData ValidIssuerTestCases + public static TheoryData ValidIssuerTestCases { get { - TheoryData theoryData = new(); + TheoryData theoryData = new(); string validIssuer = Guid.NewGuid().ToString(); string issClaim = Guid.NewGuid().ToString(); var validConfig = new OpenIdConnectConfiguration() { Issuer = issClaim }; string[] validIssuers = new string[] { validIssuer }; - theoryData.Add(new IssuerValidationTheoryData("FromConfig") + theoryData.Add(new ValidateIssuerTheoryData("FromConfig") { Issuer = issClaim, - OperationResult = new ValidatedIssuer(issClaim, IssuerValidationSource.IssuerMatchedConfiguration), + ValidationResult = new ValidatedIssuer(issClaim, IssuerValidationSource.IssuerMatchedConfiguration), SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim), ValidationParameters = new ValidationParameters() { @@ -182,10 +184,10 @@ public static TheoryData ValidIssuerTestCases } }); - theoryData.Add(new IssuerValidationTheoryData("FromValidationParametersValidIssuers") + theoryData.Add(new ValidateIssuerTheoryData("FromValidationParametersValidIssuers") { Issuer = issClaim, - OperationResult = new ValidatedIssuer(issClaim, IssuerValidationSource.IssuerMatchedValidationParameters), + ValidationResult = new ValidatedIssuer(issClaim, IssuerValidationSource.IssuerMatchedValidationParameters), SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim), ValidationParameters = new ValidationParameters(), ValidIssuerToAdd = issClaim @@ -195,22 +197,4 @@ public static TheoryData ValidIssuerTestCases } } } - - public class IssuerValidationTheoryData : TheoryDataBase - { - public IssuerValidationTheoryData(string testId) : base(testId) { } - - public BaseConfiguration Configuration { get; set; } - - public string Issuer { get; set; } - - internal ValidationResult OperationResult { get; set; } - - public SecurityToken SecurityToken { get; set; } - - internal ValidationParameters ValidationParameters { get; set; } - - internal ValidationFailureType ValidationFailure { get; set; } - public string ValidIssuerToAdd { get; internal set; } - } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/LifetimeValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/LifetimeValidationResultTests.cs index b8d5bcfc6e..d4b866ed50 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/LifetimeValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/LifetimeValidationResultTests.cs @@ -35,7 +35,7 @@ public void InvalidLifetimes(ValidateLifetimeTheoryData theoryData) ValidationError validationError = validationResult.Error; IdentityComparer.AreStringsEqual( validationError.FailureType.Name, - theoryData.OperationResult.Error!.FailureType.Name, + theoryData.ValidationResult.Error!.FailureType.Name, context); theoryData.ExpectedException.ProcessException(validationError.GetException(), context); @@ -43,7 +43,7 @@ public void InvalidLifetimes(ValidateLifetimeTheoryData theoryData) } catch (Exception ex) { - context.AddDiff($"Did not expect an exception: {ex}."); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } TestUtilities.AssertFailIfErrors(context); @@ -70,7 +70,7 @@ public static TheoryData InvalidTestCases Expires = oneHourFromNow, NotBefore = oneHourAgo, ValidationParameters = null, - OperationResult = new LifetimeValidationError( + ValidationResult = new LifetimeValidationError( new MessageDetail(LogMessages.IDX10000, "validationParameters"), ValidationFailureType.NullArgument, null, @@ -82,7 +82,7 @@ public static TheoryData InvalidTestCases ExpectedException = ExpectedException.SecurityTokenInvalidLifetimeException("IDX10225:"), NotBefore = oneHourAgo, ValidationParameters = new ValidationParameters() { TimeProvider = timeProvider }, - OperationResult = new LifetimeValidationError( + ValidationResult = new LifetimeValidationError( new MessageDetail(LogMessages.IDX10225, "null"), LifetimeValidationFailure.NoExpirationTime, null, @@ -95,7 +95,7 @@ public static TheoryData InvalidTestCases Expires = oneHourAgo, NotBefore = oneHourFromNow, ValidationParameters = new ValidationParameters() { TimeProvider = timeProvider }, - OperationResult = new LifetimeValidationError( + ValidationResult = new LifetimeValidationError( new MessageDetail( LogMessages.IDX10224, LogHelper.MarkAsNonPII(oneHourFromNow), @@ -111,7 +111,7 @@ public static TheoryData InvalidTestCases Expires = twoHoursFromNow, NotBefore = oneHourFromNow, ValidationParameters = new ValidationParameters() { TimeProvider = timeProvider }, - OperationResult = new LifetimeValidationError( + ValidationResult = new LifetimeValidationError( new MessageDetail( LogMessages.IDX10222, LogHelper.MarkAsNonPII(oneHourFromNow), @@ -127,7 +127,7 @@ public static TheoryData InvalidTestCases Expires = oneHourAgo, NotBefore = twoHoursAgo, ValidationParameters = new ValidationParameters() { TimeProvider = timeProvider }, - OperationResult = new LifetimeValidationError( + ValidationResult = new LifetimeValidationError( new MessageDetail( LogMessages.IDX10223, LogHelper.MarkAsNonPII(oneHourAgo), @@ -146,7 +146,7 @@ public static TheoryData InvalidTestCases ClockSkew = TimeSpan.FromMinutes(5), TimeProvider = timeProvider }, - OperationResult = new LifetimeValidationError( + ValidationResult = new LifetimeValidationError( new MessageDetail( LogMessages.IDX10222, LogHelper.MarkAsNonPII(sixMinutesFromNow), @@ -165,7 +165,7 @@ public static TheoryData InvalidTestCases ClockSkew = TimeSpan.FromMinutes(5), TimeProvider = timeProvider }, - OperationResult = new LifetimeValidationError( + ValidationResult = new LifetimeValidationError( new MessageDetail( LogMessages.IDX10223, LogHelper.MarkAsNonPII(sixMinutesAgo), @@ -196,7 +196,7 @@ public void ValidLifetimes(ValidateLifetimeTheoryData theoryData) if (validationResult.Succeeded) { IdentityComparer.AreValidatedLifetimesEqual( - theoryData.OperationResult.Result, + theoryData.ValidationResult.Result, validationResult.Result, context); } @@ -207,7 +207,7 @@ public void ValidLifetimes(ValidateLifetimeTheoryData theoryData) } catch (Exception ex) { - context.AddDiff($"Did not expect an exception: {ex}."); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } TestUtilities.AssertFailIfErrors(context); @@ -231,14 +231,14 @@ public static TheoryData ValidTestCases { Expires = oneHourFromNow, NotBefore = oneHourAgo, - OperationResult = new ValidatedLifetime(oneHourAgo, oneHourFromNow), + ValidationResult = new ValidatedLifetime(oneHourAgo, oneHourFromNow), ValidationParameters = new ValidationParameters(){TimeProvider = timeProvider } }, new ValidateLifetimeTheoryData("NotBeforeIsNull") { Expires = oneHourFromNow, NotBefore = null, - OperationResult = new ValidatedLifetime(null, oneHourFromNow), + ValidationResult = new ValidatedLifetime(null, oneHourFromNow), ValidationParameters = new ValidationParameters(){ TimeProvider = timeProvider } }, new ValidateLifetimeTheoryData("SkewForward") @@ -249,7 +249,7 @@ public static TheoryData ValidTestCases ClockSkew = TimeSpan.FromMinutes(5), TimeProvider = timeProvider }, - OperationResult = new ValidatedLifetime(twoMinutesFromNow, oneHourFromNow), + ValidationResult = new ValidatedLifetime(twoMinutesFromNow, oneHourFromNow), }, new ValidateLifetimeTheoryData("SkewBackward") { @@ -259,21 +259,10 @@ public static TheoryData ValidTestCases ClockSkew = TimeSpan.FromMinutes(5), TimeProvider = timeProvider }, - OperationResult = new ValidatedLifetime(twoMinutesAgo, oneMinuteAgo), + ValidationResult = new ValidatedLifetime(twoMinutesAgo, oneMinuteAgo), }, }; } } } - - public class ValidateLifetimeTheoryData : TheoryDataBase - { - public ValidateLifetimeTheoryData(string testId) : base(testId) { } - public DateTime? NotBefore { get; set; } - public DateTime? Expires { get; set; } - public SecurityToken SecurityToken { get; set; } - internal ValidationParameters ValidationParameters { get; set; } - internal ValidationResult OperationResult { get; set; } - internal ValidationFailureType ValidationFailure { get; set; } - } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ReplayValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ReplayValidationResultTests.cs index 052bd56dc2..8194f36c73 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ReplayValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ReplayValidationResultTests.cs @@ -12,14 +12,14 @@ namespace Microsoft.IdentityModel.Tokens.Validation.Tests public class ReplayValidationTests { [Theory, MemberData(nameof(InvalidTestCases), DisableDiscoveryEnumeration = true)] - public void InvalidReplays(TokenReplayTheoryData theoryData) + public void InvalidReplays(ValidateTokenReplayTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.InvalidReplays", theoryData); try { ValidationResult validationResult = Validators.ValidateTokenReplay( theoryData.ExpirationTime, - theoryData.SecurityToken, + theoryData.Token, theoryData.ValidationParameters, theoryData.CallContext); @@ -32,7 +32,7 @@ public void InvalidReplays(TokenReplayTheoryData theoryData) ValidationError validationError = validationResult.Error; IdentityComparer.AreStringsEqual( validationError.FailureType.Name, - theoryData.OperationResult.Error.FailureType.Name, + theoryData.ValidationResult.Error.FailureType.Name, context); theoryData.ExpectedException.ProcessException(validationError.GetException(), context); @@ -40,13 +40,13 @@ public void InvalidReplays(TokenReplayTheoryData theoryData) } catch (Exception ex) { - context.AddDiff($"Did not expect an exception: {ex}."); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } TestUtilities.AssertFailIfErrors(context); } - public static TheoryData InvalidTestCases + public static TheoryData InvalidTestCases { get { @@ -54,15 +54,15 @@ public static TheoryData InvalidTestCases DateTime oneHourAgo = now.AddHours(-1); DateTime oneHourFromNow = now.AddHours(1); - return new TheoryData + return new TheoryData { - new TokenReplayTheoryData("SecurityToken_Null") + new ValidateTokenReplayTheoryData("SecurityToken_Null") { ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), ExpirationTime = now, - SecurityToken = null, + Token = null, ValidationParameters = new ValidationParameters(), - OperationResult = new TokenReplayValidationError( + ValidationResult = new TokenReplayValidationError( new MessageDetail( LogMessages.IDX10000, LogHelper.MarkAsNonPII("securityToken")), @@ -70,13 +70,13 @@ public static TheoryData InvalidTestCases null, null), }, - new TokenReplayTheoryData("SecurityToken_Empty") + new ValidateTokenReplayTheoryData("SecurityToken_Empty") { ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), ExpirationTime = now, - SecurityToken = string.Empty, + Token = string.Empty, ValidationParameters = new ValidationParameters(), - OperationResult = new TokenReplayValidationError( + ValidationResult = new TokenReplayValidationError( new MessageDetail( LogMessages.IDX10000, LogHelper.MarkAsNonPII("securityToken")), @@ -84,13 +84,13 @@ public static TheoryData InvalidTestCases null, null), }, - new TokenReplayTheoryData("ValidationParameters_Null") + new ValidateTokenReplayTheoryData("ValidationParameters_Null") { ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), ExpirationTime = now, - SecurityToken = "token", + Token = "token", ValidationParameters = null, - OperationResult = new TokenReplayValidationError( + ValidationResult = new TokenReplayValidationError( new MessageDetail( LogMessages.IDX10000, LogHelper.MarkAsNonPII("validationParameters")), @@ -98,11 +98,11 @@ public static TheoryData InvalidTestCases null, null), }, - new TokenReplayTheoryData("Invalid_ReplayCacheIsPresent_ExpirationTimeIsNull") + new ValidateTokenReplayTheoryData("Invalid_ReplayCacheIsPresent_ExpirationTimeIsNull") { ExpectedException = ExpectedException.SecurityTokenReplayDetected("IDX10227:"), ExpirationTime = null, - SecurityToken = "token", + Token = "token", ValidationParameters = new ValidationParameters { TokenReplayCache = new TokenReplayCache @@ -111,7 +111,7 @@ public static TheoryData InvalidTestCases OnFindReturnValue = false } }, - OperationResult = new TokenReplayValidationError( + ValidationResult = new TokenReplayValidationError( new MessageDetail( LogMessages.IDX10227, LogHelper.MarkAsUnsafeSecurityArtifact("token", t => t.ToString())), @@ -119,11 +119,11 @@ public static TheoryData InvalidTestCases null, null), }, - new TokenReplayTheoryData("Invalid_ReplayCacheIsPresent_TokenIsAlreadyInCache") + new ValidateTokenReplayTheoryData("Invalid_ReplayCacheIsPresent_TokenIsAlreadyInCache") { ExpectedException = ExpectedException.SecurityTokenReplayDetected("IDX10228:"), ExpirationTime = oneHourFromNow, - SecurityToken= "token", + Token= "token", ValidationParameters = new ValidationParameters { TokenReplayCache = new TokenReplayCache @@ -132,7 +132,7 @@ public static TheoryData InvalidTestCases OnFindReturnValue = true }, }, - OperationResult = new TokenReplayValidationError( + ValidationResult = new TokenReplayValidationError( new MessageDetail( LogMessages.IDX10228, LogHelper.MarkAsUnsafeSecurityArtifact("token", t => t.ToString())), @@ -140,10 +140,10 @@ public static TheoryData InvalidTestCases null, null), }, - new TokenReplayTheoryData("Invalid_ReplayCacheIsPresent_AddingTokenToCacheFails") + new ValidateTokenReplayTheoryData("Invalid_ReplayCacheIsPresent_AddingTokenToCacheFails") { ExpirationTime = oneHourFromNow, - SecurityToken= "token", + Token= "token", ValidationParameters = new ValidationParameters { TokenReplayCache = new TokenReplayCache @@ -153,7 +153,7 @@ public static TheoryData InvalidTestCases } }, ExpectedException = ExpectedException.SecurityTokenReplayDetected("IDX10229:"), - OperationResult = new TokenReplayValidationError( + ValidationResult = new TokenReplayValidationError( new MessageDetail( LogMessages.IDX10229, LogHelper.MarkAsUnsafeSecurityArtifact("token", t => t.ToString())), @@ -166,7 +166,7 @@ public static TheoryData InvalidTestCases } [Theory, MemberData(nameof(ValidTestCases), DisableDiscoveryEnumeration = true)] - public void ValidReplays(TokenReplayTheoryData theoryData) + public void ValidReplays(ValidateTokenReplayTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.ValidReplays", theoryData); @@ -174,7 +174,7 @@ public void ValidReplays(TokenReplayTheoryData theoryData) { ValidationResult validationResult = Validators.ValidateTokenReplay( theoryData.ExpirationTime, - theoryData.SecurityToken, + theoryData.Token, theoryData.ValidationParameters, theoryData.CallContext); @@ -182,7 +182,7 @@ public void ValidReplays(TokenReplayTheoryData theoryData) { IdentityComparer.AreDateTimesEqualWithEpsilon( validationResult.Result, - theoryData.OperationResult.Result, + theoryData.ValidationResult.Result, 1, context); } @@ -191,7 +191,7 @@ public void ValidReplays(TokenReplayTheoryData theoryData) ValidationError validationError = validationResult.Error; IdentityComparer.AreStringsEqual( validationError.FailureType.Name, - theoryData.OperationResult.Error.FailureType.Name, + theoryData.ValidationResult.Error.FailureType.Name, context); theoryData.ExpectedException.ProcessException(validationError.GetException(), context); @@ -199,13 +199,13 @@ public void ValidReplays(TokenReplayTheoryData theoryData) } catch (Exception ex) { - context.AddDiff($"Did not expect an exception: {ex}."); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } TestUtilities.AssertFailIfErrors(context); } - public static TheoryData ValidTestCases + public static TheoryData ValidTestCases { get { @@ -213,39 +213,30 @@ public static TheoryData ValidTestCases DateTime oneHourAgo = now.AddHours(-1); DateTime oneHourFromNow = now.AddHours(1); - return new TheoryData + return new TheoryData { - new TokenReplayTheoryData("Valid_ReplayCache_Null") + new ValidateTokenReplayTheoryData("Valid_ReplayCache_Null") { ExpirationTime = oneHourAgo, - SecurityToken = "token", + Token = "token", ValidationParameters = new ValidationParameters { TokenReplayCache = null }, - OperationResult = oneHourAgo, + ValidationResult = oneHourAgo, }, - new TokenReplayTheoryData("Valid_ReplayCache_NotNull") + new ValidateTokenReplayTheoryData("Valid_ReplayCache_NotNull") { ExpirationTime = oneHourFromNow, - SecurityToken = "token", + Token = "token", ValidationParameters = new ValidationParameters { TokenReplayCache = new TokenReplayCache { OnAddReturnValue = true, OnFindReturnValue = false }, }, - OperationResult = oneHourFromNow, + ValidationResult = oneHourFromNow, }, }; } } } - - public class TokenReplayTheoryData : TheoryDataBase - { - public TokenReplayTheoryData(string testId) : base(testId) { } - public DateTime? ExpirationTime { get; set; } - public string SecurityToken { get; set; } - internal ValidationParameters ValidationParameters { get; set; } - internal ValidationResult OperationResult { get; set; } - } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs index 06fac09bd8..e9571f5741 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs @@ -13,7 +13,7 @@ namespace Microsoft.IdentityModel.Tokens.Validation.Tests public class SigningKeyTests { [Theory, MemberData(nameof(InvalidTestCases), DisableDiscoveryEnumeration = true)] - public void InvalidSigningKeys(SigningKeyValidationTheoryData theoryData) + public void InvalidSigningKeys(ValidateSigningKeyTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.InvalidSigningKeys", theoryData); @@ -43,13 +43,13 @@ public void InvalidSigningKeys(SigningKeyValidationTheoryData theoryData) } catch (Exception ex) { - context.AddDiff($"Did not expect an exception: {ex}."); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } TestUtilities.AssertFailIfErrors(context); } - public static TheoryData InvalidTestCases + public static TheoryData InvalidTestCases { get { @@ -58,10 +58,10 @@ public static TheoryData InvalidTestCases DateTime utcExpired = KeyingMaterial.ExpiredX509SecurityKey_Public.Certificate.NotAfter.ToUniversalTime(); DateTime utcNotYetValid = KeyingMaterial.NotYetValidX509SecurityKey_Public.Certificate.NotBefore.ToUniversalTime(); - return new TheoryData + return new TheoryData { // TODO Error message IDX10253 message is not accurate. - new SigningKeyValidationTheoryData("SecurityKeyIsNull") + new ValidateSigningKeyTheoryData("SecurityKeyIsNull") { ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(substringExpected: "IDX10253:"), SecurityKey = null, @@ -73,7 +73,7 @@ public static TheoryData InvalidTestCases null, null) }, - new SigningKeyValidationTheoryData("SecurityTokenIsNull") + new ValidateSigningKeyTheoryData("SecurityTokenIsNull") { ExpectedException = ExpectedException.ArgumentNullException(substringExpected: "IDX10000:"), SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, @@ -87,7 +87,7 @@ public static TheoryData InvalidTestCases null, null) }, - new SigningKeyValidationTheoryData("ValidationParametersIsNull") + new ValidateSigningKeyTheoryData("ValidationParametersIsNull") { ExpectedException = ExpectedException.ArgumentNullException(substringExpected: "IDX10000:"), SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, @@ -101,7 +101,7 @@ public static TheoryData InvalidTestCases null, null), // InvalidSigningKey }, - new SigningKeyValidationTheoryData("SecurityKeyIsExpired") + new ValidateSigningKeyTheoryData("SecurityKeyIsExpired") { ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(substringExpected: "IDX10249:"), SecurityKey = KeyingMaterial.ExpiredX509SecurityKey_Public, @@ -116,7 +116,7 @@ public static TheoryData InvalidTestCases null, null), // InvalidSigningKey }, - new SigningKeyValidationTheoryData("SecurityKeyIsNotYetValid") + new ValidateSigningKeyTheoryData("SecurityKeyIsNotYetValid") { ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(substringExpected: "IDX10248:"), SecurityKey = KeyingMaterial.NotYetValidX509SecurityKey_Public, @@ -136,7 +136,7 @@ public static TheoryData InvalidTestCases } [Theory, MemberData(nameof(ValidTestCases), DisableDiscoveryEnumeration = true)] - public void ValidSigningKeys(SigningKeyValidationTheoryData theoryData) + public void ValidSigningKeys(ValidateSigningKeyTheoryData theoryData) { CompareContext context = TestUtilities.WriteHeader($"{this}.ValidSigningKeys", theoryData); @@ -163,22 +163,22 @@ public void ValidSigningKeys(SigningKeyValidationTheoryData theoryData) } catch (Exception ex) { - context.AddDiff($"Did not expect an exception: {ex}."); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } TestUtilities.AssertFailIfErrors(context); } - public static TheoryData ValidTestCases + public static TheoryData ValidTestCases { get { MockTimeProvider timeProvider = new MockTimeProvider(); DateTime utcNow = timeProvider.GetUtcNow().UtcDateTime; - return new TheoryData + return new TheoryData { - new SigningKeyValidationTheoryData("SecurityTokenIsPresent") + new ValidateSigningKeyTheoryData("SecurityTokenIsPresent") { SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, SecurityToken = new JwtSecurityToken(), @@ -189,14 +189,4 @@ public static TheoryData ValidTestCases } } } - - public class SigningKeyValidationTheoryData : TheoryDataBase - { - public SigningKeyValidationTheoryData(string testId) : base(testId) { } - public SecurityKey SecurityKey { get; set; } - public SecurityToken SecurityToken { get; set; } - internal ValidationParameters ValidationParameters { get; set; } - public BaseConfiguration BaseConfiguration { get; set; } - internal ValidationResult ValidationResult { get; set; } - } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/StackFrameTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/StackFrameTests.cs index 130cbd674a..4962f4ffae 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/StackFrameTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/StackFrameTests.cs @@ -4,95 +4,123 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.TestUtils; using Microsoft.IdentityModel.Tokens.Experimental; using Xunit; -namespace Microsoft.IdentityModel.Tokens.Validation.Tests +namespace Microsoft.IdentityModel.Tokens.TokenValidation.Tests { + [CollectionDefinition("StackFrameTests", DisableParallelization = true)] public class StackFrameTests { + /// + /// The purpose of this test is to ensure that the stack frames are cached and reused + /// + /// + /// [Theory, MemberData(nameof(StackFrameTestCases), DisableDiscoveryEnumeration = true)] - public async Task CallStack(StackFrameTheoryData theoryData) + public async Task StackFrameCount(ValidateTokenTheoryData theoryData) { - CompareContext context = TestUtilities.WriteHeader($"{this}.CallStack", theoryData); - JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); - - ValidationResult validationResult = - await jsonWebTokenHandler.ValidateTokenAsync( - theoryData.SecurityToken, - theoryData.ValidationParameters, - theoryData.CallContext, - CancellationToken.None); - - validationResult = - await jsonWebTokenHandler.ValidateTokenAsync( - theoryData.SecurityToken, - theoryData.ValidationParameters, - theoryData.CallContext, - CancellationToken.None); + CompareContext context = TestUtilities.WriteHeader($"{this}.StackFrameCount", theoryData); + + try + { + ValidationError.CachedStackFrames.Clear(); + + ValidationResult validationResult = + await theoryData.TestingTokenHandler.ValidateTokenAsync( + theoryData.Token, + theoryData.ValidationParameters, + theoryData.CallContext, + CancellationToken.None); + + int numberOfCachedStackFrames = ValidationError.CachedStackFrames.Count; + + validationResult = + await theoryData.TestingTokenHandler.ValidateTokenAsync( + theoryData.Token, + theoryData.ValidationParameters, + theoryData.CallContext, + CancellationToken.None); + + int numberOfKeys = theoryData.ValidationParameters.SigningKeys.Count; + + // If TryAllSigningKeys is true, we expect a StackFrame to be added to ValidationError.StackFrames for each key used in validation + // but not added to the ValidationError.CachedStackFrames. + // However, when adding new keys, watch out for the case where different keys result in different stack frames. + // This test succeeds because each key failure results in the fault in the same location so the StackFrame will not be added to ValidationError.CachedStackFrames. + // If this test starts to fail a good place to start looking is for failures resulting in a new StackFrame created. + // Setting ValidateTokenTheoryData.IncludeInStackFrameCountTest to false may be helpful, but be careful as it may be an indication of a bug in the code under test. + // We could have added a property in ValidationTokenTheoryData.ExpectedStackFrameCount or ValidationTokenTheoryData.StackFrameCountAdjustment + // but experience shows these are fragile. + int expectedValidationErrorStackFrameCount = ValidationError.CachedStackFrames.Count + + (theoryData.ValidationParameters.TryAllSigningKeys ? (numberOfKeys > 1 ? numberOfKeys - 1 : 0) : 0); + + // Check that ValidationError.StackFrames has the expected number of stack frames. + if (validationResult.Error.StackFrames.Count != expectedValidationErrorStackFrameCount) + context.Diffs.Add( + $"validationResult.Error.StackFrames.Count: '{validationResult.Error.StackFrames.Count}'" + + $"\n!=" + + $"\nexpectedStackFrameCount: '{expectedValidationErrorStackFrameCount}" + + $"\nTokenHandler: '{theoryData.TestingTokenHandler}'" + + $"\nTestId: '{theoryData.TestId}.'"); + + // Check that the number of CachedStackFrames has not changed from the first validation fault. + if (numberOfCachedStackFrames != ValidationError.CachedStackFrames.Count) + context.Diffs.Add( + $"numberOfCachedStackFrames: '{numberOfCachedStackFrames}'" + + $"\n!=" + + $"\nValidationError.CachedStackFrames.Count'{ValidationError.CachedStackFrames.Count}'" + + $"\nTokenHandler: '{theoryData.TestingTokenHandler}'" + + $"\nTestId: '{theoryData.TestId}."); + } + catch (Exception ex) + { + TestUtilities.RecordUnexpectedException(context, theoryData, ex); + } TestUtilities.AssertFailIfErrors(context); } - public static TheoryData StackFrameTestCases + public static TheoryData StackFrameTestCases { get { - TheoryData theoryData = new TheoryData(); + TheoryData theoryData = new(); - theoryData.Add(new StackFrameTheoryData("Expired") - { - SecurityToken = CreateToken(DateTime.UtcNow, DateTime.UtcNow.AddMinutes(-1), DateTime.UtcNow.AddMinutes(-20)), - ValidationParameters = CreateValidationParameters() - }); + AddInvalidTokenTestCases(theoryData, new JsonWebTestingTokenHandler()); + AddInvalidTokenTestCases(theoryData, new SamlSecurityTestingTokenHandler()); + AddInvalidTokenTestCases(theoryData, new Saml2SecurityTestingTokenHandler()); return theoryData; } } - static ValidationParameters CreateValidationParameters(TimeSpan? clockSkew = null) + private static void AddInvalidTokenTestCases(TheoryData theoryData, ITestingTokenHandler tokenHandler) { - ValidationParameters validationParameters = new ValidationParameters(); - - if (clockSkew is not null) - validationParameters.ClockSkew = clockSkew.Value; - - // Skip all validations except lifetime - validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation; - validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; - validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation; - validationParameters.SignatureKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation; - validationParameters.SignatureValidator = SkipValidationDelegates.SkipSignatureValidation; - - return validationParameters; + tokenHandler.SetDefaultTimesOnTokenCreation = false; + + AddStackFrameTests(TestCaseProvider.GenerateInvalidAlgorithmTestCases(tokenHandler), theoryData); + AddStackFrameTests(TestCaseProvider.GenerateInvalidAudienceTestCases(tokenHandler), theoryData); + AddStackFrameTests(TestCaseProvider.GenerateInvalidIssuerSigningKeyTestCases(tokenHandler), theoryData); + AddStackFrameTests(TestCaseProvider.GenerateInvalidIssuerTestCases(tokenHandler), theoryData); + AddStackFrameTests(TestCaseProvider.GenerateInvalidLifetimeTestCases(tokenHandler), theoryData); + AddStackFrameTests(TestCaseProvider.GenerateInvalidReadTokenTestCases(tokenHandler), theoryData); + AddStackFrameTests(TestCaseProvider.GenerateInvalidSignatureTestCases(tokenHandler), theoryData); + AddStackFrameTests(TestCaseProvider.GenerateInvalidTokenReplayTestCases(tokenHandler), theoryData); } - private static JsonWebToken CreateToken(DateTime? issuedAt, DateTime? notBefore, DateTime? expires) + private static void AddStackFrameTests( + TheoryData theoryDataSource, + TheoryData theoryDataTarget) { - JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); - jsonWebTokenHandler.SetDefaultTimesOnTokenCreation = false; // Allow for null values to be passed in to validate. - - SecurityTokenDescriptor securityTokenDescriptor = new SecurityTokenDescriptor + foreach (var test in theoryDataSource) { - Subject = Default.ClaimsIdentity, - SigningCredentials = KeyingMaterial.RsaSigningCreds_2048, - IssuedAt = issuedAt, - NotBefore = notBefore, - Expires = expires, - }; - - return new JsonWebToken(jsonWebTokenHandler.CreateToken(securityTokenDescriptor)); - } - } + if (!test.IncludeInStackFrameCountTest) + continue; - public class StackFrameTheoryData : TheoryDataBase - { - public StackFrameTheoryData(string testId) : base(testId) { } - public SecurityToken SecurityToken { get; set; } - internal ValidationParameters ValidationParameters { get; set; } - internal ValidationResult OperationResult { get; set; } - internal ValidationFailureType ValidationFailure { get; set; } + theoryDataTarget.Add(test); + } + } } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/TokenTypeValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/TokenTypeValidationResultTests.cs index be02b0e295..d3bd3acb0b 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/TokenTypeValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/TokenTypeValidationResultTests.cs @@ -52,7 +52,7 @@ public void InvalidTokenTypes(TokenTypeTheoryData theoryData) } catch (Exception ex) { - context.AddDiff($"Did not expect an exception: {ex}"); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } TestUtilities.AssertFailIfErrors(context); @@ -179,7 +179,7 @@ public void ValidTokenTypes(TokenTypeTheoryData theoryData) } catch (Exception ex) { - context.AddDiff($"Did not expect an exception: {ex}"); + TestUtilities.RecordUnexpectedException(context, theoryData, ex); } TestUtilities.AssertFailIfErrors(context); diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs index 2e90c50ee4..b37f109d36 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs @@ -51,7 +51,7 @@ public static TheoryData ValidateAudienceParameter { Audiences = new List { " " }, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"), - TokenValidationParameters = new TokenValidationParameters{ ValidAudience = "audience"} + TokenValidationParameters = new TokenValidationParameters{ ValidAudience = "audience"}, }, new AudienceValidationTheoryData("AudiencesNull") { @@ -457,7 +457,7 @@ public void TokenReplay(string securityToken, DateTime? expirationTime, TokenVal // Each TokenReplayValidator in this test checks that the expiration parameter passed into it is equal to the expiration time of the token. // If they're not equal, the test will fail. [Theory, MemberData(nameof(CheckParametersForTokenReplayTheoryData), DisableDiscoveryEnumeration = true)] - public void CheckParametersForTokenReplay(TokenReplayTheoryData theoryData) + public void CheckParametersForTokenReplay(ValidateTokenReplayTheoryData theoryData) { TestUtilities.WriteHeader($"{this}.CheckParametersForTokenReplay", theoryData); var context = new CompareContext($"{this}.CheckParametersForTokenReplay, {theoryData.TestId}"); @@ -468,7 +468,7 @@ public void CheckParametersForTokenReplay(TokenReplayTheoryData theoryData) tvp.ValidateAudience = false; tvp.ValidateIssuer = false; tvp.ValidateLifetime = false; - var token = theoryData.SecurityToken; + var token = theoryData.Token; var tokenValidator = theoryData.SecurityTokenHandler; try @@ -485,7 +485,7 @@ public void CheckParametersForTokenReplay(TokenReplayTheoryData theoryData) TestUtilities.AssertFailIfErrors(context); } - public static TheoryData CheckParametersForTokenReplayTheoryData + public static TheoryData CheckParametersForTokenReplayTheoryData { get { diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs index 50e877ada7..39d8cb49e0 100644 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs +++ b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs @@ -2070,7 +2070,7 @@ public static TheoryData ValidateIssuerTheoryData } [Theory, MemberData(nameof(TokenReplayValidationTheoryData), DisableDiscoveryEnumeration = true)] - public void TokenReplayValidation(TokenReplayTheoryData theoryData) + public void TokenReplayValidation(ValidateTokenReplayTheoryData theoryData) { TestUtilities.WriteHeader($"{this}.TokenReplayValidation", theoryData); var context = new CompareContext($"{this}.ReadKeyInfo, {theoryData.TestId}"); @@ -2104,7 +2104,7 @@ public void TokenReplayValidation(TokenReplayTheoryData theoryData) TestUtilities.AssertFailIfErrors(context); } - public static TheoryData TokenReplayValidationTheoryData + public static TheoryData TokenReplayValidationTheoryData { get {