From a664f723a7e6797dd946178c02dd88f5098313cd Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Tue, 10 Dec 2024 15:40:56 +0000 Subject: [PATCH 1/8] Removed static stack frames and replaced with the simplified approach of GetCurrentStackFrame() and AddCurrentStackFrame() --- .../JsonWebTokenHandler.DecryptToken.cs | 21 ++----- .../JsonWebTokenHandler.ReadToken.cs | 7 +-- ...nWebTokenHandler.ValidateToken.Internal.cs | 57 ++++++------------- ...bTokenHandler.ValidateToken.StackFrames.cs | 56 ------------------ .../JwtTokenUtilities.DecryptTokenResult.cs | 7 +-- .../JwtTokenUtilities.cs | 7 +-- ...rityTokenHandler.ValidateToken.Internal.cs | 18 ++---- ...yTokenHandler.ValidateToken.StackFrames.cs | 30 ---------- ...rityTokenHandler.ValidateToken.Internal.cs | 18 ++---- ...yTokenHandler.ValidateToken.StackFrames.cs | 31 ---------- .../Validation/Validators.Algorithm.cs | 5 +- .../Validation/Validators.Issuer.cs | 10 ++-- 12 files changed, 47 insertions(+), 220 deletions(-) delete mode 100644 src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.StackFrames.cs delete mode 100644 src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.StackFrames.cs delete mode 100644 src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.StackFrames.cs diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.DecryptToken.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.DecryptToken.cs index 7b7a641cb2..4ca9467379 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.DecryptToken.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.DecryptToken.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Text; using Microsoft.IdentityModel.Logging; @@ -31,49 +30,42 @@ internal ValidationResult DecryptToken( { if (jwtToken == null) { - StackFrame tokenNullStackFrame = StackFrames.DecryptionTokenNull ??= new StackFrame(true); return ValidationError.NullParameter( nameof(jwtToken), - tokenNullStackFrame); + ValidationError.GetCurrentStackFrame()); } if (validationParameters == null) { - StackFrame validationParametersNullStackFrame = StackFrames.DecryptionValidationParametersNull ??= new StackFrame(true); return ValidationError.NullParameter( nameof(validationParameters), - validationParametersNullStackFrame); + ValidationError.GetCurrentStackFrame()); } if (string.IsNullOrEmpty(jwtToken.Enc)) { - StackFrame headerMissingStackFrame = StackFrames.DecryptionHeaderMissing ??= new StackFrame(true); return new ValidationError( new MessageDetail(TokenLogMessages.IDX10612), ValidationFailureType.TokenDecryptionFailed, typeof(SecurityTokenException), - headerMissingStackFrame); + ValidationError.GetCurrentStackFrame()); } (IList? contentEncryptionKeys, ValidationError? validationError) result = GetContentEncryptionKeys(jwtToken, validationParameters, configuration, callContext); if (result.validationError != null) - { - StackFrame decryptionGetKeysStackFrame = StackFrames.DecryptionGetEncryptionKeys ??= new StackFrame(true); - return result.validationError.AddStackFrame(decryptionGetKeysStackFrame); - } + return result.validationError.AddCurrentStackFrame(); if (result.contentEncryptionKeys == null || result.contentEncryptionKeys.Count == 0) { - StackFrame noKeysTriedStackFrame = StackFrames.DecryptionNoKeysTried ??= new StackFrame(true); return new ValidationError( new MessageDetail( TokenLogMessages.IDX10609, LogHelper.MarkAsSecurityArtifact(jwtToken, JwtTokenUtilities.SafeLogJwtToken)), ValidationFailureType.TokenDecryptionFailed, typeof(SecurityTokenDecryptionFailedException), - noKeysTriedStackFrame); + ValidationError.GetCurrentStackFrame()); } return JwtTokenUtilities.DecryptJwtToken( @@ -211,7 +203,6 @@ internal ValidationResult DecryptToken( return (unwrappedKeys, null); else { - StackFrame decryptionKeyUnwrapFailedStackFrame = StackFrames.DecryptionKeyUnwrapFailed ??= new StackFrame(true); ValidationError validationError = new( new MessageDetail( TokenLogMessages.IDX10618, @@ -220,7 +211,7 @@ internal ValidationResult DecryptToken( LogHelper.MarkAsSecurityArtifact(jwtToken, JwtTokenUtilities.SafeLogJwtToken)), ValidationFailureType.TokenDecryptionFailed, typeof(SecurityTokenKeyWrapException), - decryptionKeyUnwrapFailedStackFrame); + ValidationError.GetCurrentStackFrame()); return (null, validationError); } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ReadToken.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ReadToken.cs index 44929e628b..8ea160e10a 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ReadToken.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ReadToken.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using Microsoft.IdentityModel.Tokens; #nullable enable @@ -28,10 +27,9 @@ internal static ValidationResult ReadToken( { if (string.IsNullOrEmpty(token)) { - StackFrame nullTokenStackFrame = StackFrames.ReadTokenNullOrEmpty ?? new StackFrame(true); return ValidationError.NullParameter( nameof(token), - nullTokenStackFrame); + ValidationError.GetCurrentStackFrame()); } try @@ -43,12 +41,11 @@ internal static ValidationResult ReadToken( catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { - StackFrame malformedTokenStackFrame = StackFrames.ReadTokenMalformed ?? new StackFrame(true); return new ValidationError( new MessageDetail(LogMessages.IDX14107), ValidationFailureType.TokenReadingFailed, typeof(SecurityTokenMalformedException), - malformedTokenStackFrame, + ValidationError.GetCurrentStackFrame(), ex); } } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs index 1bb3e578ca..f4548d604d 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -42,23 +41,20 @@ internal async Task> ValidateTokenAsync( { if (string.IsNullOrEmpty(token)) { - StackFrame nullTokenStackFrame = StackFrames.TokenStringNull ??= new StackFrame(true); return ValidationError.NullParameter( nameof(token), - nullTokenStackFrame); + ValidationError.GetCurrentStackFrame()); } if (validationParameters is null) { - StackFrame nullValidationParametersStackFrame = StackFrames.TokenStringValidationParametersNull ??= new StackFrame(true); return ValidationError.NullParameter( nameof(validationParameters), - nullValidationParametersStackFrame); + ValidationError.GetCurrentStackFrame()); } if (token.Length > MaximumTokenSizeInBytes) { - StackFrame invalidTokenLengthStackFrame = StackFrames.InvalidTokenLength ??= new StackFrame(true); return new ValidationError( new MessageDetail( TokenLogMessages.IDX10209, @@ -66,7 +62,7 @@ internal async Task> ValidateTokenAsync( LogHelper.MarkAsNonPII(MaximumTokenSizeInBytes)), ValidationFailureType.InvalidSecurityToken, typeof(ArgumentException), - invalidTokenLengthStackFrame); + ValidationError.GetCurrentStackFrame()); } ValidationResult readResult = ReadToken(token, callContext); @@ -82,12 +78,10 @@ internal async Task> ValidateTokenAsync( if (validationResult.IsValid) return validationResult; // No need to unwrap and re-wrap the result. - StackFrame validationFailureStackFrame = StackFrames.TokenStringValidationFailed ??= new StackFrame(true); - return validationResult.UnwrapError().AddStackFrame(validationFailureStackFrame); + return validationResult.UnwrapError().AddCurrentStackFrame(); } - StackFrame readFailureStackFrame = StackFrames.TokenStringReadFailed ??= new StackFrame(true); - return readResult.UnwrapError().AddStackFrame(readFailureStackFrame); + return readResult.UnwrapError().AddCurrentStackFrame(); } /// @@ -99,28 +93,25 @@ internal async Task> ValidateTokenAsync( { if (token is null) { - StackFrame nullTokenStackFrame = StackFrames.TokenNull ??= new StackFrame(true); return ValidationError.NullParameter( nameof(token), - nullTokenStackFrame); + ValidationError.GetCurrentStackFrame()); } if (validationParameters is null) { - StackFrame nullValidationParametersStackFrame = StackFrames.TokenValidationParametersNull ??= new StackFrame(true); return ValidationError.NullParameter( nameof(validationParameters), - nullValidationParametersStackFrame); + ValidationError.GetCurrentStackFrame()); } if (token is not JsonWebToken jsonWebToken) { - StackFrame notJwtStackFrame = StackFrames.TokenNotJWT ??= new StackFrame(true); return new ValidationError( new MessageDetail(TokenLogMessages.IDX10001, nameof(token), nameof(JsonWebToken)), ValidationFailureType.InvalidSecurityToken, typeof(ArgumentException), - notJwtStackFrame); + ValidationError.GetCurrentStackFrame()); } BaseConfiguration? currentConfiguration = @@ -200,8 +191,7 @@ await ValidateJWEAsync(jsonWebToken, validationParameters, lkgConfiguration, cal } // If we reach this point, the token validation failed and we should return the error. - StackFrame stackFrame = StackFrames.TokenValidationFailed ??= new StackFrame(true); - return result.UnwrapError().AddStackFrame(stackFrame); + return result.UnwrapError().AddCurrentStackFrame(); } private async ValueTask> ValidateJWEAsync( @@ -215,15 +205,13 @@ private async ValueTask> ValidateJWEAsync( jwtToken, validationParameters, configuration, callContext); if (!decryptionResult.IsValid) { - StackFrame decryptionFailureStackFrame = StackFrames.DecryptionFailed ??= new StackFrame(true); - return decryptionResult.UnwrapError().AddStackFrame(decryptionFailureStackFrame); + return decryptionResult.UnwrapError().AddCurrentStackFrame(); } ValidationResult readResult = ReadToken(decryptionResult.UnwrapResult(), callContext); if (!readResult.IsValid) { - StackFrame readFailureStackFrame = StackFrames.DecryptedReadFailed ??= new StackFrame(true); - return readResult.UnwrapError().AddStackFrame(readFailureStackFrame); + return readResult.UnwrapError().AddCurrentStackFrame(); } JsonWebToken decryptedToken = (readResult.UnwrapResult() as JsonWebToken)!; @@ -233,8 +221,7 @@ await ValidateJWSAsync(decryptedToken!, validationParameters, configuration, cal if (!validationResult.IsValid) { - StackFrame validationFailureStackFrame = StackFrames.JWEValidationFailed ??= new StackFrame(true); - return validationResult.UnwrapError().AddStackFrame(validationFailureStackFrame); + return validationResult.UnwrapError().AddCurrentStackFrame(); } JsonWebToken jsonWebToken = (validationResult.UnwrapResult().SecurityToken as JsonWebToken)!; @@ -289,10 +276,7 @@ private async ValueTask> ValidateJWSAsync( tokenAudiences, jsonWebToken, validationParameters, callContext); if (!audienceValidationResult.IsValid) - { - StackFrame audienceValidationFailureStackFrame = StackFrames.AudienceValidationFailed ??= new StackFrame(true); - return audienceValidationResult.UnwrapError().AddStackFrame(audienceValidationFailureStackFrame); - } + return audienceValidationResult.UnwrapError().AddCurrentStackFrame(); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) @@ -362,10 +346,7 @@ private async ValueTask> ValidateJWSAsync( { ValidationResult actorReadingResult = ReadToken(jsonWebToken.Actor, callContext); if (!actorReadingResult.IsValid) - { - StackFrame actorReadingFailureStackFrame = StackFrames.ActorReadFailed ??= new StackFrame(true); - return actorReadingResult.UnwrapError().AddStackFrame(actorReadingFailureStackFrame); - } + return actorReadingResult.UnwrapError().AddCurrentStackFrame(); JsonWebToken actorToken = (actorReadingResult.UnwrapResult() as JsonWebToken)!; ValidationParameters actorParameters = validationParameters.ActorValidationParameters; @@ -374,10 +355,7 @@ await ValidateJWSAsync(actorToken, actorParameters, configuration, callContext, .ConfigureAwait(false); if (!innerActorValidationResult.IsValid) - { - StackFrame actorValidationFailureStackFrame = StackFrames.ActorValidationFailed ??= new StackFrame(true); - return innerActorValidationResult.UnwrapError().AddStackFrame(actorValidationFailureStackFrame); - } + return innerActorValidationResult.UnwrapError().AddCurrentStackFrame(); actorValidationResult = innerActorValidationResult; } @@ -410,10 +388,7 @@ await ValidateJWSAsync(actorToken, actorParameters, configuration, callContext, ValidationResult signatureValidationResult = ValidateSignature( jsonWebToken, validationParameters, configuration, callContext); if (!signatureValidationResult.IsValid) - { - StackFrame signatureValidationFailureStackFrame = StackFrames.SignatureValidationFailed ??= new StackFrame(true); - return signatureValidationResult.UnwrapError().AddStackFrame(signatureValidationFailureStackFrame); - } + return signatureValidationResult.UnwrapError().AddCurrentStackFrame(); ValidationResult issuerSigningKeyValidationResult; diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.StackFrames.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.StackFrames.cs deleted file mode 100644 index 69a02ccacb..0000000000 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.StackFrames.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Diagnostics; -using Microsoft.IdentityModel.Tokens; - -#nullable enable -namespace Microsoft.IdentityModel.JsonWebTokens -{ - public partial class JsonWebTokenHandler : TokenHandler - { - // Cached stack frames to build exceptions from validation errors - internal static class StackFrames - { - // ValidateTokenAsync from string - internal static StackFrame? TokenStringNull; - internal static StackFrame? TokenStringValidationParametersNull; - internal static StackFrame? InvalidTokenLength; - internal static StackFrame? TokenStringValidationFailed; - internal static StackFrame? TokenStringReadFailed; - // ValidateTokenAsync from SecurityToken - internal static StackFrame? TokenNull; - internal static StackFrame? TokenValidationParametersNull; - internal static StackFrame? TokenNotJWT; - internal static StackFrame? TokenValidationFailed; - // ValidateJWEAsync - internal static StackFrame? DecryptionFailed; - internal static StackFrame? DecryptedReadFailed; - internal static StackFrame? JWEValidationFailed; - // ValidateJWSAsync - internal static StackFrame? LifetimeValidationFailed; - internal static StackFrame? AudienceValidationFailed; - internal static StackFrame? IssuerValidationFailed; - internal static StackFrame? ReplayValidationFailed; - internal static StackFrame? ActorReadFailed; - internal static StackFrame? ActorValidationFailed; - internal static StackFrame? TypeValidationFailed; - internal static StackFrame? SignatureValidationFailed; - internal static StackFrame? IssuerSigningKeyValidationFailed; - // DecryptToken - internal static StackFrame? DecryptionTokenNull; - internal static StackFrame? DecryptionValidationParametersNull; - internal static StackFrame? DecryptionHeaderMissing; - internal static StackFrame? DecryptionGetEncryptionKeys; - internal static StackFrame? DecryptionNoKeysTried; - internal static StackFrame? DecryptionKeyUnwrapFailed; - // ReadToken - internal static StackFrame? ReadTokenNullOrEmpty; - internal static StackFrame? ReadTokenMalformed; - // ValidateSignature - internal static StackFrame? KidNotMatchedNoTryAll; - internal static StackFrame? NoKeysProvided; - } - } -} -#nullable restore diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.DecryptTokenResult.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.DecryptTokenResult.cs index a4a242cd90..82a416b751 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.DecryptTokenResult.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.DecryptTokenResult.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using System.Text; using Microsoft.IdentityModel.Tokens; using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; @@ -28,12 +27,12 @@ internal static ValidationResult DecryptJwtToken( if (validationParameters == null) return ValidationError.NullParameter( nameof(validationParameters), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame()); if (decryptionParameters == null) return ValidationError.NullParameter( nameof(decryptionParameters), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame()); bool decryptionSucceeded = false; bool algorithmNotSupportedByCryptoProvider = false; @@ -124,7 +123,7 @@ internal static ValidationResult DecryptJwtToken( new MessageDetail(TokenLogMessages.IDX10679, zipAlgorithm), ValidationFailureType.TokenDecryptionFailed, typeof(SecurityTokenDecompressionFailedException), - new StackFrame(true), + ValidationError.GetCurrentStackFrame(), ex); } } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs index 2794ff2d8d..02eb40bce4 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics; using System.Globalization; using System.Security.Claims; using System.Security.Cryptography; @@ -373,7 +372,7 @@ private static ValidationError GetDecryptionError( LogHelper.MarkAsSecurityArtifact(decryptionParameters.EncodedToken, SafeLogJwtToken)), ValidationFailureType.TokenDecryptionFailed, typeof(SecurityTokenDecryptionFailedException), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame()); else if (algorithmNotSupportedByCryptoProvider) return new ValidationError( new MessageDetail( @@ -382,7 +381,7 @@ private static ValidationError GetDecryptionError( LogHelper.MarkAsNonPII(decryptionParameters.Enc)), ValidationFailureType.TokenDecryptionFailed, typeof(SecurityTokenDecryptionFailedException), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame()); else return new ValidationError( new MessageDetail( @@ -390,7 +389,7 @@ private static ValidationError GetDecryptionError( LogHelper.MarkAsSecurityArtifact(decryptionParameters.EncodedToken, SafeLogJwtToken)), ValidationFailureType.TokenDecryptionFailed, typeof(SecurityTokenDecryptionFailedException), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame()); } private static byte[] DecryptToken(CryptoProviderFactory cryptoProviderFactory, SecurityKey key, string encAlg, byte[] ciphertext, byte[] headerAscii, byte[] initializationVector, byte[] authenticationTag) diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs index 2e073084f8..2ddddbfd87 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -44,10 +43,9 @@ internal async Task> ValidateTokenAsync( { if (securityToken is null) { - StackFrames.TokenNull ??= new StackFrame(true); return ValidationError.NullParameter( nameof(securityToken), - StackFrames.TokenNull); + ValidationError.GetCurrentStackFrame()); } if (securityToken is not SamlSecurityToken samlToken) @@ -65,10 +63,9 @@ internal async Task> ValidateTokenAsync( if (validationParameters is null) { - StackFrames.TokenValidationParametersNull ??= new StackFrame(true); return ValidationError.NullParameter( nameof(validationParameters), - StackFrames.TokenValidationParametersNull); + ValidationError.GetCurrentStackFrame()); } ValidationResult conditionsResult = ValidateConditions(samlToken, validationParameters, callContext); @@ -135,10 +132,7 @@ internal async Task> ValidateTokenAsync( ValidationResult signatureValidationResult = ValidateSignature(samlToken, validationParameters, callContext); if (!signatureValidationResult.IsValid) - { - StackFrames.SignatureValidationFailed ??= new StackFrame(true); - return signatureValidationResult.UnwrapError().AddStackFrame(StackFrames.SignatureValidationFailed); - } + return signatureValidationResult.UnwrapError().AddCurrentStackFrame(); ValidationResult issuerSigningKeyValidationResult; @@ -188,18 +182,16 @@ internal virtual ValidationResult ValidateConditions( { if (samlToken.Assertion is null) { - StackFrames.AssertionNull ??= new StackFrame(true); return ValidationError.NullParameter( nameof(samlToken.Assertion), - StackFrames.AssertionNull); + ValidationError.GetCurrentStackFrame()); } if (samlToken.Assertion.Conditions is null) { - StackFrames.AssertionConditionsNull ??= new StackFrame(true); return ValidationError.NullParameter( nameof(samlToken.Assertion.Conditions), - StackFrames.AssertionConditionsNull); + ValidationError.GetCurrentStackFrame()); } ValidationResult lifetimeValidationResult; diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.StackFrames.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.StackFrames.cs deleted file mode 100644 index c7a05acfc0..0000000000 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.StackFrames.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Diagnostics; - -#nullable enable -namespace Microsoft.IdentityModel.Tokens.Saml -{ - public partial class SamlSecurityTokenHandler : SecurityTokenHandler - { - // Cached stack frames to build exceptions from validation errors - internal static class StackFrames - { - // Stack frames from ValidateTokenAsync using SecurityToken - internal static StackFrame? TokenNull; - internal static StackFrame? TokenValidationParametersNull; - internal static StackFrame? IssuerValidationFailed; - internal static StackFrame? SignatureValidationFailed; - - // Stack frames from ValidateConditions - internal static StackFrame? AudienceValidationFailed; - internal static StackFrame? AssertionNull; - internal static StackFrame? AssertionConditionsNull; - internal static StackFrame? AssertionConditionsValidationFailed; - internal static StackFrame? LifetimeValidationFailed; - internal static StackFrame? OneTimeUseValidationFailed; - } - } -} -#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs index 4b9f18d590..5cab5447cd 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.IdentityModel.Tokens.Saml; @@ -43,10 +42,9 @@ internal async Task> ValidateTokenAsync( { if (securityToken is null) { - StackFrames.TokenNull ??= new StackFrame(true); return ValidationError.NullParameter( nameof(securityToken), - StackFrames.TokenNull); + ValidationError.GetCurrentStackFrame()); } if (securityToken is not Saml2SecurityToken samlToken) @@ -64,10 +62,9 @@ internal async Task> ValidateTokenAsync( if (validationParameters is null) { - StackFrames.TokenValidationParametersNull ??= new StackFrame(true); return ValidationError.NullParameter( nameof(validationParameters), - StackFrames.TokenValidationParametersNull); + ValidationError.GetCurrentStackFrame()); } validationParameters = await SamlTokenUtilities.PopulateValidationParametersWithCurrentConfigurationAsync(validationParameters, cancellationToken).ConfigureAwait(false); @@ -138,10 +135,7 @@ internal async Task> ValidateTokenAsync( var signatureValidationResult = ValidateSignature(samlToken, validationParameters, callContext); if (!signatureValidationResult.IsValid) - { - StackFrames.SignatureValidationFailed ??= new StackFrame(true); - return signatureValidationResult.UnwrapError().AddStackFrame(StackFrames.SignatureValidationFailed); - } + return signatureValidationResult.UnwrapError().AddCurrentStackFrame(); ValidationResult issuerSigningKeyValidationResult; @@ -191,18 +185,16 @@ internal virtual ValidationResult ValidateConditions( { if (samlToken.Assertion is null) { - StackFrames.AssertionNull ??= new StackFrame(true); return ValidationError.NullParameter( nameof(samlToken.Assertion), - StackFrames.AssertionNull); + ValidationError.GetCurrentStackFrame()); } if (samlToken.Assertion.Conditions is null) { - StackFrames.AssertionConditionsNull ??= new StackFrame(true); return ValidationError.NullParameter( nameof(samlToken.Assertion.Conditions), - StackFrames.AssertionConditionsNull); + ValidationError.GetCurrentStackFrame()); } ValidationResult lifetimeValidationResult; diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.StackFrames.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.StackFrames.cs deleted file mode 100644 index 42c3ee2832..0000000000 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.StackFrames.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Diagnostics; - -#nullable enable -namespace Microsoft.IdentityModel.Tokens.Saml2 -{ - public partial class Saml2SecurityTokenHandler : SecurityTokenHandler - { - // Cached stack frames to build exceptions from validation errors - internal static class StackFrames - { - // Stack frames from ValidateTokenAsync using SecurityToken - internal static StackFrame? TokenNull; - internal static StackFrame? TokenValidationParametersNull; - internal static StackFrame? IssuerSigningKeyValidationFailed; - internal static StackFrame? IssuerValidationFailed; - internal static StackFrame? SignatureValidationFailed; - - // Stack frames from ValidateConditions - internal static StackFrame? AudienceValidationFailed; - internal static StackFrame? AssertionNull; - internal static StackFrame? AssertionConditionsNull; - internal static StackFrame? AssertionConditionsValidationFailed; - internal static StackFrame? LifetimeValidationFailed; - internal static StackFrame? OneTimeUseValidationFailed; - } - } -} -#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs index 4950042e26..a9fb5b21d1 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using System.Linq; using Microsoft.IdentityModel.Logging; @@ -48,7 +47,7 @@ internal static ValidationResult ValidateAlgorithm( if (validationParameters == null) return ValidationError.NullParameter( nameof(validationParameters), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame()); if (validationParameters.ValidAlgorithms != null && validationParameters.ValidAlgorithms.Count > 0 && @@ -59,7 +58,7 @@ internal static ValidationResult ValidateAlgorithm( LogHelper.MarkAsNonPII(algorithm)), ValidationFailureType.AlgorithmValidationFailed, typeof(SecurityTokenInvalidAlgorithmException), - new StackFrame(true), + ValidationError.GetCurrentStackFrame(), algorithm); return algorithm; diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs index 3bae752f35..ccb3087cc4 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs @@ -66,19 +66,19 @@ internal static async Task> ValidateIssuerAsyn new MessageDetail(LogMessages.IDX10211), ValidationFailureType.IssuerValidationFailed, typeof(SecurityTokenInvalidIssuerException), - new StackFrame(true), + ValidationError.GetCurrentStackFrame(), issuer); } if (validationParameters == null) return ValidationError.NullParameter( nameof(validationParameters), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame()); if (securityToken == null) return ValidationError.NullParameter( nameof(securityToken), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame()); BaseConfiguration? configuration = null; if (validationParameters.ConfigurationManager != null) @@ -90,7 +90,7 @@ internal static async Task> ValidateIssuerAsyn new MessageDetail(LogMessages.IDX10211), ValidationFailureType.IssuerValidationFailed, typeof(SecurityTokenInvalidIssuerException), - new StackFrame(true), + ValidationError.GetCurrentStackFrame(), issuer); if (configuration != null) @@ -141,7 +141,7 @@ internal static async Task> ValidateIssuerAsyn LogHelper.MarkAsNonPII(configuration?.Issuer)), ValidationFailureType.IssuerValidationFailed, typeof(SecurityTokenInvalidIssuerException), - new StackFrame(true), + ValidationError.GetCurrentStackFrame(), issuer); } } From 9b66954a7073dd467ddc7248c85634c235435f30 Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Tue, 10 Dec 2024 15:50:06 +0000 Subject: [PATCH 2/8] Updated IssuerValidationSource to be extensible. Extracted validated fields onto their own files and made the structures read-only. --- .../InternalAPI.Unshipped.txt | 17 +++++++- .../Results/IssuerValidationSource.cs | 24 +++++++++++ .../Validation/Results/ValidatedIssuer.cs | 27 ++++++++++++ .../Validation/Results/ValidatedLifetime.cs | 36 ++++++++++++++++ .../Results/ValidatedSigningKeyLifetime.cs | 43 +++++++++++++++++++ .../Validation/Results/ValidatedTokenType.cs | 34 +++++++++++++++ .../Validation/Validators.Issuer.cs | 10 ----- .../Validation/Validators.IssuerSigningKey.cs | 2 - .../Validation/Validators.Lifetime.cs | 2 - .../Validation/Validators.TokenType.cs | 1 - .../IdentityComparer.cs | 10 ++--- 11 files changed, 184 insertions(+), 22 deletions(-) create mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/IssuerValidationSource.cs create mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedIssuer.cs create mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedLifetime.cs create mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedSigningKeyLifetime.cs create mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedTokenType.cs diff --git a/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt index c05846e38a..a22b4e89ef 100644 --- a/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt @@ -22,8 +22,8 @@ Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError.InvalidSigningKey Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError.IssuerSigningKeyValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, Microsoft.IdentityModel.Tokens.SecurityKey invalidSigningKey, System.Exception innerException = null) -> void Microsoft.IdentityModel.Tokens.IssuerValidationError.InvalidIssuer.get -> string Microsoft.IdentityModel.Tokens.IssuerValidationError.IssuerValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidIssuer, System.Exception innerException = null) -> void -Microsoft.IdentityModel.Tokens.IssuerValidationSource.IssuerMatchedConfiguration = 1 -> Microsoft.IdentityModel.Tokens.IssuerValidationSource -Microsoft.IdentityModel.Tokens.IssuerValidationSource.IssuerMatchedValidationParameters = 2 -> Microsoft.IdentityModel.Tokens.IssuerValidationSource +Microsoft.IdentityModel.Tokens.IssuerValidationSource.IssuerValidationSource(string name) -> void +Microsoft.IdentityModel.Tokens.IssuerValidationSource.Name.get -> string Microsoft.IdentityModel.Tokens.LifetimeValidationError.Expires.get -> System.DateTime? Microsoft.IdentityModel.Tokens.LifetimeValidationError.LifetimeValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.DateTime? notBefore, System.DateTime? expires, System.Exception innerException = null) -> void Microsoft.IdentityModel.Tokens.LifetimeValidationError.NotBefore.get -> System.DateTime? @@ -45,6 +45,16 @@ Microsoft.IdentityModel.Tokens.TokenTypeValidationError.TokenTypeValidationError Microsoft.IdentityModel.Tokens.TokenValidationParameters.TimeProvider.get -> System.TimeProvider Microsoft.IdentityModel.Tokens.TokenValidationParameters.TimeProvider.set -> void Microsoft.IdentityModel.Tokens.ValidatedToken.Log(Microsoft.Extensions.Logging.ILogger logger) -> void +Microsoft.IdentityModel.Tokens.IssuerValidationSource +Microsoft.IdentityModel.Tokens.IssuerValidationSource.IssuerValidationSource(string name) -> void +Microsoft.IdentityModel.Tokens.IssuerValidationSource.Name.get -> string +Microsoft.IdentityModel.Tokens.ValidatedIssuer +Microsoft.IdentityModel.Tokens.ValidatedIssuer.Issuer.get -> string +Microsoft.IdentityModel.Tokens.ValidatedIssuer.Issuer.init -> void +Microsoft.IdentityModel.Tokens.ValidatedIssuer.ValidatedIssuer() -> void +Microsoft.IdentityModel.Tokens.ValidatedIssuer.ValidatedIssuer(string Issuer, Microsoft.IdentityModel.Tokens.IssuerValidationSource ValidationSource) -> void +Microsoft.IdentityModel.Tokens.ValidatedIssuer.ValidationSource.get -> Microsoft.IdentityModel.Tokens.IssuerValidationSource +Microsoft.IdentityModel.Tokens.ValidatedIssuer.ValidationSource.init -> void Microsoft.IdentityModel.Tokens.ValidationError.AddCurrentStackFrame(string filePath = "", int lineNumber = 0, int skipFrames = 1) -> Microsoft.IdentityModel.Tokens.ValidationError Microsoft.IdentityModel.Tokens.ValidationError.GetException(System.Type exceptionType, System.Exception innerException) -> System.Exception Microsoft.IdentityModel.Tokens.ValidationError.Log(Microsoft.Extensions.Logging.ILogger logger) -> void @@ -66,6 +76,9 @@ static Microsoft.IdentityModel.Tokens.TokenReplayValidationError.NullParameter(s static Microsoft.IdentityModel.Tokens.TokenTypeValidationError.NullParameter(string parameterName, System.Diagnostics.StackFrame stackFrame) -> Microsoft.IdentityModel.Tokens.TokenTypeValidationError static Microsoft.IdentityModel.Tokens.Utility.SerializeAsSingleCommaDelimitedString(System.Collections.Generic.IList strings) -> string static Microsoft.IdentityModel.Tokens.ValidationError.GetCurrentStackFrame(string filePath = "", int lineNumber = 0, int skipFrames = 1) -> System.Diagnostics.StackFrame +static readonly Microsoft.IdentityModel.Tokens.IssuerValidationSource.IssuerMatchedConfiguration -> Microsoft.IdentityModel.Tokens.IssuerValidationSource +static readonly Microsoft.IdentityModel.Tokens.IssuerValidationSource.IssuerMatchedValidationParameters -> Microsoft.IdentityModel.Tokens.IssuerValidationSource +static readonly Microsoft.IdentityModel.Tokens.IssuerValidationSource.NotValidated -> Microsoft.IdentityModel.Tokens.IssuerValidationSource static readonly Microsoft.IdentityModel.Tokens.LoggingEventId.TokenValidationFailed -> Microsoft.Extensions.Logging.EventId static readonly Microsoft.IdentityModel.Tokens.LoggingEventId.TokenValidationSucceeded -> Microsoft.Extensions.Logging.EventId static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.AlgorithmValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/IssuerValidationSource.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/IssuerValidationSource.cs new file mode 100644 index 0000000000..46868d0966 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/IssuerValidationSource.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Represents the source of the validation of an issuer. + /// + internal class IssuerValidationSource + { + protected IssuerValidationSource(string name) + { + Name = name; + } + + public string Name { get; } + + public static readonly IssuerValidationSource NotValidated = new("NotValidated"); + public static readonly IssuerValidationSource IssuerMatchedConfiguration = new("IssuerMatchedConfiguration"); + public static readonly IssuerValidationSource IssuerMatchedValidationParameters = new("IssuerMatchedValidationParameters"); + } +} +#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedIssuer.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedIssuer.cs new file mode 100644 index 0000000000..75d5bc99df --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedIssuer.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Represents a validated issuer, including the source of the validation. + /// + internal readonly struct ValidatedIssuer + { + /// + /// Initializes a new instance of . + /// + /// The issuer that was validated. + /// The source of the validation, i.e. configuration, validation parameters. + public ValidatedIssuer(string Issuer, IssuerValidationSource ValidationSource) + { + this.Issuer = Issuer; + this.ValidationSource = ValidationSource; + } + + public string Issuer { get; } + public IssuerValidationSource ValidationSource { get; } + } +} +#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedLifetime.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedLifetime.cs new file mode 100644 index 0000000000..b386789d1c --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedLifetime.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Represents a validated lifetime, including the NotBefore and Expires values. + /// + internal readonly struct ValidatedLifetime + { + /// + /// Initializes a new instance of . + /// + /// The representing the time from which the token is considered valid. + /// The representing the token's expiration time. + public ValidatedLifetime(DateTime? NotBefore, DateTime? Expires) + { + this.NotBefore = NotBefore; + this.Expires = Expires; + } + + /// + /// The representing the time from which the token is considered valid. + /// + public DateTime? NotBefore { get; } + + /// + /// The representing the token's expiration time. + /// + public DateTime? Expires { get; } + } +} +#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedSigningKeyLifetime.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedSigningKeyLifetime.cs new file mode 100644 index 0000000000..a927694956 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedSigningKeyLifetime.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Represents a validated signing key lifetime. + /// + internal readonly struct ValidatedSigningKeyLifetime + { + /// + /// Initializes a new instance of . + /// + /// The date from which the signing key is considered valid. + /// The date until which the signing key is considered valid. + /// The time the validation occurred. + internal ValidatedSigningKeyLifetime(DateTime? ValidFrom, DateTime? ValidTo, DateTime? ValidationTime) + { + this.ValidFrom = ValidFrom; + this.ValidTo = ValidTo; + this.ValidationTime = ValidationTime; + } + + /// + /// The date from which the signing key is considered valid. + /// + public DateTime? ValidFrom { get; } + + /// + /// The date until which the signing key is considered valid. + /// + public DateTime? ValidTo { get; } + + /// + /// The time the validation occurred. + /// + public DateTime? ValidationTime { get; } + } +} +#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedTokenType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedTokenType.cs new file mode 100644 index 0000000000..aba3f41d5d --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedTokenType.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Represents a validated token type, including the number of valid types present in the validation parameters. + /// + internal readonly struct ValidatedTokenType + { + /// + /// Initializes a new instance of . + /// + /// The token type that was validated. + /// The number of valid types present in the validation parameters. + internal ValidatedTokenType(string Type, int ValidTypeCount) + { + this.Type = Type; + this.ValidTypeCount = ValidTypeCount; + } + + /// + /// The token type that was validated. + /// + public string Type { get; } + + /// + /// The number of valid types present in the validation parameters. + /// + public int ValidTypeCount { get; } + } +} +#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs index ccb3087cc4..51ff189a58 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs @@ -9,16 +9,6 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens { - // TODO how do we extend this? - internal enum IssuerValidationSource - { - NotValidated = 0, - IssuerMatchedConfiguration, - IssuerMatchedValidationParameters - } - - internal record struct ValidatedIssuer(string Issuer, IssuerValidationSource ValidationSource); - /// /// Definition for delegate that will validate the issuer value in a token. /// diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs index 6e4ef95132..7edb5ed242 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs @@ -8,8 +8,6 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens { - internal record struct ValidatedSigningKeyLifetime(DateTime? ValidFrom, DateTime? ValidTo, DateTime? ValidationTime); - /// /// Definition for delegate that will validate the that signed a . /// diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs index 6f7262e40f..f72d403310 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs @@ -7,8 +7,6 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens { - internal record struct ValidatedLifetime(DateTime? NotBefore, DateTime? Expires); - /// /// Definition for delegate that will validate the lifetime of a . /// diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs index a95a60b363..7af2ac9ad8 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs @@ -8,7 +8,6 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens { - internal record struct ValidatedTokenType(string Type, int ValidTypeCount); /// /// Definition for delegate that will validate the token type of a token. /// diff --git a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs index f3152b4d34..dc7f78345b 100644 --- a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs +++ b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs @@ -1331,11 +1331,11 @@ internal static bool AreValidatedIssuersEqual(ValidatedIssuer validatedIssuer1, "validatedIssuer2.Issuer", localContext); - AreIntsEqual( - (int)validatedIssuer1.ValidationSource, - (int)validatedIssuer2.ValidationSource, - "validatedIssuer1.ValidationSource", - "validatedIssuer2.ValidationSource", + AreStringsEqual( + validatedIssuer1.ValidationSource.Name, + validatedIssuer2.ValidationSource.Name, + "validatedIssuer1.ValidationSource.Name", + "validatedIssuer2.ValidationSource.Name", localContext); return context.Merge(localContext); From 073aae5bb058cb84a34e7ebd2b97c3015f85ccf3 Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Tue, 10 Dec 2024 15:50:33 +0000 Subject: [PATCH 3/8] Updated documentation --- ...nWebTokenHandler.ValidateToken.Internal.cs | 14 ++----- ...rityTokenHandler.ValidateToken.Internal.cs | 10 +++++ ...rityTokenHandler.ValidateToken.Internal.cs | 10 +++++ .../Validation/Results/ValidatedToken.cs | 36 +++++++++++++++++ .../Validation/Results/ValidationResult.cs | 39 ++++++++++--------- .../Validation/Validators.Algorithm.cs | 9 +++-- .../Validation/Validators.Audience.cs | 14 +++---- .../Validation/Validators.Issuer.cs | 9 +---- .../Validation/Validators.IssuerSigningKey.cs | 14 +++---- .../Validation/Validators.Lifetime.cs | 13 ++----- .../Validation/Validators.TokenReplay.cs | 17 ++++---- .../Validation/Validators.TokenType.cs | 11 ++++-- 12 files changed, 118 insertions(+), 78 deletions(-) diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs index f4548d604d..b0f96f3ce4 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs @@ -17,22 +17,14 @@ public partial class JsonWebTokenHandler : TokenHandler { /// /// Validates a token. - /// On a validation failure, no exception will be thrown; instead, the exception will be set in the returned TokenValidationResult.Exception property. - /// Callers should always check the TokenValidationResult.IsValid property to verify the validity of the result. + /// On a validation failure, no exception will be thrown; instead, the will contain the information about the error that occurred. + /// Callers should always check the ValidationResult.IsValid property to verify the validity of the result. /// /// The token to be validated. /// The to be used for validating the token. - /// A that contains useful information for logging. + /// A that contains call information. /// A that can be used to request cancellation of the asynchronous operation. /// A with either a if the token was validated or an with the failure information and exception otherwise. - /// - /// ValidationError.GetException() will return one of the following exceptions if the is invalid. - /// - /// Returned if is null or empty. - /// Returned if is null. - /// Returned if 'token.Length' is greater than . - /// Returned if is not a valid , - /// Returned if the validationParameters.TokenReader delegate is not able to parse/read the token as a valid , internal async Task> ValidateTokenAsync( string token, ValidationParameters validationParameters, diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs index 2ddddbfd87..c48ebe8f76 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs @@ -14,6 +14,16 @@ namespace Microsoft.IdentityModel.Tokens.Saml /// public partial class SamlSecurityTokenHandler : SecurityTokenHandler { + /// + /// Validates a token. + /// On a validation failure, no exception will be thrown; instead, the will contain the information about the error that occurred. + /// Callers should always check the ValidationResult.IsValid property to verify the validity of the result. + /// + /// The token to be validated. + /// The to be used for validating the token. + /// A that contains call information. + /// A that can be used to request cancellation of the asynchronous operation. + /// A with either a if the token was validated or an with the failure information and exception otherwise. internal async Task> ValidateTokenAsync( string token, ValidationParameters validationParameters, diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs index 5cab5447cd..4d204760ee 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs @@ -15,6 +15,16 @@ namespace Microsoft.IdentityModel.Tokens.Saml2 /// public partial class Saml2SecurityTokenHandler : SecurityTokenHandler { + /// + /// Validates a token. + /// On a validation failure, no exception will be thrown; instead, the will contain the information about the error that occurred. + /// Callers should always check the ValidationResult.IsValid property to verify the validity of the result. + /// + /// The token to be validated. + /// The to be used for validating the token. + /// A that contains call information. + /// A that can be used to request cancellation of the asynchronous operation. + /// A with either a if the token was validated or an with the failure information and exception otherwise. internal async Task> ValidateTokenAsync( string token, ValidationParameters validationParameters, diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs index 7eccd96c75..e4bb9474d8 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs @@ -48,27 +48,60 @@ ActorValidationResult is not null ); } + /// + /// The that was validated. + /// public SecurityToken SecurityToken { get; private set; } + /// + /// The that was used to validate the token. + /// public TokenHandler TokenHandler { get; private set; } + /// + /// The that were used to validate the token. + /// public ValidationParameters ValidationParameters { get; private set; } #region Validated Properties + /// + /// The result of validating the actor, if any. + /// public ValidatedToken? ActorValidationResult { get; internal set; } + /// + /// The audience that was validated, if any. + /// public string? ValidatedAudience { get; internal set; } + /// + /// The issuer that was validated. If present, it contains the source of the validation as well. + /// public ValidatedIssuer? ValidatedIssuer { get; internal set; } + /// + /// The lifetime that was validated, if any. + /// public ValidatedLifetime? ValidatedLifetime { get; internal set; } + /// + /// The expiration time of the token that was used to validate the token was not replayed, if any. + /// public DateTime? ValidatedTokenReplayExpirationTime { get; internal set; } + /// + /// The token type that was validated, if any. + /// public ValidatedTokenType? ValidatedTokenType { get; internal set; } + /// + /// The that was used to validate the token, if any. + /// public SecurityKey? ValidatedSigningKey { get; internal set; } + /// + /// The validated lifetime of the that was used to sign the token, if any. + /// public ValidatedSigningKeyLifetime? ValidatedSigningKeyLifetime { get; internal set; } #endregion @@ -177,6 +210,9 @@ private object ClaimsIdentitySyncObj #endregion #region Logging + /// + /// Internal class used for logging. + /// private static class Logger { private static readonly Action s_tokenValidationFailed = diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidationResult.cs index 1d5c475ba4..1c1710066e 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidationResult.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidationResult.cs @@ -17,7 +17,8 @@ namespace Microsoft.IdentityModel.Tokens readonly ValidationError? _error; /// - /// Creates a successful, valid validation result. + /// Creates a new instance of indicating a successful operation + /// and containing an object of the associated type. /// /// The value associated with the success. public ValidationResult(TResult result) @@ -28,7 +29,8 @@ public ValidationResult(TResult result) } /// - /// Creates an error, invalid validation result. + /// Creates a new instance of indicating a failed operation + /// and containing a with the error information. /// /// The error associated with the failure. public ValidationResult(ValidationError error) @@ -115,10 +117,10 @@ public TResult? Result } /// - /// + /// Determines whether the specified object is equal to the current instance of . /// - /// - /// + /// The object to compare with the current instance. + /// true if the specified object is equal to the current instance; otherwise, false. public override bool Equals(object? obj) { if (obj is ValidationResult other) @@ -130,10 +132,9 @@ public override bool Equals(object? obj) } /// - /// + /// Returns the hash code for this instance of . /// - /// - /// + /// The hash code for the current instance. public override int GetHashCode() { if (IsValid) @@ -143,32 +144,32 @@ public override int GetHashCode() } /// - /// + /// Equality comparison operator for . /// - /// - /// - /// + /// The left value to compare. + /// The right value to compare. + /// A boolean indicating whether the left value is equal to the right one. public static bool operator ==(ValidationResult left, ValidationResult right) { return left.Equals(right); } /// - /// + /// Inequality comparison operator for . /// - /// - /// - /// + /// The left value to compare. + /// The right value to compare. + /// A boolean indicating whether the left value is not equal to the right one. public static bool operator !=(ValidationResult left, ValidationResult right) { return !(left == right); } /// - /// + /// Determines whether the specified is equal to the current instance. /// - /// - /// + /// The to compare with the current instance. + /// true if the specified is equal to the current instance; otherwise, false. public bool Equals(ValidationResult other) { if (other.IsValid != IsValid) diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs index a9fb5b21d1..1ed23b224b 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs @@ -25,6 +25,9 @@ internal delegate ValidationResult AlgorithmValidationDelegate( ValidationParameters validationParameters, CallContext callContext); + /// + /// Partial class for Algorithm Validation. + /// public static partial class Validators { /// @@ -34,15 +37,15 @@ public static partial class Validators /// The that signed the . /// The being validated. /// required for validation. - /// -#pragma warning disable CA1801 // TODO: remove pragma disable once callContext is used for logging + /// The that contains call information. internal static ValidationResult ValidateAlgorithm( string algorithm, +#pragma warning disable CA1801 SecurityKey securityKey, SecurityToken securityToken, ValidationParameters validationParameters, CallContext callContext) -#pragma warning restore CA1801 // TODO: remove pragma disable once callContext is used for logging +#pragma warning restore CA1801 { if (validationParameters == null) return ValidationError.NullParameter( diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs index 8b0903d3dc..3c11cf948d 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs @@ -35,14 +35,14 @@ public static partial class Validators /// The audiences found in the . /// The being validated. /// The to be used for validating the token. - /// - /// If 'validationParameters' is null. - /// If 'audiences' is null and is true. - /// If is null or whitespace and is null. - /// If none of the 'audiences' matched either or one of . + /// The that contains call information. /// An EXACT match is required. -#pragma warning disable CA1801 // TODO: remove pragma disable once callContext is used for logging - internal static ValidationResult ValidateAudience(IList tokenAudiences, SecurityToken? securityToken, ValidationParameters validationParameters, CallContext callContext) + internal static ValidationResult ValidateAudience( + IList tokenAudiences, +#pragma warning disable CA1801 + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) #pragma warning restore CA1801 { if (validationParameters == null) diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs index 51ff189a58..4772b31fc4 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.IdentityModel.Logging; @@ -27,7 +26,7 @@ internal delegate Task> IssuerValidationDelega CancellationToken cancellationToken); /// - /// IssuerValidation + /// Partial class for Issuer Validation. /// public static partial class Validators { @@ -113,13 +112,7 @@ internal static async Task> ValidateIssuerAsyn } if (string.Equals(validationParameters.ValidIssuers[i], issuer)) - { - // TODO: Add to CallContext - //if (LogHelper.IsEnabled(EventLogLevel.Informational)) - // LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer)); - return new ValidatedIssuer(issuer!, IssuerValidationSource.IssuerMatchedValidationParameters); - } } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs index 7edb5ed242..1c0cfec536 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs @@ -26,9 +26,8 @@ internal delegate ValidationResult IssuerSigningKey CallContext callContext); /// - /// SigningKeyValidation + /// Partial class for Issuer Signing Key Validation. /// - public static partial class Validators { /// @@ -38,10 +37,7 @@ public static partial class Validators /// The being validated. /// The to be used for validating the token. /// The to be used for validation. - /// The to be used for logging. - /// if 'securityKey' is null and ValidateIssuerSigningKey is true. - /// if 'securityToken' is null and ValidateIssuerSigningKey is true. - /// if 'validationParameters' is null. + /// The that contains call information. internal static ValidationResult ValidateIssuerSigningKey( SecurityKey securityKey, SecurityToken securityToken, @@ -49,7 +45,7 @@ internal static ValidationResult ValidateIssuerSign #pragma warning disable CA1801 // Review unused parameters BaseConfiguration? configuration, #pragma warning restore CA1801 // Review unused parameters - CallContext? callContext) + CallContext callContext) { if (validationParameters == null) return IssuerSigningKeyValidationError.NullParameter( @@ -77,12 +73,12 @@ internal static ValidationResult ValidateIssuerSign /// /// The that signed the . /// The to be used for validating the token. - /// + /// The that contains call information. #pragma warning disable CA1801 // Review unused parameters internal static ValidationResult ValidateIssuerSigningKeyLifeTime( SecurityKey securityKey, ValidationParameters validationParameters, - CallContext? callContext) + CallContext callContext) #pragma warning restore CA1801 // Review unused parameters { DateTime utcNow = validationParameters.TimeProvider.GetUtcNow().UtcDateTime; diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs index f72d403310..7aa37577b3 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs @@ -14,7 +14,7 @@ namespace Microsoft.IdentityModel.Tokens /// The 'expiration' time found in the . /// The that is being validated. /// The to be used for validating the token. - /// + /// The that contains call information. /// A that contains the results of validating the issuer. /// This delegate is not expected to throw. internal delegate ValidationResult LifetimeValidationDelegate( @@ -25,7 +25,7 @@ internal delegate ValidationResult LifetimeValidationDelegate CallContext callContext); /// - /// IssuerValidation + /// Partial class for Lifetime Validation. /// public static partial class Validators { @@ -36,15 +36,10 @@ public static partial class Validators /// The 'expiration' time found in the . /// The being validated. /// The to be used for validating the token. - /// + /// The that contains call information. /// A indicating whether validation was successful, and providing a if it was not. - /// If 'validationParameters' is null. - /// If 'expires.HasValue' is false. - /// If 'notBefore' is > 'expires'. - /// If 'notBefore' is > DateTime.UtcNow. - /// If 'expires' is < DateTime.UtcNow. /// All time comparisons apply . -#pragma warning disable CA1801 // TODO: remove pragma disable once callContext is used for logging +#pragma warning disable CA1801 internal static ValidationResult ValidateLifetime( DateTime? notBefore, DateTime? expires, diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs index 3e8de4ffd9..f25292e5cb 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs @@ -3,6 +3,7 @@ using System; +#nullable enable namespace Microsoft.IdentityModel.Tokens { /// @@ -11,7 +12,7 @@ namespace Microsoft.IdentityModel.Tokens /// When does the expire.. /// The security token that is being validated. /// The to be used for validating the token. - /// + /// The that contains call information. /// A that contains the results of validating the token. /// This delegate is not expected to throw. internal delegate ValidationResult TokenReplayValidationDelegate( @@ -31,14 +32,13 @@ public static partial class Validators /// When does the security token expire. /// The being validated. /// The to be used for validating the token. - /// - /// If 'securityToken' is null or whitespace. - /// If 'validationParameters' is null or whitespace. - /// If is not null and expirationTime.HasValue is false. When a TokenReplayCache is set, tokens require an expiration time. - /// If the 'securityToken' is found in the cache. - /// If the 'securityToken' could not be added to the . + /// The that contains call information. #pragma warning disable CA1801 // Review unused parameters - internal static ValidationResult ValidateTokenReplay(DateTime? expirationTime, string securityToken, ValidationParameters validationParameters, CallContext callContext) + internal static ValidationResult ValidateTokenReplay( + DateTime? expirationTime, + string securityToken, + ValidationParameters validationParameters, + CallContext callContext) #pragma warning restore CA1801 // Review unused parameters { if (string.IsNullOrWhiteSpace(securityToken)) @@ -92,3 +92,4 @@ public static partial class Validators } } } +#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs index 7af2ac9ad8..2ccce42550 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs @@ -14,7 +14,7 @@ namespace Microsoft.IdentityModel.Tokens /// The token type or null if it couldn't be resolved (e.g from the 'typ' header for a JWT). /// The that is being validated. /// required for validation. - /// + /// The that contains call information. /// A that contains the results of validating the token type. /// An EXACT match is required. (case sensitive) is used for comparing against . internal delegate ValidationResult TokenTypeValidationDelegate( @@ -23,6 +23,9 @@ internal delegate ValidationResult TokenTypeValidationDelega ValidationParameters validationParameters, CallContext callContext); + /// + /// Partial class for Token Type Validation. + /// public static partial class Validators { /// @@ -31,16 +34,16 @@ public static partial class Validators /// The token type or null if it couldn't be resolved (e.g from the 'typ' header for a JWT). /// The that is being validated. /// required for validation. - /// + /// The that contains call information. /// A that contains the results of validating the token type. /// An EXACT match is required. (case sensitive) is used for comparing against . -#pragma warning disable CA1801 // TODO: remove pragma disable once callContext is used for logging +#pragma warning disable CA1801 internal static ValidationResult ValidateTokenType( string? type, SecurityToken? securityToken, ValidationParameters validationParameters, CallContext callContext) -#pragma warning restore CA1801 // TODO: remove pragma disable once callContext is used for logging +#pragma warning restore CA1801 { if (securityToken == null) return TokenTypeValidationError.NullParameter( From 206c0beb08d4fd9815afee5f51e51cd68da29601 Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Mon, 16 Dec 2024 14:49:10 +0000 Subject: [PATCH 4/8] Added nullability annotations to ValidationParameters. Enabled setting IList values from two-part constructors. --- .../Validation/ValidationParameters.cs | 178 ++++++++++++------ 1 file changed, 120 insertions(+), 58 deletions(-) diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs index 24fdaed4e5..6b0d151d0a 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs @@ -8,6 +8,7 @@ using System.Threading; using Microsoft.IdentityModel.Logging; +#nullable enable namespace Microsoft.IdentityModel.Tokens { /// @@ -15,21 +16,24 @@ namespace Microsoft.IdentityModel.Tokens /// internal class ValidationParameters { - private string _authenticationType; + private string? _authenticationType; private TimeSpan _clockSkew = DefaultClockSkew; private string _nameClaimType = ClaimsIdentity.DefaultNameClaimType; private string _roleClaimType = ClaimsIdentity.DefaultRoleClaimType; - private Dictionary _instancePropertyBag; - private IList _issuerSigningKeys; - private IList _validIssuers; - private IList _validTokenTypes; - private IList _validAudiences; + private Dictionary? _instancePropertyBag; + private IList? _issuerSigningKeys; + private IDictionary? _propertyBag; + private IList? _tokenDecryptionKeys; + private IList? _validIssuers; + private IList? _validTokenTypes; + private IList? _validAudiences; + private IList? _validAlgorithms; private AlgorithmValidationDelegate _algorithmValidator = Validators.ValidateAlgorithm; private AudienceValidationDelegate _audienceValidator = Validators.ValidateAudience; private IssuerValidationDelegateAsync _issuerValidatorAsync = Validators.ValidateIssuerAsync; private LifetimeValidationDelegate _lifetimeValidator = Validators.ValidateLifetime; - private SignatureValidationDelegate _signatureValidator; + private SignatureValidationDelegate? _signatureValidator; private TokenReplayValidationDelegate _tokenReplayValidator = Validators.ValidateTokenReplay; private TokenTypeValidationDelegate _tokenTypeValidator = Validators.ValidateTokenType; private IssuerSigningKeyValidationDelegate _issuerSigningKeyValidator = Validators.ValidateIssuerSigningKey; @@ -61,11 +65,13 @@ protected ValidationParameters(ValidationParameters other) if (other == null) throw LogHelper.LogExceptionMessage(new ArgumentNullException(nameof(other))); + ActorValidationParameters = other.ActorValidationParameters; AlgorithmValidator = other.AlgorithmValidator; AudienceValidator = other.AudienceValidator; _authenticationType = other._authenticationType; ClockSkew = other.ClockSkew; ConfigurationManager = other.ConfigurationManager; + CryptoProviderFactory = other.CryptoProviderFactory; DebugId = other.DebugId; IncludeTokenOnFailedValidation = other.IncludeTokenOnFailedValidation; IgnoreTrailingSlashWhenValidatingAudience = other.IgnoreTrailingSlashWhenValidatingAudience; @@ -77,24 +83,23 @@ protected ValidationParameters(ValidationParameters other) LogTokenId = other.LogTokenId; NameClaimType = other.NameClaimType; NameClaimTypeRetriever = other.NameClaimTypeRetriever; - PropertyBag = other.PropertyBag; + _propertyBag = other.PropertyBag; RefreshBeforeValidation = other.RefreshBeforeValidation; RoleClaimType = other.RoleClaimType; RoleClaimTypeRetriever = other.RoleClaimTypeRetriever; SaveSigninToken = other.SaveSigninToken; - SignatureValidator = other.SignatureValidator; + _signatureValidator = other.SignatureValidator; TimeProvider = other.TimeProvider; TokenDecryptionKeyResolver = other.TokenDecryptionKeyResolver; - TokenDecryptionKeys = other.TokenDecryptionKeys; + _tokenDecryptionKeys = other.TokenDecryptionKeys; TokenReplayCache = other.TokenReplayCache; TokenReplayValidator = other.TokenReplayValidator; TokenTypeValidator = other.TokenTypeValidator; ValidateActor = other.ValidateActor; - ValidateSignatureLast = other.ValidateSignatureLast; ValidateWithLKG = other.ValidateWithLKG; - ValidAlgorithms = other.ValidAlgorithms; _validIssuers = other.ValidIssuers; _validAudiences = other.ValidAudiences; + _validAlgorithms = other.ValidAlgorithms; _validTokenTypes = other.ValidTypes; } @@ -111,7 +116,7 @@ public ValidationParameters() /// /// Gets or sets . /// - public ValidationParameters ActorValidationParameters { get; set; } + public ValidationParameters? ActorValidationParameters { get; set; } /// /// Allows overriding the delegate used to validate the cryptographic algorithm used. @@ -145,7 +150,7 @@ public AudienceValidationDelegate AudienceValidator /// Gets or sets the AuthenticationType when creating a . /// /// If 'value' is null or whitespace. - public string AuthenticationType + public string? AuthenticationType { get { @@ -158,7 +163,7 @@ public string AuthenticationType throw LogHelper.LogExceptionMessage(new ArgumentNullException("AuthenticationType")); } - _authenticationType = value; + _authenticationType = value!; } } @@ -230,26 +235,28 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken, //if (LogHelper.IsEnabled(EventLogLevel.Informational)) // LogHelper.LogInformation(LogMessages.IDX10245, securityToken); -#pragma warning disable RS0030 // Do not use banned APIs - return new ClaimsIdentity(authenticationType: AuthenticationType ?? DefaultAuthenticationType, nameType: nameClaimType ?? ClaimsIdentity.DefaultNameClaimType, roleType: roleClaimType ?? ClaimsIdentity.DefaultRoleClaimType); -#pragma warning disable RS0030 // Do not use banned APIs + return ClaimsIdentityFactory.Create( + authenticationType: AuthenticationType ?? DefaultAuthenticationType, + nameType: nameClaimType ?? ClaimsIdentity.DefaultNameClaimType, + roleType: roleClaimType ?? ClaimsIdentity.DefaultRoleClaimType, + securityToken); } /// /// If set, this property will be used to obtain the issuer and signing keys associated with the metadata endpoint of . /// The obtained issuer and signing keys will then be used along with those present on the ValidationParameters for validation of the incoming token. /// - public BaseConfigurationManager ConfigurationManager { get; set; } + public BaseConfigurationManager? ConfigurationManager { get; set; } /// /// Users can override the default with this property. This factory will be used for creating signature providers. /// - public CryptoProviderFactory CryptoProviderFactory { get; set; } + public CryptoProviderFactory? CryptoProviderFactory { get; set; } /// /// Gets or sets a string that helps with setting breakpoints when debugging. /// - public string DebugId { get; set; } + public string? DebugId { get; set; } /// /// Gets or sets a boolean that controls if a '/' is significant at the end of the audience. @@ -282,7 +289,8 @@ public IssuerSigningKeyValidationDelegate IssuerSigningKeyValidator /// Gets a that is unique to this instance. /// Calling will result in a new instance of this IDictionary. /// - public IDictionary InstancePropertyBag => _instancePropertyBag ??= new Dictionary(); + public IDictionary InstancePropertyBag => + _instancePropertyBag ??= new Dictionary(); /// /// Gets a value indicating if was called to obtain this instance. @@ -294,18 +302,25 @@ public IssuerSigningKeyValidationDelegate IssuerSigningKeyValidator /// /// /// This will be used to check the signature. This can be helpful when the does not contain a key identifier. - /// If both and are set, IssuerSigningKeyResolverUsingConfiguration takes - /// priority. /// - public IssuerSigningKeyResolverDelegate IssuerSigningKeyResolver { get; set; } + public IssuerSigningKeyResolverDelegate? IssuerSigningKeyResolver { get; set; } /// /// Gets the used for signature validation. /// - public IList IssuerSigningKeys => - _issuerSigningKeys ?? - Interlocked.CompareExchange(ref _issuerSigningKeys, [], null) ?? - _issuerSigningKeys; + public IList IssuerSigningKeys + { + get + { + return _issuerSigningKeys ?? + Interlocked.CompareExchange(ref _issuerSigningKeys, [], null) ?? + _issuerSigningKeys; + } + internal set + { + _issuerSigningKeys = value; + } + } /// /// Allows overriding the delegate that will be used to validate the issuer of the token. @@ -368,13 +383,13 @@ public string NameClaimType /// The issuer associated with the token. /// Returns the value that will set the property . /// - public Func NameClaimTypeRetriever { get; set; } + public Func? NameClaimTypeRetriever { get; set; } /// /// Gets or sets the that contains a collection of custom key/value pairs. /// This allows addition of parameters that could be used in custom token validation scenarios. /// - public IDictionary PropertyBag { get; } + public IDictionary PropertyBag => _propertyBag ??= new Dictionary(); /// /// A boolean to control whether configuration should be refreshed before validating a token. @@ -418,7 +433,7 @@ public string RoleClaimType /// The issuer associated with the token. /// Returns the value that will set the property . /// - public Func RoleClaimTypeRetriever { get; set; } + public Func? RoleClaimTypeRetriever { get; set; } /// /// Gets or sets a boolean to control if the original token should be saved after the security token is validated. @@ -435,7 +450,7 @@ public string RoleClaimType /// /// If set, this delegate will be called to validate the signature of the token, instead of default processing. /// - public SignatureValidationDelegate SignatureValidator + public SignatureValidationDelegate? SignatureValidator { get { return _signatureValidator; } set { _signatureValidator = value; } @@ -452,18 +467,30 @@ public SignatureValidationDelegate SignatureValidator /// /// This will be used to decrypt the token. This can be helpful when the does not contain a key identifier. /// - internal DecryptionKeyResolverDelegate TokenDecryptionKeyResolver { get; set; } + internal DecryptionKeyResolverDelegate? TokenDecryptionKeyResolver { get; set; } /// /// Gets the that is to be used for decrypting inbound tokens. /// - public IList TokenDecryptionKeys { get; internal set; } + public IList TokenDecryptionKeys + { + get + { + return _tokenDecryptionKeys ?? + Interlocked.CompareExchange(ref _tokenDecryptionKeys, [], null) ?? + _tokenDecryptionKeys; + } + internal set + { + _tokenDecryptionKeys = value; + } + } /// /// Gets or set the that store tokens that can be checked to help detect token replay. /// /// If set, then tokens must have an expiration time or the runtime will fault. - public ITokenReplayCache TokenReplayCache { get; set; } + public ITokenReplayCache? TokenReplayCache { get; set; } /// /// Allows overriding the delegate that will be used to validate the token replay of the token. @@ -514,15 +541,6 @@ public TokenTypeValidationDelegate TokenTypeValidator [DefaultValue(false)] public bool ValidateWithLKG { get; set; } - /// - /// Gets or sets a boolean that controls the validation order of the payload and signature during token validation. - /// - /// If is set to true, it will validate payload ahead of signature. - /// The default is false. - /// - [DefaultValue(false)] - public bool ValidateSignatureLast { get; set; } - /// /// Gets or sets the valid algorithms for cryptographic operations. /// @@ -530,26 +548,56 @@ public TokenTypeValidationDelegate TokenTypeValidator /// If set to a non-empty collection, only the algorithms listed will be considered valid. /// The default is null. /// - public IList ValidAlgorithms { get; set; } + public IList ValidAlgorithms + { + get + { + return _validAlgorithms ?? + Interlocked.CompareExchange(ref _validAlgorithms, [], null) ?? + _validAlgorithms; + } + internal set + { + _validAlgorithms = value; + } + } /// /// Gets the that contains valid audiences that will be used to check against the token's audience. /// The default is an empty collection. /// - public IList ValidAudiences => - _validAudiences ?? - Interlocked.CompareExchange(ref _validAudiences, [], null) ?? - _validAudiences; + public IList ValidAudiences + { + get + { + return _validAudiences ?? + Interlocked.CompareExchange(ref _validAudiences, [], null) ?? + _validAudiences; + } + internal set + { + _validAudiences = value; + } + } /// /// Gets the that contains valid issuers that will be used to check against the token's issuer. /// The default is an empty collection. /// /// The that contains valid issuers that will be used to check against the token's 'iss' claim. - public IList ValidIssuers => - _validIssuers ?? - Interlocked.CompareExchange(ref _validIssuers, [], null) ?? - _validIssuers; + public IList ValidIssuers + { + get + { + return _validIssuers ?? + Interlocked.CompareExchange(ref _validIssuers, [], null) ?? + _validIssuers; + } + internal set + { + _validIssuers = value; + } + } /// /// Gets the that contains valid types that will be used to check against the JWT header's 'typ' claim. @@ -558,11 +606,25 @@ public TokenTypeValidationDelegate TokenTypeValidator /// The default is an empty collection. /// /// The that contains valid token types that will be used to check against the token's 'typ' claim. - public IList ValidTypes => - _validTokenTypes ?? - Interlocked.CompareExchange(ref _validTokenTypes, [], null) ?? - _validTokenTypes; + public IList ValidTypes + { + get + { + return _validTokenTypes ?? + Interlocked.CompareExchange(ref _validTokenTypes, [], null) ?? + _validTokenTypes; + } + internal set + { + _validTokenTypes = value; + } + } + /// + /// Gets or sets a boolean that controls if the actor claim should be validated. + /// + /// Default value is false. public bool ValidateActor { get; set; } } } +#nullable restore From aa94abc2c0534900dab56aec7b22d540d6238f8d Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Mon, 16 Dec 2024 14:49:56 +0000 Subject: [PATCH 5/8] Handle case where ValidateActor is true, there is an actor token, but no ActorValidationParameters are provided. --- .../JsonWebTokenHandler.ValidateToken.Internal.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs index b0f96f3ce4..2fd9d160c2 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs @@ -340,6 +340,11 @@ private async ValueTask> ValidateJWSAsync( if (!actorReadingResult.IsValid) return actorReadingResult.UnwrapError().AddCurrentStackFrame(); + if (validationParameters.ActorValidationParameters is null) + return ValidationError.NullParameter( + nameof(validationParameters.ActorValidationParameters), + ValidationError.GetCurrentStackFrame()); + JsonWebToken actorToken = (actorReadingResult.UnwrapResult() as JsonWebToken)!; ValidationParameters actorParameters = validationParameters.ActorValidationParameters; ValidationResult innerActorValidationResult = From 9e2746ca0511586acb9540e83166878c02d39f25 Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Mon, 16 Dec 2024 14:58:43 +0000 Subject: [PATCH 6/8] Updated documentation, added missing interfaces and methods required once the classes/structures are made public. --- .../Results/IssuerValidationSource.cs | 23 ++++-- .../Validation/Results/ValidatedIssuer.cs | 72 ++++++++++++++++++- .../Validation/Results/ValidatedLifetime.cs | 63 +++++++++++++++- .../Results/ValidatedSigningKeyLifetime.cs | 63 +++++++++++++++- .../Validation/Results/ValidatedToken.cs | 38 ++++------ .../Validation/Results/ValidatedTokenType.cs | 65 ++++++++++++++++- .../Validation/Results/ValidationResult.cs | 2 +- ...ndler.ValidateTokenAsyncTests.Algorithm.cs | 7 +- ...Tests.ValidateTokenAsyncTests.Algorithm.cs | 11 ++- ...Tests.ValidateTokenAsyncTests.Algorithm.cs | 11 ++- 10 files changed, 306 insertions(+), 49 deletions(-) diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/IssuerValidationSource.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/IssuerValidationSource.cs index 46868d0966..30ae1301ed 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/IssuerValidationSource.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/IssuerValidationSource.cs @@ -9,15 +9,30 @@ namespace Microsoft.IdentityModel.Tokens /// internal class IssuerValidationSource { - protected IssuerValidationSource(string name) - { - Name = name; - } + /// + /// Initializes a new instance of . + /// + /// The name of the issuer validation source. + public IssuerValidationSource(string name) => Name = name; + /// + /// The name of the issuer validation source. + /// public string Name { get; } + /// + /// Represents the issuer validation source that has not been validated. + /// public static readonly IssuerValidationSource NotValidated = new("NotValidated"); + + /// + /// Represents the issuer validation source that has been matched with the configuration provided. + /// public static readonly IssuerValidationSource IssuerMatchedConfiguration = new("IssuerMatchedConfiguration"); + + /// + /// Represents the issuer validation source that has been matched with the validation parameters provided. + /// public static readonly IssuerValidationSource IssuerMatchedValidationParameters = new("IssuerMatchedValidationParameters"); } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedIssuer.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedIssuer.cs index 75d5bc99df..e5752cec94 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedIssuer.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedIssuer.cs @@ -1,13 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; + #nullable enable namespace Microsoft.IdentityModel.Tokens { /// /// Represents a validated issuer, including the source of the validation. /// - internal readonly struct ValidatedIssuer + internal readonly struct ValidatedIssuer : IEquatable { /// /// Initializes a new instance of . @@ -20,8 +22,76 @@ public ValidatedIssuer(string Issuer, IssuerValidationSource ValidationSource) this.ValidationSource = ValidationSource; } + /// + /// The issuer that was validated. + /// public string Issuer { get; } + + /// + /// The source of the validation, i.e. configuration, validation parameters. + /// public IssuerValidationSource ValidationSource { get; } + + /// + /// Determines whether the specified object is equal to the current instance of . + /// + /// The object to compare with the current instance. + /// true if the specified object is equal to the current instance; otherwise, false. + public override bool Equals(object? obj) + { + if (obj is ValidatedIssuer other) + { + return Equals(other); + } + + return false; + } + + /// + /// Returns the hash code for this instance of . + /// + /// + public override int GetHashCode() + { + return Issuer.GetHashCode() ^ ValidationSource.GetHashCode(); + } + + /// + /// Equality comparison operator for . + /// + /// The left value to compare. + /// The right value to compare. + /// A boolean indicating whether the left value is equal to the right one. + public static bool operator ==(ValidatedIssuer left, ValidatedIssuer right) + { + return left.Equals(right); + } + + /// + /// Inequality comparison operator for . + /// + /// The left value to compare. + /// The right value to compare. + /// A boolean indicating whether the left value is not equal to the right one. + public static bool operator !=(ValidatedIssuer left, ValidatedIssuer right) + { + return !(left == right); + } + + /// + /// Determines whether the specified is equal to the current instance. + /// + /// The to compare with the current instance. + /// true if the specified is equal to the current instance; otherwise, false. + public bool Equals(ValidatedIssuer other) + { + if (other.Issuer != Issuer || other.ValidationSource != ValidationSource) + { + return false; + } + + return true; + } } } #nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedLifetime.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedLifetime.cs index b386789d1c..88f17d02bf 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedLifetime.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedLifetime.cs @@ -9,7 +9,7 @@ namespace Microsoft.IdentityModel.Tokens /// /// Represents a validated lifetime, including the NotBefore and Expires values. /// - internal readonly struct ValidatedLifetime + internal readonly struct ValidatedLifetime : IEquatable { /// /// Initializes a new instance of . @@ -31,6 +31,67 @@ public ValidatedLifetime(DateTime? NotBefore, DateTime? Expires) /// The representing the token's expiration time. /// public DateTime? Expires { get; } + + /// + /// Determines whether the specified object is equal to the current instance of . + /// + /// The object to compare with the current instance. + /// true if the specified object is equal to the current instance; otherwise, false. + public override bool Equals(object? obj) + { + if (obj is ValidatedLifetime other) + { + return Equals(other); + } + + return false; + } + + /// + /// Returns the hash code for this instance of . + /// + /// + public override int GetHashCode() + { + return NotBefore.GetHashCode() ^ Expires.GetHashCode(); + } + + /// + /// Equality comparison operator for . + /// + /// The left value to compare. + /// The right value to compare. + /// A boolean indicating whether the left value is equal to the right one. + public static bool operator ==(ValidatedLifetime left, ValidatedLifetime right) + { + return left.Equals(right); + } + + /// + /// Inequality comparison operator for . + /// + /// The left value to compare. + /// The right value to compare. + /// A boolean indicating whether the left value is not equal to the right one. + public static bool operator !=(ValidatedLifetime left, ValidatedLifetime right) + { + return !(left == right); + } + + /// + /// Determines whether the specified is equal to the current instance. + /// + /// The to compare with the current instance. + /// true if the specified is equal to the current instance; otherwise, false. + public bool Equals(ValidatedLifetime other) + { + if (other.NotBefore != NotBefore || other.Expires != Expires) + { + return false; + } + + return true; + } } } #nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedSigningKeyLifetime.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedSigningKeyLifetime.cs index a927694956..dc7285728f 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedSigningKeyLifetime.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedSigningKeyLifetime.cs @@ -9,7 +9,7 @@ namespace Microsoft.IdentityModel.Tokens /// /// Represents a validated signing key lifetime. /// - internal readonly struct ValidatedSigningKeyLifetime + internal readonly struct ValidatedSigningKeyLifetime : IEquatable { /// /// Initializes a new instance of . @@ -38,6 +38,67 @@ internal ValidatedSigningKeyLifetime(DateTime? ValidFrom, DateTime? ValidTo, Dat /// The time the validation occurred. /// public DateTime? ValidationTime { get; } + + /// + /// Determines whether the specified object is equal to the current instance of . + /// + /// The object to compare with the current instance. + /// true if the specified object is equal to the current instance; otherwise, false. + public override bool Equals(object? obj) + { + if (obj is ValidatedSigningKeyLifetime other) + { + return Equals(other); + } + + return false; + } + + /// + /// Returns the hash code for this instance of . + /// + /// + public override int GetHashCode() + { + return ValidFrom.GetHashCode() ^ ValidTo.GetHashCode() ^ ValidationTime.GetHashCode(); + } + + /// + /// Equality comparison operator for . + /// + /// The left value to compare. + /// The right value to compare. + /// A boolean indicating whether the left value is equal to the right one. + public static bool operator ==(ValidatedSigningKeyLifetime left, ValidatedSigningKeyLifetime right) + { + return left.Equals(right); + } + + /// + /// Inequality comparison operator for . + /// + /// The left value to compare. + /// The right value to compare. + /// A boolean indicating whether the left value is not equal to the right one. + public static bool operator !=(ValidatedSigningKeyLifetime left, ValidatedSigningKeyLifetime right) + { + return !(left == right); + } + + /// + /// Determines whether the specified is equal to the current instance. + /// + /// The to compare with the current instance. + /// true if the specified is equal to the current instance; otherwise, false. + public bool Equals(ValidatedSigningKeyLifetime other) + { + if (other.ValidFrom != ValidFrom || other.ValidTo != ValidTo || other.ValidationTime != ValidationTime) + { + return false; + } + + return true; + } } } #nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs index e4bb9474d8..ba4fe95fa4 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs @@ -14,30 +14,21 @@ namespace Microsoft.IdentityModel.Tokens /// /// Contains the results of successfully validating a . /// - internal class ValidatedToken + /// + /// Creates an instance of + /// + /// The that is being validated. + /// The that is being used to validate the token. + /// The to be used for validating the token. + internal class ValidatedToken( + SecurityToken securityToken, + TokenHandler tokenHandler, + ValidationParameters validationParameters) { - /// - /// Creates an instance of - /// - /// The that is being validated. - /// The that is being used to validate the token. - /// The to be used for validating the token. - internal ValidatedToken( - SecurityToken securityToken, - TokenHandler tokenHandler, - ValidationParameters validationParameters) - { - TokenHandler = tokenHandler ?? throw new ArgumentNullException(nameof(tokenHandler)); - SecurityToken = securityToken ?? throw new ArgumentNullException(nameof(securityToken)); - ValidationParameters = validationParameters ?? throw new ArgumentNullException(nameof(validationParameters)); - } - /// /// Logs the validation result. /// - public void Log(ILogger logger) - { - Logger.TokenValidationSucceeded( + public void Log(ILogger logger) => Logger.TokenValidationSucceeded( logger, ValidatedAudience ?? "none", ValidatedLifetime, @@ -46,22 +37,21 @@ public void Log(ILogger logger) ValidatedSigningKey?.KeyId ?? "none", ActorValidationResult is not null ); - } /// /// The that was validated. /// - public SecurityToken SecurityToken { get; private set; } + public SecurityToken SecurityToken { get; private set; } = securityToken ?? throw new ArgumentNullException(nameof(securityToken)); /// /// The that was used to validate the token. /// - public TokenHandler TokenHandler { get; private set; } + public TokenHandler TokenHandler { get; private set; } = tokenHandler ?? throw new ArgumentNullException(nameof(tokenHandler)); /// /// The that were used to validate the token. /// - public ValidationParameters ValidationParameters { get; private set; } + public ValidationParameters ValidationParameters { get; private set; } = validationParameters ?? throw new ArgumentNullException(nameof(validationParameters)); #region Validated Properties /// diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedTokenType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedTokenType.cs index aba3f41d5d..8c76e00fa3 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedTokenType.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedTokenType.cs @@ -1,20 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; + #nullable enable namespace Microsoft.IdentityModel.Tokens { /// /// Represents a validated token type, including the number of valid types present in the validation parameters. /// - internal readonly struct ValidatedTokenType + internal readonly struct ValidatedTokenType : IEquatable { /// /// Initializes a new instance of . /// /// The token type that was validated. /// The number of valid types present in the validation parameters. - internal ValidatedTokenType(string Type, int ValidTypeCount) + public ValidatedTokenType(string Type, int ValidTypeCount) { this.Type = Type; this.ValidTypeCount = ValidTypeCount; @@ -29,6 +31,65 @@ internal ValidatedTokenType(string Type, int ValidTypeCount) /// The number of valid types present in the validation parameters. /// public int ValidTypeCount { get; } + + /// + /// Determines whether the specified object is equal to the current instance of . + /// + /// The object to compare with the current instance. + /// true if the specified object is equal to the current instance; otherwise, false. + public override bool Equals(object? obj) + { + if (obj is ValidatedTokenType other) + { + return Equals(other); + } + + return false; + } + + /// + /// Returns the hash code for this instance of . + /// + /// The hash code for the current instance. + public override int GetHashCode() + { + return Type.GetHashCode() ^ ValidTypeCount.GetHashCode(); + } + + /// + /// Equality comparison operator for . + /// + /// The left value to compare. + /// The right value to compare. + /// A boolean indicating whether the left value is equal to the right one. + public static bool operator ==(ValidatedTokenType left, ValidatedTokenType right) + { + return left.Equals(right); + } + + /// + /// Inequality comparison operator for . + /// + /// The left value to compare. + /// The right value to compare. + /// A boolean indicating whether the left value is not equal to the right one. + public static bool operator !=(ValidatedTokenType left, ValidatedTokenType right) + { + return !(left == right); + } + + /// + /// Determines whether the specified is equal to the current instance. + /// + /// The to compare with the current instance. + /// true if the specified is equal to the current instance; otherwise, false. + public bool Equals(ValidatedTokenType other) + { + if (other.Type != Type || other.ValidTypeCount != ValidTypeCount) + return false; + + return true; + } } } #nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidationResult.cs index 1c1710066e..8bb3c9d5e0 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidationResult.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidationResult.cs @@ -186,7 +186,7 @@ public bool Equals(ValidationResult other) /// # /// Required for compatibility, see CA2225 for more information /// The existing instance. - public ValidationResult ToResult() + public ValidationResult ToValidationResult() { return this; } diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Algorithm.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Algorithm.cs index b26524130c..b6872c70e9 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Algorithm.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Algorithm.cs @@ -103,13 +103,14 @@ static TokenValidationParameters CreateTokenValidationParameters( static ValidationParameters CreateValidationParameters( SecurityKey? signingKey = null, List? validAlgorithms = null) { - ValidationParameters validationParameters = new ValidationParameters(); + ValidationParameters validationParameters = new ValidationParameters + { + ValidAlgorithms = validAlgorithms ?? [] + }; if (signingKey is not null) validationParameters.IssuerSigningKeys.Add(signingKey); - validationParameters.ValidAlgorithms = validAlgorithms; - // Skip all validations except signature and algorithm validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation; diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.Algorithm.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.Algorithm.cs index 13581b3d01..17fdda1df1 100644 --- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.Algorithm.cs +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.Algorithm.cs @@ -143,12 +143,11 @@ public static TheoryData ValidateTokenAsy static ValidationParameters CreateValidationParameters( SecurityKey? signingKey = null, List? validAlgorithms = null, bool tryAllKeys = false) { - ValidationParameters validationParameters = new ValidationParameters(); - - if (signingKey is not null) - validationParameters.IssuerSigningKeys.Add(signingKey); - - validationParameters.ValidAlgorithms = validAlgorithms; + ValidationParameters validationParameters = new ValidationParameters() + { + ValidAlgorithms = validAlgorithms ?? [], + IssuerSigningKeys = signingKey is not null ? [signingKey] : [], + }; validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation; diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.ValidateTokenAsyncTests.Algorithm.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.ValidateTokenAsyncTests.Algorithm.cs index 5d1a77c4c6..258cda2eb4 100644 --- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.ValidateTokenAsyncTests.Algorithm.cs +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.ValidateTokenAsyncTests.Algorithm.cs @@ -142,12 +142,11 @@ public static TheoryData ValidateTokenAsy static ValidationParameters CreateValidationParameters( SecurityKey? signingKey = null, List? validAlgorithms = null, bool tryAllKeys = false) { - ValidationParameters validationParameters = new ValidationParameters(); - - if (signingKey is not null) - validationParameters.IssuerSigningKeys.Add(signingKey); - - validationParameters.ValidAlgorithms = validAlgorithms; + ValidationParameters validationParameters = new ValidationParameters() + { + ValidAlgorithms = validAlgorithms ?? [], + IssuerSigningKeys = signingKey is not null ? [signingKey] : [], + }; validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation; From 54063b3a309b836872a29e8fa86c10b7437a2025 Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Tue, 17 Dec 2024 16:19:55 +0000 Subject: [PATCH 7/8] Added missing documentation around validation errors (cherry picked from commit 07a105fa0435ea78e526817a5a2236afb30f75da) --- .../Saml/Exceptions/SamlValidationError.cs | 19 +++++++++-- .../Saml2/Exceptions/Saml2ValidationError.cs | 19 +++++++++-- .../Details/AlgorithmValidationError.cs | 23 +++++++++++-- .../Details/AudienceValidationError.cs | 26 +++++++++++++-- .../IssuerSigningKeyValidationError.cs | 31 +++++++++++++++-- .../Results/Details/IssuerValidationError.cs | 25 ++++++++++++-- .../Details/LifetimeValidationError.cs | 25 ++++++++++++-- .../Results/Details/MessageDetail.cs | 5 +++ .../Details/SignatureValidationError.cs | 33 ++++++++++++++++--- .../Details/TokenReplayValidationError.cs | 33 ++++++++++++++++--- .../Details/TokenTypeValidationError.cs | 33 ++++++++++++++++--- .../Results/Details/ValidationError.cs | 27 +++++++++++---- .../CustomValidationErrors.cs | 16 ++++----- 13 files changed, 271 insertions(+), 44 deletions(-) diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Exceptions/SamlValidationError.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Exceptions/SamlValidationError.cs index d7f39f8cc4..0d8aedb89c 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Exceptions/SamlValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Exceptions/SamlValidationError.cs @@ -7,9 +7,20 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens.Saml { + /// + /// Represents a SAML validation error. + /// internal class SamlValidationError : ValidationError { - internal SamlValidationError( + /// + /// Initializes a new instance of the class. + /// + /// contains information about the exception that is used to generate the exception message. + /// is the type of validation failure that occurred. + /// is the type of exception that occurred. + /// is the stack frame where the exception occurred. + /// is the inner exception that occurred. + public SamlValidationError( MessageDetail messageDetail, ValidationFailureType validationFailureType, Type exceptionType, @@ -19,7 +30,11 @@ internal SamlValidationError( { } - internal override Exception GetException() + /// + /// Creates an instance of an using + /// + /// An instance of an Exception. + public override Exception GetException() { if (ExceptionType == typeof(SamlSecurityTokenReadException)) { diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Exceptions/Saml2ValidationError.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Exceptions/Saml2ValidationError.cs index 7425f50679..fe8b874192 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Exceptions/Saml2ValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Exceptions/Saml2ValidationError.cs @@ -7,9 +7,20 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens.Saml2 { + /// + /// Represents a SAML2 validation error. + /// internal class Saml2ValidationError : ValidationError { - internal Saml2ValidationError( + /// + /// Initializes a new instance of the class. + /// + /// contains information about the exception that is used to generate the exception message. + /// is the type of validation failure that occurred. + /// is the type of exception that occurred. + /// is the stack frame where the exception occurred. + /// is the inner exception that occurred. + public Saml2ValidationError( MessageDetail messageDetail, ValidationFailureType validationFailureType, Type exceptionType, @@ -19,7 +30,11 @@ internal Saml2ValidationError( { } - internal override Exception GetException() + /// + /// Creates an instance of an using + /// + /// An instance of an Exception. + public override Exception GetException() { if (ExceptionType == typeof(Saml2SecurityTokenReadException)) { diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AlgorithmValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AlgorithmValidationError.cs index de0dd174d5..ebb962e9f0 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AlgorithmValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AlgorithmValidationError.cs @@ -7,8 +7,20 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens { + /// + /// Represents an algorithm validation error. + /// internal class AlgorithmValidationError : ValidationError { + /// + /// Initializes a new instance of the class. + /// + /// contains information about the exception that is used to generate the exception message. + /// is the type of validation failure that occurred. + /// is the type of exception that occurred. + /// is the stack frame where the exception occurred. + /// is the algorithm that could not be validated. + /// is the inner exception that occurred. public AlgorithmValidationError( MessageDetail messageDetail, ValidationFailureType validationFailureType, @@ -21,7 +33,11 @@ public AlgorithmValidationError( InvalidAlgorithm = invalidAlgorithm; } - internal override Exception GetException() + /// + /// Creates an instance of an using + /// + /// An instance of an Exception. + public override Exception GetException() { if (ExceptionType == typeof(SecurityTokenInvalidAlgorithmException)) { @@ -37,7 +53,10 @@ internal override Exception GetException() return base.GetException(); } - protected string? InvalidAlgorithm { get; } + /// + /// The algorithm that could not be validated. + /// + public string? InvalidAlgorithm { get; } } } #nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AudienceValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AudienceValidationError.cs index 4c6dbd42c3..7cc7401185 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AudienceValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AudienceValidationError.cs @@ -8,8 +8,21 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens { + /// + /// Represents an audience validation error. + /// internal class AudienceValidationError : ValidationError { + /// + /// Initializes a new instance of the class. + /// + /// contains information about the exception that is used to generate the exception message. + /// is the type of validation failure that occurred. + /// is the type of exception that occurred. + /// is the stack frame where the exception occurred. + /// are the audiences that were in the token. + /// are the audiences that were expected. + /// is the inner exception that occurred. public AudienceValidationError( MessageDetail messageDetail, ValidationFailureType validationFailureType, @@ -28,7 +41,7 @@ public AudienceValidationError( /// Creates an instance of an using /// /// An instance of an Exception. - internal override Exception GetException() + public override Exception GetException() { if (ExceptionType == typeof(SecurityTokenInvalidAudienceException)) { @@ -41,8 +54,15 @@ internal override Exception GetException() return base.GetException(ExceptionType, null); } - protected IList? TokenAudiences { get; } - protected IList? ValidAudiences { get; } + /// + /// The audiences that were in the token. + /// + public IList? TokenAudiences { get; } + + /// + /// The audiences that were expected. + /// + public IList? ValidAudiences { get; } } } #nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/IssuerSigningKeyValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/IssuerSigningKeyValidationError.cs index 8f380847ae..3858527620 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/IssuerSigningKeyValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/IssuerSigningKeyValidationError.cs @@ -7,9 +7,21 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens { + /// + /// Represents an issuer signing key validation error. + /// internal class IssuerSigningKeyValidationError : ValidationError { - internal IssuerSigningKeyValidationError( + /// + /// Initializes a new instance of the class. + /// + /// contains information about the exception that is used to generate the exception message. + /// is the type of validation failure that occurred. + /// is the type of exception that occurred. + /// is the stack frame where the exception occurred. + /// is the signing key that could not be validated. + /// is the inner exception that occurred. + public IssuerSigningKeyValidationError( MessageDetail messageDetail, ValidationFailureType validationFailureType, Type exceptionType, @@ -21,7 +33,11 @@ internal IssuerSigningKeyValidationError( InvalidSigningKey = invalidSigningKey; } - internal override Exception GetException() + /// + /// Creates an instance of an using + /// + /// An instance of an Exception. + public override Exception GetException() { if (ExceptionType == typeof(SecurityTokenInvalidSigningKeyException)) { @@ -37,13 +53,22 @@ internal override Exception GetException() return base.GetException(); } - internal static new IssuerSigningKeyValidationError NullParameter(string parameterName, StackFrame stackFrame) => new( + /// + /// Creates a new instance of representing a null parameter. + /// + /// The name of the parameter. + /// The stack frame where the error occurred. + /// A new . + public static new IssuerSigningKeyValidationError NullParameter(string parameterName, StackFrame stackFrame) => new( MessageDetail.NullParameter(parameterName), ValidationFailureType.NullArgument, typeof(SecurityTokenArgumentNullException), stackFrame, null); // InvalidSigningKey + /// + /// The signing key that was found invalid. + /// protected SecurityKey? InvalidSigningKey { get; } } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/IssuerValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/IssuerValidationError.cs index 438681b973..6a93541cd0 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/IssuerValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/IssuerValidationError.cs @@ -7,9 +7,21 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens { + /// + /// Represents an issuer validation error. + /// internal class IssuerValidationError : ValidationError { - internal IssuerValidationError( + /// + /// Initializes a new instance of the class. + /// + /// contains information about the exception that is used to generate the exception message. + /// is the type of validation failure that occurred. + /// is the type of exception that occurred. + /// is the stack frame where the exception occurred. + /// is the issuer that could not be validated. + /// is the inner exception that occurred. + public IssuerValidationError( MessageDetail messageDetail, ValidationFailureType validationFailureType, Type exceptionType, @@ -21,9 +33,16 @@ internal IssuerValidationError( InvalidIssuer = invalidIssuer; } - internal string? InvalidIssuer { get; } + /// + /// The issuer that could not be validated. + /// + public string? InvalidIssuer { get; } - internal override Exception GetException() + /// + /// Creates an instance of an using + /// + /// An instance of an exception. + public override Exception GetException() { if (ExceptionType == typeof(SecurityTokenInvalidIssuerException)) { diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/LifetimeValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/LifetimeValidationError.cs index f193985d81..4ada390f67 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/LifetimeValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/LifetimeValidationError.cs @@ -7,8 +7,21 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens { + /// + /// Represents a lifetime validation error. + /// internal class LifetimeValidationError : ValidationError { + /// + /// Initializes a new instance of the class. + /// + /// contains information about the exception that is used to generate the exception message. + /// is the type of validation failure that occurred. + /// is the type of exception that occurred. + /// is the stack frame where the exception occurred. + /// is the date from which the token is valid. + /// is the date at which the token expires. + /// is the inner exception that occurred. public LifetimeValidationError( MessageDetail messageDetail, ValidationFailureType validationFailureType, @@ -28,7 +41,7 @@ public LifetimeValidationError( /// Creates an instance of an using /// /// An instance of an Exception. - internal override Exception GetException() + public override Exception GetException() { if (ExceptionType == typeof(SecurityTokenNoExpirationException)) { @@ -68,9 +81,15 @@ internal override Exception GetException() return base.GetException(ExceptionType, null); } - protected DateTime? NotBefore { get; } + /// + /// The date from which the token is valid. + /// + public DateTime? NotBefore { get; } - protected DateTime? Expires { get; } + /// + /// The date at which the token expires. + /// + public DateTime? Expires { get; } } } #nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/MessageDetail.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/MessageDetail.cs index 188e3a51b8..b0b71fd296 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/MessageDetail.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/MessageDetail.cs @@ -26,6 +26,11 @@ public MessageDetail(string formatString, params object[] parameters) Parameters = parameters; } + /// + /// Creates a message detail for a null parameter. + /// + /// The name of the parameter. + /// A new . public static MessageDetail NullParameter(string parameterName) => new MessageDetail(LogMessages.IDX10000, LogHelper.MarkAsNonPII(parameterName)); diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/SignatureValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/SignatureValidationError.cs index 78c51069d8..7b4bf700fe 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/SignatureValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/SignatureValidationError.cs @@ -7,8 +7,20 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens { - internal class SignatureValidationError : ValidationError + /// + /// Represents a signature validation error. + /// + public class SignatureValidationError : ValidationError { + /// + /// Initializes a new instance of the class. + /// + /// contains information about the exception that is used to generate the exception message. + /// is the type of validation failure that occurred. + /// is the type of exception that occurred. + /// is the stack frame where the exception occurred. + /// if present, is the inner validation error that caused this signature validation error. + /// is the inner exception that occurred. public SignatureValidationError( MessageDetail messageDetail, ValidationFailureType validationFailureType, @@ -21,7 +33,11 @@ public SignatureValidationError( InnerValidationError = innerValidationError; } - internal override Exception GetException() + /// + /// Creates an instance of an using + /// + /// An instance of an exception. + public override Exception GetException() { var inner = InnerException ?? InnerValidationError?.GetException(); @@ -43,7 +59,13 @@ internal override Exception GetException() return base.GetException(); } - internal static new SignatureValidationError NullParameter( + /// + /// Creates a new instance of representing a null parameter. + /// + /// The name of the parameter. + /// The stack frame where the error occurred. + /// A new . + public static new SignatureValidationError NullParameter( string parameterName, StackFrame stackFrame) => new( MessageDetail.NullParameter(parameterName), ValidationFailureType.NullArgument, @@ -51,7 +73,10 @@ internal override Exception GetException() stackFrame, null); // innerValidationError - protected internal ValidationError? InnerValidationError { get; } + /// + /// The inner validation error that caused this signature validation error. + /// + public ValidationError? InnerValidationError { get; } } } #nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenReplayValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenReplayValidationError.cs index 02d2c53d4a..957fa6e7a1 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenReplayValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenReplayValidationError.cs @@ -7,9 +7,21 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens { + /// + /// Represents a token replay validation error. + /// internal class TokenReplayValidationError : ValidationError { - internal TokenReplayValidationError( + /// + /// Initializes a new instance of the class. + /// + /// contains information about the exception that is used to generate the exception message. + /// is the type of validation failure that occurred. + /// is the type of exception that occurred. + /// is the stack frame where the exception occurred. + /// is the expiration time of the token that failed the validation. + /// is the inner exception that occurred. + public TokenReplayValidationError( MessageDetail messageDetail, ValidationFailureType validationFailureType, Type exceptionType, @@ -21,7 +33,11 @@ internal TokenReplayValidationError( ExpirationTime = expirationTime; } - internal override Exception GetException() + /// + /// Creates an instance of an using + /// + /// An instance of an exception. + public override Exception GetException() { if (ExceptionType == typeof(SecurityTokenReplayDetectedException)) { @@ -41,14 +57,23 @@ internal override Exception GetException() return base.GetException(); } - internal static new TokenReplayValidationError NullParameter(string parameterName, StackFrame stackFrame) => new( + /// + /// Creates a new instance of representing a null parameter. + /// + /// The name of the parameter. + /// The stack frame where the error occurred. + /// A new . + public static new TokenReplayValidationError NullParameter(string parameterName, StackFrame stackFrame) => new( MessageDetail.NullParameter(parameterName), ValidationFailureType.NullArgument, typeof(SecurityTokenArgumentNullException), stackFrame, null); - protected DateTime? ExpirationTime { get; } + /// + /// The expiration time of the token that failed the validation. + /// + public DateTime? ExpirationTime { get; } } } #nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenTypeValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenTypeValidationError.cs index 298abeefce..988041aaee 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenTypeValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenTypeValidationError.cs @@ -7,9 +7,21 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens { + /// + /// Represents a token type validation error. + /// internal class TokenTypeValidationError : ValidationError { - internal TokenTypeValidationError( + /// + /// Initializes a new instance of the class. + /// + /// contains information about the exception that is used to generate the exception message. + /// is the type of validation failure that occurred. + /// is the type of exception that occurred. + /// is the stack frame where the exception occurred. + /// is the token type that could not be validated. + /// is the inner exception that occurred. + public TokenTypeValidationError( MessageDetail messageDetail, ValidationFailureType validationFailureType, Type exceptionType, @@ -21,7 +33,11 @@ internal TokenTypeValidationError( InvalidTokenType = invalidTokenType; } - internal override Exception GetException() + /// + /// Creates an instance of an using + /// + /// An instance of an exception. + public override Exception GetException() { if (ExceptionType == typeof(SecurityTokenInvalidTypeException)) { @@ -37,14 +53,23 @@ internal override Exception GetException() return base.GetException(); } - internal static new TokenTypeValidationError NullParameter(string parameterName, StackFrame stackFrame) => new( + /// + /// Creates a new instance of representing a null parameter. + /// + /// The name of the parameter. + /// The stack frame where the error occurred. + /// A new . + public static new TokenTypeValidationError NullParameter(string parameterName, StackFrame stackFrame) => new( MessageDetail.NullParameter(parameterName), ValidationFailureType.NullArgument, typeof(SecurityTokenArgumentNullException), stackFrame, null); // invalidTokenType - protected string? InvalidTokenType { get; } + /// + /// The token type that could not be validated. + /// + public string? InvalidTokenType { get; } } } #nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs index b4e0a6714f..ad02f05994 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs @@ -27,7 +27,7 @@ internal class ValidationError /// is the type of exception that occurred. /// is the stack frame where the exception occurred. /// is the inner exception that occurred. - internal ValidationError( + internal protected ValidationError( MessageDetail messageDetail, ValidationFailureType validationFailureType, Type exceptionType, @@ -48,7 +48,7 @@ internal ValidationError( /// Creates an instance of an using /// /// An instance of an Exception. - internal virtual Exception GetException() + public virtual Exception GetException() { return GetException(ExceptionType, InnerException); } @@ -184,12 +184,22 @@ internal Exception GetException(Type exceptionType, Exception? innerException) return exception; } - internal void Log(ILogger logger) + /// + /// Logs the validation error. + /// + /// The to be used for logging. + public void Log(ILogger logger) { Logger.TokenValidationFailed(logger, FailureType.Name, MessageDetail.Message); } - internal static ValidationError NullParameter(string parameterName, StackFrame stackFrame) => new( + /// + /// Creates a new instance of representing a null parameter. + /// + /// The name of the parameter. + /// The stack frame where the error occurred. + /// A new . + public static ValidationError NullParameter(string parameterName, StackFrame stackFrame) => new( MessageDetail.NullParameter(parameterName), ValidationFailureType.NullArgument, typeof(SecurityTokenArgumentNullException), @@ -211,6 +221,11 @@ internal void Log(ILogger logger) /// public Exception? InnerException { get; } + /// + /// Gets the message that explains the error. + /// + public string Message => MessageDetail.Message; + /// /// Gets the message details that are used to generate the exception message. /// @@ -240,7 +255,7 @@ public ValidationError AddStackFrame(StackFrame stackFrame) /// 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. - internal ValidationError AddCurrentStackFrame([CallerFilePath] string filePath = "", [CallerLineNumber] int lineNumber = 0, int skipFrames = 1) + public ValidationError AddCurrentStackFrame([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)); @@ -256,7 +271,7 @@ internal ValidationError AddCurrentStackFrame([CallerFilePath] string filePath = /// 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. - internal static StackFrame GetCurrentStackFrame( + public static StackFrame GetCurrentStackFrame( [CallerFilePath] string filePath = "", [CallerLineNumber] int lineNumber = 0, int skipFrames = 1) { // String is allocated, but it goes out of scope immediately after the call diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs index 7571d4f8aa..61ccb96d64 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs @@ -29,7 +29,7 @@ public CustomIssuerValidationError( { } - internal override Exception GetException() + public override Exception GetException() { if (ExceptionType == typeof(CustomSecurityTokenInvalidIssuerException)) { @@ -76,7 +76,7 @@ public CustomAudienceValidationError( { } - internal override Exception GetException() + public override Exception GetException() { if (ExceptionType == typeof(CustomSecurityTokenInvalidAudienceException)) { @@ -126,7 +126,7 @@ public CustomLifetimeValidationError( { } - internal override Exception GetException() + public override Exception GetException() { if (ExceptionType == typeof(CustomSecurityTokenInvalidLifetimeException)) { @@ -176,7 +176,7 @@ public CustomIssuerSigningKeyValidationError( { } - internal override Exception GetException() + public override Exception GetException() { if (ExceptionType == typeof(CustomSecurityTokenInvalidSigningKeyException)) { @@ -221,7 +221,7 @@ public CustomTokenTypeValidationError( : base(messageDetail, validationFailureType, exceptionType, stackFrame, invalidTokenType, innerException) { } - internal override Exception GetException() + public override Exception GetException() { if (ExceptionType == typeof(CustomSecurityTokenInvalidTypeException)) { @@ -266,7 +266,7 @@ public CustomSignatureValidationError( base(messageDetail, validationFailureType, exceptionType, stackFrame, innerValidationError, innerException) { } - internal override Exception GetException() + public override Exception GetException() { if (ExceptionType == typeof(CustomSecurityTokenInvalidSignatureException)) { @@ -312,7 +312,7 @@ public CustomAlgorithmValidationError( : base(messageDetail, validationFailureType, exceptionType, stackFrame, algorithm, innerException) { } - internal override Exception GetException() + public override Exception GetException() { if (ExceptionType == typeof(CustomSecurityTokenInvalidAlgorithmException)) { @@ -359,7 +359,7 @@ public CustomTokenReplayValidationError( { } - internal override Exception GetException() + public override Exception GetException() { if (ExceptionType == typeof(CustomSecurityTokenReplayDetectedException)) { From 5ac80f61f7983b7b888db88b61e872397fe81f82 Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Tue, 17 Dec 2024 16:20:46 +0000 Subject: [PATCH 8/8] Added CLSCompliant flag to Log methods to address the build issue until the ILogger compliance can be resolved. (cherry picked from commit af561ef0da5c26a26a8df91904d8385d4e507fcf) --- .../Validation/Results/Details/ValidationError.cs | 1 + .../Validation/Results/ValidatedToken.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs index ad02f05994..9bdacb9367 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs @@ -188,6 +188,7 @@ internal Exception GetException(Type exceptionType, Exception? innerException) /// Logs the validation error. /// /// The to be used for logging. + [CLSCompliant(false)] public void Log(ILogger logger) { Logger.TokenValidationFailed(logger, FailureType.Name, MessageDetail.Message); diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs index ba4fe95fa4..39121e24af 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs @@ -28,6 +28,7 @@ internal class ValidatedToken( /// /// Logs the validation result. /// + [CLSCompliant(false)] public void Log(ILogger logger) => Logger.TokenValidationSucceeded( logger, ValidatedAudience ?? "none",