From cb2966ea2b0a0adae3286de9d42c39ba703899ac Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Wed, 26 Jun 2024 13:57:22 +0100 Subject: [PATCH 1/5] Split remaining validators into partial classes --- .../Validation/Validators.Algorithm.cs | 46 +++ .../Validators.IssuerSecurityKey.cs | 108 ++++++++ .../Validation/Validators.TokenReplay.cs | 82 ++++++ .../Validation/Validators.TokenType.cs | 60 ++++ .../Validators.cs | 262 ------------------ 5 files changed, 296 insertions(+), 262 deletions(-) create mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs create mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSecurityKey.cs create mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs create mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs delete mode 100644 src/Microsoft.IdentityModel.Tokens/Validators.cs diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs new file mode 100644 index 0000000000..7b03368139 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Linq; +using Microsoft.IdentityModel.Logging; + +namespace Microsoft.IdentityModel.Tokens +{ + public static partial class Validators + { + /// + /// Validates if a given algorithm for a is valid. + /// + /// The algorithm to be validated. + /// The that signed the . + /// The being validated. + /// required for validation. + public static void ValidateAlgorithm(string algorithm, SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters) + { + if (validationParameters == null) + throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + + if (validationParameters.AlgorithmValidator != null) + { + if (!validationParameters.AlgorithmValidator(algorithm, securityKey, securityToken, validationParameters)) + { + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAlgorithmException(LogHelper.FormatInvariant(LogMessages.IDX10697, LogHelper.MarkAsNonPII(algorithm), securityKey)) + { + InvalidAlgorithm = algorithm, + }); + } + + return; + } + + if (validationParameters.ValidAlgorithms != null && validationParameters.ValidAlgorithms.Any() && !validationParameters.ValidAlgorithms.Contains(algorithm, StringComparer.Ordinal)) + { + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAlgorithmException(LogHelper.FormatInvariant(LogMessages.IDX10696, LogHelper.MarkAsNonPII(algorithm))) + { + InvalidAlgorithm = algorithm, + }); + } + } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSecurityKey.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSecurityKey.cs new file mode 100644 index 0000000000..1a50cb9d45 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSecurityKey.cs @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Security.Cryptography.X509Certificates; +using Microsoft.IdentityModel.Abstractions; +using Microsoft.IdentityModel.Logging; + +namespace Microsoft.IdentityModel.Tokens +{ + public static partial class Validators + { + /// + /// Validates the that signed a . + /// + /// The that signed the . + /// The being validated. + /// required for validation. + /// if 'securityKey' is null and ValidateIssuerSigningKey is true. + /// if 'securityToken' is null and ValidateIssuerSigningKey is true. + /// if 'validationParameters' is null. + public static void ValidateIssuerSecurityKey(SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters) + { + ValidateIssuerSecurityKey(securityKey, securityToken, validationParameters, null); + } + + /// + /// Validates the that signed a . + /// + /// The that signed the . + /// The being validated. + /// required for validation. + /// The required for issuer and signing key validation. + /// if 'securityKey' is null and ValidateIssuerSigningKey is true. + /// if 'securityToken' is null and ValidateIssuerSigningKey is true. + /// if 'validationParameters' is null. + internal static void ValidateIssuerSecurityKey(SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + { + if (validationParameters == null) + throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + + if (validationParameters.IssuerSigningKeyValidatorUsingConfiguration != null) + { + if (!validationParameters.IssuerSigningKeyValidatorUsingConfiguration(securityKey, securityToken, validationParameters, configuration)) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10232, securityKey)) { SigningKey = securityKey }); + + return; + } + + if (validationParameters.IssuerSigningKeyValidator != null) + { + if (!validationParameters.IssuerSigningKeyValidator(securityKey, securityToken, validationParameters)) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10232, securityKey)) { SigningKey = securityKey }); + + return; + } + + if (!validationParameters.ValidateIssuerSigningKey) + { + LogHelper.LogVerbose(LogMessages.IDX10237); + return; + } + + if (!validationParameters.RequireSignedTokens && securityKey == null) + { + LogHelper.LogInformation(LogMessages.IDX10252); + return; + } + else if (securityKey == null) + { + throw LogHelper.LogExceptionMessage(new ArgumentNullException(nameof(securityKey), LogMessages.IDX10253)); + } + + if (securityToken == null) + throw LogHelper.LogArgumentNullException(nameof(securityToken)); + + ValidateIssuerSigningKeyLifeTime(securityKey, validationParameters); + } + + /// + /// Given a signing key, when it's derived from a certificate, validates that the certificate is already active and non-expired + /// + /// The that signed the . + /// The that are used to validate the token. + internal static void ValidateIssuerSigningKeyLifeTime(SecurityKey securityKey, TokenValidationParameters validationParameters) + { + X509SecurityKey x509SecurityKey = securityKey as X509SecurityKey; + if (x509SecurityKey?.Certificate is X509Certificate2 cert) + { + DateTime utcNow = DateTime.UtcNow; + var notBeforeUtc = cert.NotBefore.ToUniversalTime(); + var notAfterUtc = cert.NotAfter.ToUniversalTime(); + + if (notBeforeUtc > DateTimeUtil.Add(utcNow, validationParameters.ClockSkew)) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10248, LogHelper.MarkAsNonPII(notBeforeUtc), LogHelper.MarkAsNonPII(utcNow)))); + + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10250, LogHelper.MarkAsNonPII(notBeforeUtc), LogHelper.MarkAsNonPII(utcNow)); + + if (notAfterUtc < DateTimeUtil.Add(utcNow, validationParameters.ClockSkew.Negate())) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10249, LogHelper.MarkAsNonPII(notAfterUtc), LogHelper.MarkAsNonPII(utcNow)))); + + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10251, LogHelper.MarkAsNonPII(notAfterUtc), LogHelper.MarkAsNonPII(utcNow)); + } + } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs new file mode 100644 index 0000000000..82d761c314 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using Microsoft.IdentityModel.Abstractions; +using Microsoft.IdentityModel.Logging; + +namespace Microsoft.IdentityModel.Tokens +{ + public static partial class Validators + { + /// + /// Validates if a token has been replayed. + /// + /// When does the security token expire. + /// The being validated. + /// required for validation. + /// 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 . + public static void ValidateTokenReplay(DateTime? expirationTime, string securityToken, TokenValidationParameters validationParameters) + { + if (string.IsNullOrWhiteSpace(securityToken)) + throw LogHelper.LogArgumentNullException(nameof(securityToken)); + + if (validationParameters == null) + throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + + if (validationParameters.TokenReplayValidator != null) + { + if (!validationParameters.TokenReplayValidator(expirationTime, securityToken, validationParameters)) + throw LogHelper.LogExceptionMessage(new SecurityTokenReplayDetectedException( + LogHelper.FormatInvariant( + LogMessages.IDX10228, + LogHelper.MarkAsUnsafeSecurityArtifact(securityToken, t => t.ToString())))); + return; + } + + if (!validationParameters.ValidateTokenReplay) + { + LogHelper.LogVerbose(LogMessages.IDX10246); + return; + } + + // check if token if replay cache is set, then there must be an expiration time. + if (validationParameters.TokenReplayCache != null) + { + if (!expirationTime.HasValue) + throw LogHelper.LogExceptionMessage(new SecurityTokenNoExpirationException(LogHelper.FormatInvariant(LogMessages.IDX10227, securityToken))); + + if (validationParameters.TokenReplayCache.TryFind(securityToken)) + throw LogHelper.LogExceptionMessage(new SecurityTokenReplayDetectedException(LogHelper.FormatInvariant(LogMessages.IDX10228, securityToken))); + + if (!validationParameters.TokenReplayCache.TryAdd(securityToken, expirationTime.Value)) + throw LogHelper.LogExceptionMessage(new SecurityTokenReplayAddFailedException(LogHelper.FormatInvariant(LogMessages.IDX10229, securityToken))); + } + + // if it reaches here, that means no token replay is detected. + LogHelper.LogInformation(LogMessages.IDX10240); + } + + /// + /// Validates if a token has been replayed. + /// + /// The being validated. + /// When does the security token expire. + /// required for validation. + /// 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 . + public static void ValidateTokenReplay(string securityToken, DateTime? expirationTime, TokenValidationParameters validationParameters) + { + ValidateTokenReplay(expirationTime, securityToken, validationParameters); + } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs new file mode 100644 index 0000000000..42fc27893c --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs @@ -0,0 +1,60 @@ +using System; +using System.Linq; +using Microsoft.IdentityModel.Abstractions; +using Microsoft.IdentityModel.Logging; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.IdentityModel.Tokens +{ + public static partial class Validators + { + /// + /// Validates the type of the token. + /// + /// 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. + /// If is null. + /// If is null. + /// If is null or whitespace and is not null. + /// If failed to match . + /// An EXACT match is required. (case sensitive) is used for comparing against . + /// The actual token type, that may be the same as or a different value if the token type was resolved from a different location. + public static string ValidateTokenType(string type, SecurityToken securityToken, TokenValidationParameters validationParameters) + { + if (securityToken == null) + throw new ArgumentNullException(nameof(securityToken)); + + if (validationParameters == null) + throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + + if (validationParameters.TypeValidator == null && (validationParameters.ValidTypes == null || !validationParameters.ValidTypes.Any())) + { + LogHelper.LogVerbose(LogMessages.IDX10255); + return type; + } + + if (validationParameters.TypeValidator != null) + return validationParameters.TypeValidator(type, securityToken, validationParameters); + + // Note: don't throw an exception for a null or empty token type when a user-defined delegate is set + // to allow it to extract the actual token type from a different location (e.g from the claims). + if (string.IsNullOrEmpty(type)) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidTypeException(LogMessages.IDX10256) { InvalidType = null }); + + if (!validationParameters.ValidTypes.Contains(type, StringComparer.Ordinal)) + { + throw LogHelper.LogExceptionMessage( + new SecurityTokenInvalidTypeException(LogHelper.FormatInvariant(LogMessages.IDX10257, LogHelper.MarkAsNonPII(type), Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidTypes))) + { InvalidType = type }); + } + + // if it reaches here, token type was succcessfully validated. + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10258, LogHelper.MarkAsNonPII(type)); + + return type; + } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Validators.cs b/src/Microsoft.IdentityModel.Tokens/Validators.cs deleted file mode 100644 index 000ba4c617..0000000000 --- a/src/Microsoft.IdentityModel.Tokens/Validators.cs +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using Microsoft.IdentityModel.Abstractions; -using Microsoft.IdentityModel.Logging; - -namespace Microsoft.IdentityModel.Tokens -{ - /// - /// AudienceValidator - /// - public static partial class Validators - { - /// - /// Validates if a given algorithm for a is valid. - /// - /// The algorithm to be validated. - /// The that signed the . - /// The being validated. - /// required for validation. - public static void ValidateAlgorithm(string algorithm, SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters) - { - if (validationParameters == null) - throw LogHelper.LogArgumentNullException(nameof(validationParameters)); - - if (validationParameters.AlgorithmValidator != null) - { - if (!validationParameters.AlgorithmValidator(algorithm, securityKey, securityToken, validationParameters)) - { - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAlgorithmException(LogHelper.FormatInvariant(LogMessages.IDX10697, LogHelper.MarkAsNonPII(algorithm), securityKey)) - { - InvalidAlgorithm = algorithm, - }); - } - - return; - } - - if (validationParameters.ValidAlgorithms != null && validationParameters.ValidAlgorithms.Any() && !validationParameters.ValidAlgorithms.Contains(algorithm, StringComparer.Ordinal)) - { - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAlgorithmException(LogHelper.FormatInvariant(LogMessages.IDX10696, LogHelper.MarkAsNonPII(algorithm))) - { - InvalidAlgorithm = algorithm, - }); - } - } - - /// - /// Validates the that signed a . - /// - /// The that signed the . - /// The being validated. - /// required for validation. - /// if 'securityKey' is null and ValidateIssuerSigningKey is true. - /// if 'securityToken' is null and ValidateIssuerSigningKey is true. - /// if 'validationParameters' is null. - public static void ValidateIssuerSecurityKey(SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters) - { - ValidateIssuerSecurityKey(securityKey, securityToken, validationParameters, null); - } - - /// - /// Validates the that signed a . - /// - /// The that signed the . - /// The being validated. - /// required for validation. - /// The required for issuer and signing key validation. - /// if 'securityKey' is null and ValidateIssuerSigningKey is true. - /// if 'securityToken' is null and ValidateIssuerSigningKey is true. - /// if 'validationParameters' is null. - internal static void ValidateIssuerSecurityKey(SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) - { - if (validationParameters == null) - throw LogHelper.LogArgumentNullException(nameof(validationParameters)); - - if (validationParameters.IssuerSigningKeyValidatorUsingConfiguration != null) - { - if (!validationParameters.IssuerSigningKeyValidatorUsingConfiguration(securityKey, securityToken, validationParameters, configuration)) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10232, securityKey)) { SigningKey = securityKey }); - - return; - } - - if (validationParameters.IssuerSigningKeyValidator != null) - { - if (!validationParameters.IssuerSigningKeyValidator(securityKey, securityToken, validationParameters)) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10232, securityKey)) { SigningKey = securityKey }); - - return; - } - - if (!validationParameters.ValidateIssuerSigningKey) - { - LogHelper.LogVerbose(LogMessages.IDX10237); - return; - } - - if (!validationParameters.RequireSignedTokens && securityKey == null) - { - LogHelper.LogInformation(LogMessages.IDX10252); - return; - } - else if (securityKey == null) - { - throw LogHelper.LogExceptionMessage(new ArgumentNullException(nameof(securityKey), LogMessages.IDX10253)); - } - - if (securityToken == null) - throw LogHelper.LogArgumentNullException(nameof(securityToken)); - - ValidateIssuerSigningKeyLifeTime(securityKey, validationParameters); - } - - /// - /// Given a signing key, when it's derived from a certificate, validates that the certificate is already active and non-expired - /// - /// The that signed the . - /// The that are used to validate the token. - internal static void ValidateIssuerSigningKeyLifeTime(SecurityKey securityKey, TokenValidationParameters validationParameters) - { - X509SecurityKey x509SecurityKey = securityKey as X509SecurityKey; - if (x509SecurityKey?.Certificate is X509Certificate2 cert) - { - DateTime utcNow = DateTime.UtcNow; - var notBeforeUtc = cert.NotBefore.ToUniversalTime(); - var notAfterUtc = cert.NotAfter.ToUniversalTime(); - - if (notBeforeUtc > DateTimeUtil.Add(utcNow, validationParameters.ClockSkew)) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10248, LogHelper.MarkAsNonPII(notBeforeUtc), LogHelper.MarkAsNonPII(utcNow)))); - - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10250, LogHelper.MarkAsNonPII(notBeforeUtc), LogHelper.MarkAsNonPII(utcNow)); - - if (notAfterUtc < DateTimeUtil.Add(utcNow, validationParameters.ClockSkew.Negate())) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10249, LogHelper.MarkAsNonPII(notAfterUtc), LogHelper.MarkAsNonPII(utcNow)))); - - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10251, LogHelper.MarkAsNonPII(notAfterUtc), LogHelper.MarkAsNonPII(utcNow)); - } - } - - /// - /// Validates if a token has been replayed. - /// - /// When does the security token expire. - /// The being validated. - /// required for validation. - /// 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 . - public static void ValidateTokenReplay(DateTime? expirationTime, string securityToken, TokenValidationParameters validationParameters) - { - if (string.IsNullOrWhiteSpace(securityToken)) - throw LogHelper.LogArgumentNullException(nameof(securityToken)); - - if (validationParameters == null) - throw LogHelper.LogArgumentNullException(nameof(validationParameters)); - - if (validationParameters.TokenReplayValidator != null) - { - if (!validationParameters.TokenReplayValidator(expirationTime, securityToken, validationParameters)) - throw LogHelper.LogExceptionMessage(new SecurityTokenReplayDetectedException( - LogHelper.FormatInvariant( - LogMessages.IDX10228, - LogHelper.MarkAsUnsafeSecurityArtifact(securityToken, t => t.ToString())))); - return; - } - - if (!validationParameters.ValidateTokenReplay) - { - LogHelper.LogVerbose(LogMessages.IDX10246); - return; - } - - // check if token if replay cache is set, then there must be an expiration time. - if (validationParameters.TokenReplayCache != null) - { - if (!expirationTime.HasValue) - throw LogHelper.LogExceptionMessage(new SecurityTokenNoExpirationException(LogHelper.FormatInvariant(LogMessages.IDX10227, securityToken))); - - if (validationParameters.TokenReplayCache.TryFind(securityToken)) - throw LogHelper.LogExceptionMessage(new SecurityTokenReplayDetectedException(LogHelper.FormatInvariant(LogMessages.IDX10228, securityToken))); - - if (!validationParameters.TokenReplayCache.TryAdd(securityToken, expirationTime.Value)) - throw LogHelper.LogExceptionMessage(new SecurityTokenReplayAddFailedException(LogHelper.FormatInvariant(LogMessages.IDX10229, securityToken))); - } - - // if it reaches here, that means no token replay is detected. - LogHelper.LogInformation(LogMessages.IDX10240); - } - - /// - /// Validates if a token has been replayed. - /// - /// The being validated. - /// When does the security token expire. - /// required for validation. - /// 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 . - public static void ValidateTokenReplay(string securityToken, DateTime? expirationTime, TokenValidationParameters validationParameters) - { - ValidateTokenReplay(expirationTime, securityToken, validationParameters); - } - - /// - /// Validates the type of the token. - /// - /// 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. - /// If is null. - /// If is null. - /// If is null or whitespace and is not null. - /// If failed to match . - /// An EXACT match is required. (case sensitive) is used for comparing against . - /// The actual token type, that may be the same as or a different value if the token type was resolved from a different location. - public static string ValidateTokenType(string type, SecurityToken securityToken, TokenValidationParameters validationParameters) - { - if (securityToken == null) - throw new ArgumentNullException(nameof(securityToken)); - - if (validationParameters == null) - throw LogHelper.LogArgumentNullException(nameof(validationParameters)); - - if (validationParameters.TypeValidator == null && (validationParameters.ValidTypes == null || !validationParameters.ValidTypes.Any())) - { - LogHelper.LogVerbose(LogMessages.IDX10255); - return type; - } - - if (validationParameters.TypeValidator != null) - return validationParameters.TypeValidator(type, securityToken, validationParameters); - - // Note: don't throw an exception for a null or empty token type when a user-defined delegate is set - // to allow it to extract the actual token type from a different location (e.g from the claims). - if (string.IsNullOrEmpty(type)) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidTypeException(LogMessages.IDX10256) { InvalidType = null }); - - if (!validationParameters.ValidTypes.Contains(type, StringComparer.Ordinal)) - { - throw LogHelper.LogExceptionMessage( - new SecurityTokenInvalidTypeException(LogHelper.FormatInvariant(LogMessages.IDX10257, LogHelper.MarkAsNonPII(type), Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidTypes))) - { InvalidType = type }); - } - - // if it reaches here, token type was succcessfully validated. - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10258, LogHelper.MarkAsNonPII(type)); - - return type; - } - } -} From 3ce64bbad6938ea73bea33f6240261066fc0f0e1 Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Wed, 26 Jun 2024 15:39:56 +0100 Subject: [PATCH 2/5] Moved header back to top --- .../Validation/Validators.TokenType.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs index 42fc27893c..6fd744fd9d 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs @@ -1,9 +1,10 @@ -using System; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; using System.Linq; using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. namespace Microsoft.IdentityModel.Tokens { From 32e3d5e36c408ac758246c2e562f2fbd1081c5df Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Thu, 27 Jun 2024 21:01:38 +0100 Subject: [PATCH 3/5] Added SigningKeyValidationResult --- .../Validation/SigningKeyValidationResult.cs | 69 +++++++++++++++++++ .../IdentityComparer.cs | 66 ++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 src/Microsoft.IdentityModel.Tokens/Validation/SigningKeyValidationResult.cs diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/SigningKeyValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/SigningKeyValidationResult.cs new file mode 100644 index 0000000000..6c3905c1cf --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/SigningKeyValidationResult.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +#nullable enable +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Contains the result of validating the used to sign a . + /// The contains a collection of for each step in the token validation. + /// + internal class SigningKeyValidationResult : ValidationResult + { + private Exception? _exception; + + /// + /// Creates an instance of + /// + /// is the security key that was validated successfully. + public SigningKeyValidationResult(SecurityKey? signingKey) + : base(ValidationFailureType.ValidationSucceeded) + { + SigningKey = signingKey; + IsValid = true; + } + + /// + /// Creates an instance of + /// + /// is the security key that was intended to be validated. + /// is the that occurred during validation. + /// is the that occurred during validation. + public SigningKeyValidationResult(SecurityKey? signingKey, ValidationFailureType validationFailure, ExceptionDetail exceptionDetail) + : base(validationFailure, exceptionDetail) + { + SigningKey = signingKey; + IsValid = false; + } + + /// + /// Gets the that occurred during validation. + /// + public override Exception? Exception + { + get + { + if (_exception != null || ExceptionDetail == null) + return _exception; + + HasValidOrExceptionWasRead = true; + _exception = ExceptionDetail.GetException(); + if (_exception is SecurityTokenInvalidSigningKeyException securityTokenInvalidSigningKeyException) + { + securityTokenInvalidSigningKeyException.SigningKey = SigningKey; + securityTokenInvalidSigningKeyException.ExceptionDetail = ExceptionDetail; + securityTokenInvalidSigningKeyException.Source = "Microsoft.IdentityModel.Tokens"; + } + + return _exception; + } + } + + /// + /// Gets the security key that was validated or intended to be validated. + /// + public SecurityKey? SigningKey { get; } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs index c8caa5156d..5d35510816 100644 --- a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs +++ b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs @@ -673,6 +673,72 @@ internal static bool AreAudienceValidationResultsEqual( return context.Merge(localContext); } + public static bool AreSigningKeyValidationResultsEqual(object object1, object object2, CompareContext context) + { + var localContext = new CompareContext(context); + if (!ContinueCheckingEquality(object1, object2, context)) + return context.Merge(localContext); + + return AreSigningKeyValidationResultsEqual( + object1 as SigningKeyValidationResult, + object2 as SigningKeyValidationResult, + "SigningKeyValidationResult1", + "SigningKeyValidationResult2", + null, + context); + } + + internal static bool AreSigningKeyValidationResultsEqual( + SigningKeyValidationResult signingKeyValidationResult1, + SigningKeyValidationResult signingKeyValidationResult2, + string name1, + string name2, + string stackPrefix, + CompareContext context) + { + var localContext = new CompareContext(context); + + AreSecurityKeysEqual(signingKeyValidationResult1.SigningKey, signingKeyValidationResult2.SigningKey, localContext); + + if (!ContinueCheckingEquality(signingKeyValidationResult1, signingKeyValidationResult2, localContext)) + return context.Merge(localContext); + + if (signingKeyValidationResult1.IsValid != signingKeyValidationResult2.IsValid) + localContext.Diffs.Add($"SigningKeyValidationResult1.IsValid: {signingKeyValidationResult2.IsValid} != SigningKeyValidationResult2.IsValid: {signingKeyValidationResult2.IsValid}"); + + if (signingKeyValidationResult1.ValidationFailureType != signingKeyValidationResult2.ValidationFailureType) + localContext.Diffs.Add($"SigningKeyValidationResult1.ValidationFailureType: {signingKeyValidationResult1.ValidationFailureType} != SigningKeyValidationResult2.ValidationFailureType: {signingKeyValidationResult2.ValidationFailureType}"); + + // true => both are not null. + if (ContinueCheckingEquality(signingKeyValidationResult1.Exception, signingKeyValidationResult2.Exception, localContext)) + { + AreStringsEqual( + signingKeyValidationResult1.Exception.Message, + signingKeyValidationResult2.Exception.Message, + $"({name1})signingKeyValidationResult1.Exception.Message", + $"({name2})signingKeyValidationResult2.Exception.Message", + localContext); + + AreStringsEqual( + signingKeyValidationResult1.Exception.Source, + signingKeyValidationResult2.Exception.Source, + $"({name1})signingKeyValidationResult1.Exception.Source", + $"({name2})signingKeyValidationResult2.Exception.Source", + localContext); + + if (!string.IsNullOrEmpty(stackPrefix)) + AreStringPrefixesEqual( + signingKeyValidationResult1.Exception.StackTrace.Trim(), + signingKeyValidationResult2.Exception.StackTrace.Trim(), + $"({name1})signingKeyValidationResult1.Exception.StackTrace", + $"({name2})signingKeyValidationResult2.Exception.StackTrace", + stackPrefix.Trim(), + localContext); + } + + return context.Merge(localContext); + } + public static bool AreJArraysEqual(object object1, object object2, CompareContext context) { var localContext = new CompareContext(context); From 0a8592047acc8c3739826e43754b8ceabf06f58e Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Thu, 27 Jun 2024 21:02:41 +0100 Subject: [PATCH 4/5] Added validation failure type for signing key, removed exception throwing from ValidateIssuerSigningKey, and added tests --- .../Validation/ValidationFailureType.cs | 6 + .../Validators.IssuerSecurityKey.cs | 212 ++++++++++- .../SigningKeyValidationResultTests.cs | 333 ++++++++++++++++++ 3 files changed, 548 insertions(+), 3 deletions(-) create mode 100644 test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs index 4c561994fb..c5a6782ea8 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs @@ -39,6 +39,12 @@ private class IssuerValidationFailure : ValidationFailureType { internal IssuerV public static readonly ValidationFailureType AudienceValidationFailed = new AudienceValidationFailure("AudienceValidationFailed"); private class AudienceValidationFailure : ValidationFailureType { internal AudienceValidationFailure(string name) : base(name) { } } + /// + /// Defines a type that represents that signing key validation failed. + /// + public static readonly ValidationFailureType SigningKeyValidationFailed = new SigningKeyValidationFailure("SigningKeyValidationFailed"); + private class SigningKeyValidationFailure : ValidationFailureType { internal SigningKeyValidationFailure(string name) : base(name) { } } + /// /// Defines a type that represents that no evaluation has taken place. /// diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSecurityKey.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSecurityKey.cs index 1a50cb9d45..e5595a178a 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSecurityKey.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSecurityKey.cs @@ -2,12 +2,37 @@ // Licensed under the MIT License. using System; +using System.Diagnostics; using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; +#nullable enable namespace Microsoft.IdentityModel.Tokens { + /// + /// Definition for delegate that will validate the that signed a . + /// + /// The security key to validate. + /// The that is being validated. + /// required for validation. + /// + /// + /// A that contains the results of validating the issuer. + /// This delegate is not expected to throw. + internal delegate Task IssuerSecurityKeyValidationDelegate( + SecurityKey signingKey, + SecurityToken securityToken, + TokenValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken); + + /// + /// SigningKeyValidation + /// + public static partial class Validators { /// @@ -21,7 +46,7 @@ public static partial class Validators /// if 'validationParameters' is null. public static void ValidateIssuerSecurityKey(SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters) { - ValidateIssuerSecurityKey(securityKey, securityToken, validationParameters, null); + ValidateIssuerSecurityKey(securityKey, securityToken, validationParameters, configuration: null); } /// @@ -34,7 +59,7 @@ public static void ValidateIssuerSecurityKey(SecurityKey securityKey, SecurityTo /// if 'securityKey' is null and ValidateIssuerSigningKey is true. /// if 'securityToken' is null and ValidateIssuerSigningKey is true. /// if 'validationParameters' is null. - internal static void ValidateIssuerSecurityKey(SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + internal static void ValidateIssuerSecurityKey(SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration? configuration) { if (validationParameters == null) throw LogHelper.LogArgumentNullException(nameof(validationParameters)); @@ -84,7 +109,7 @@ internal static void ValidateIssuerSecurityKey(SecurityKey securityKey, Security /// The that are used to validate the token. internal static void ValidateIssuerSigningKeyLifeTime(SecurityKey securityKey, TokenValidationParameters validationParameters) { - X509SecurityKey x509SecurityKey = securityKey as X509SecurityKey; + X509SecurityKey? x509SecurityKey = securityKey as X509SecurityKey; if (x509SecurityKey?.Certificate is X509Certificate2 cert) { DateTime utcNow = DateTime.UtcNow; @@ -104,5 +129,186 @@ internal static void ValidateIssuerSigningKeyLifeTime(SecurityKey securityKey, T LogHelper.LogInformation(LogMessages.IDX10251, LogHelper.MarkAsNonPII(notAfterUtc), LogHelper.MarkAsNonPII(utcNow)); } } + + /// + /// Validates the that signed a . + /// + /// The that signed the . + /// The being validated. + /// required for validation. + /// + /// if 'securityKey' is null and ValidateIssuerSigningKey is true. + /// if 'securityToken' is null and ValidateIssuerSigningKey is true. + /// if 'validationParameters' is null. + internal static SigningKeyValidationResult ValidateIssuerSecurityKey(SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters, CallContext callContext) + { + return ValidateIssuerSecurityKey(securityKey, securityToken, validationParameters, null, callContext); + } + + /// + /// Validates the that signed a . + /// + /// The that signed the . + /// The being validated. + /// required for validation. + /// The required for issuer and signing key validation. + /// + /// if 'securityKey' is null and ValidateIssuerSigningKey is true. + /// if 'securityToken' is null and ValidateIssuerSigningKey is true. + /// if 'validationParameters' is null. + internal static SigningKeyValidationResult ValidateIssuerSecurityKey(SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration? configuration, CallContext callContext) + { + if (validationParameters == null) + return new SigningKeyValidationResult( + securityKey, + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII(nameof(validationParameters))), + typeof(ArgumentNullException), + new StackFrame(true))); + + if (validationParameters.IssuerSigningKeyValidatorUsingConfiguration != null) + { + return ValidateSigningKeyUsingDelegateAndConfiguration(securityKey, securityToken, validationParameters, configuration); + } + + if (validationParameters.IssuerSigningKeyValidator != null) + { + return ValidateSigningKeyUsingDelegateAndConfiguration(securityKey, securityToken, validationParameters, null); + } + + if (!validationParameters.ValidateIssuerSigningKey) + { + LogHelper.LogVerbose(LogMessages.IDX10237); + return new SigningKeyValidationResult(securityKey); + } + + if (!validationParameters.RequireSignedTokens && securityKey == null) + { + LogHelper.LogInformation(LogMessages.IDX10252); + return new SigningKeyValidationResult(securityKey); + } + else if (securityKey == null) + { + return new SigningKeyValidationResult( + securityKey, + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10253, + LogHelper.MarkAsNonPII(nameof(securityKey))), + typeof(ArgumentNullException), + new StackFrame(true))); + } + + if (securityToken == null) + return new SigningKeyValidationResult( + securityKey, + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII(nameof(securityToken))), + typeof(ArgumentNullException), + new StackFrame(true))); + + return ValidateIssuerSigningKeyLifeTime(securityKey, validationParameters, callContext); + } + + /// + /// Given a signing key, when it's derived from a certificate, validates that the certificate is already active and non-expired + /// + /// The that signed the . + /// The that are used to validate the token. + /// +#pragma warning disable CA1801 // Review unused parameters + internal static SigningKeyValidationResult ValidateIssuerSigningKeyLifeTime(SecurityKey securityKey, TokenValidationParameters validationParameters, CallContext callContext) +#pragma warning restore CA1801 // Review unused parameters + { + X509SecurityKey? x509SecurityKey = securityKey as X509SecurityKey; + if (x509SecurityKey?.Certificate is X509Certificate2 cert) + { + DateTime utcNow = DateTime.UtcNow; + var notBeforeUtc = cert.NotBefore.ToUniversalTime(); + var notAfterUtc = cert.NotAfter.ToUniversalTime(); + + if (notBeforeUtc > DateTimeUtil.Add(utcNow, validationParameters.ClockSkew)) + return new SigningKeyValidationResult( + securityKey, + ValidationFailureType.SigningKeyValidationFailed, + new ExceptionDetail( + new MessageDetail( + LogHelper.FormatInvariant( + LogMessages.IDX10248, + LogHelper.MarkAsNonPII(notBeforeUtc), + LogHelper.MarkAsNonPII(utcNow))), + typeof(SecurityTokenInvalidSigningKeyException), + new StackFrame(true))); + + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10250, LogHelper.MarkAsNonPII(notBeforeUtc), LogHelper.MarkAsNonPII(utcNow)); + + if (notAfterUtc < DateTimeUtil.Add(utcNow, validationParameters.ClockSkew.Negate())) + return new SigningKeyValidationResult( + securityKey, + ValidationFailureType.SigningKeyValidationFailed, + new ExceptionDetail( + new MessageDetail( + LogHelper.FormatInvariant( + LogMessages.IDX10249, + LogHelper.MarkAsNonPII(notAfterUtc), + LogHelper.MarkAsNonPII(utcNow))), + typeof(SecurityTokenInvalidSigningKeyException), + new StackFrame(true))); + + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10251, LogHelper.MarkAsNonPII(notAfterUtc), LogHelper.MarkAsNonPII(utcNow)); + } + + return new SigningKeyValidationResult(securityKey); + } + + private static SigningKeyValidationResult ValidateSigningKeyUsingDelegateAndConfiguration(SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration? configuration) + { + try + { + bool success; + if (configuration != null) + success = validationParameters.IssuerSigningKeyValidatorUsingConfiguration(securityKey, securityToken, validationParameters, configuration); + else + success = validationParameters.IssuerSigningKeyValidator(securityKey, securityToken, validationParameters); + + if (!success) + return new SigningKeyValidationResult( + securityKey, + ValidationFailureType.SigningKeyValidationFailed, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10232, + securityKey), + typeof(SecurityTokenInvalidSigningKeyException), + new StackFrame(true))); + + return new SigningKeyValidationResult(securityKey); + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception exception) +#pragma warning restore CA1031 // Do not catch general exception types + { + return new SigningKeyValidationResult( + securityKey, + ValidationFailureType.SigningKeyValidationFailed, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10232, + securityKey), + exception.GetType(), + new StackFrame(true), + exception)); + } + } } } +#nullable restore diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs new file mode 100644 index 0000000000..fbba587543 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs @@ -0,0 +1,333 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.IdentityModel.Tokens.Jwt; +using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using Microsoft.IdentityModel.TestUtils; +using Xunit; + +namespace Microsoft.IdentityModel.Tokens.Validation.Tests +{ + public class SigningKeyValidationResultTests + { + [Theory, MemberData(nameof(SigningKeyValidationTestCases), DisableDiscoveryEnumeration = true)] + public void SecurityKey(SigningKeyValidationTheoryData theoryData) + { + CompareContext context = TestUtilities.WriteHeader($"{this}.SigningKeyValidationResultTests", theoryData); + + SigningKeyValidationResult signingKeyValidationResult = Validators.ValidateIssuerSecurityKey( + theoryData.SecurityKey, + theoryData.SecurityToken, + theoryData.ValidationParameters, + theoryData.BaseConfiguration, + new CallContext()); + + if (signingKeyValidationResult.Exception != null) + theoryData.ExpectedException.ProcessException(signingKeyValidationResult.Exception); + else + theoryData.ExpectedException.ProcessNoException(); + + IdentityComparer.AreSigningKeyValidationResultsEqual( + signingKeyValidationResult, + theoryData.SigningKeyValidationResult, + context); + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData SigningKeyValidationTestCases + { + get + { + DateTime utcNow = DateTime.UtcNow; + DateTime utcExpired = KeyingMaterial.ExpiredX509SecurityKey_Public.Certificate.NotAfter.ToUniversalTime(); + DateTime utcNotYetValid = KeyingMaterial.NotYetValidX509SecurityKey_Public.Certificate.NotBefore.ToUniversalTime(); + + return new TheoryData + { + new SigningKeyValidationTheoryData + { + TestId = "Invalid_SecurityKeyIsNull", + ExpectedException = ExpectedException.ArgumentNullException(substringExpected: "IDX10253:"), + SecurityKey = null, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true }, + SigningKeyValidationResult = new SigningKeyValidationResult( + null, // SecurityKey + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail(LogMessages.IDX10253), + typeof(ArgumentNullException), + new StackFrame(true))) + }, + new SigningKeyValidationTheoryData + { + TestId = "Invalid_SecurityTokenIsNullAndValidateIssuerSigningKeyTrue", + ExpectedException = ExpectedException.ArgumentNullException(), + SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, + SecurityToken = null, + ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true }, + SigningKeyValidationResult = new SigningKeyValidationResult( + KeyingMaterial.SymmetricSecurityKey2_256, + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("securityToken")), + typeof(ArgumentNullException), + new StackFrame(true))) + }, + new SigningKeyValidationTheoryData + { + TestId = "Invalid_ValidationParametersIsNull", + ExpectedException = ExpectedException.ArgumentNullException(), + SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = null, + SigningKeyValidationResult = new SigningKeyValidationResult( + KeyingMaterial.SymmetricSecurityKey2_256, + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("validationParameters")), + typeof(ArgumentNullException), + new StackFrame(true))) + }, + new SigningKeyValidationTheoryData + { + TestId = "Valid_SecurityTokenIsPresentAndValidateIssuerSigningKeyTrue", + ExpectedException = ExpectedException.NoExceptionExpected, + SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true }, + SigningKeyValidationResult = new SigningKeyValidationResult(KeyingMaterial.SymmetricSecurityKey2_256) + }, + new SigningKeyValidationTheoryData + { + TestId = "Valid_SecurityKeyIsNullAndValidateIssuerSigningKeyFalse", + ExpectedException = ExpectedException.NoExceptionExpected, + SecurityKey = null, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = false }, + SigningKeyValidationResult = new SigningKeyValidationResult(null) + }, + new SigningKeyValidationTheoryData + { + TestId = "Valid_SecurityTokenIsNullAndValidateIssuerSigningKeyFalse", + ExpectedException = ExpectedException.NoExceptionExpected, + SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, + SecurityToken = null, + ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = false }, + SigningKeyValidationResult = new SigningKeyValidationResult(KeyingMaterial.SymmetricSecurityKey2_256) + }, + new SigningKeyValidationTheoryData + { + TestId = "Valid_SecurityKeyIsNullAndRequireSignedTokensFalse", + ExpectedException = ExpectedException.NoExceptionExpected, + SecurityKey = null, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, RequireSignedTokens = false }, + SigningKeyValidationResult = new SigningKeyValidationResult(null) + }, + new SigningKeyValidationTheoryData + { + TestId = "Valid_SecurityKeyIsPresentAndRequireSignedTokensTrue", + ExpectedException = ExpectedException.NoExceptionExpected, + SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, RequireSignedTokens = true }, + SigningKeyValidationResult = new SigningKeyValidationResult(KeyingMaterial.SymmetricSecurityKey2_256) + }, + new SigningKeyValidationTheoryData + { + TestId = "Valid_SecurityKeyIsPresentAndRequireSignedTokensFalse", + ExpectedException = ExpectedException.NoExceptionExpected, + SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, RequireSignedTokens = false }, + SigningKeyValidationResult = new SigningKeyValidationResult(KeyingMaterial.SymmetricSecurityKey2_256) + }, + new SigningKeyValidationTheoryData + { + TestId = "Valid_DelegateSetAndReturnsTrue", + ExpectedException = ExpectedException.NoExceptionExpected, + SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKeyValidator = (SecurityKey securityKey, SecurityToken token, TokenValidationParameters validationParameters) => true + }, + SigningKeyValidationResult = new SigningKeyValidationResult(KeyingMaterial.SymmetricSecurityKey2_256) + }, + new SigningKeyValidationTheoryData + { + TestId = "Valid_DelegateUsingConfigurationSetConfigurationAndReturnsTrue", + ExpectedException = ExpectedException.NoExceptionExpected, + SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKeyValidatorUsingConfiguration = (SecurityKey securityKey, SecurityToken token, TokenValidationParameters validationParameters, BaseConfiguration configuration) => true + }, + BaseConfiguration = new OpenIdConnectConfiguration(), + SigningKeyValidationResult = new SigningKeyValidationResult(KeyingMaterial.SymmetricSecurityKey2_256) + }, + new SigningKeyValidationTheoryData + { + TestId = "Invalid_SecurityKeyIsExpired", + ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(substringExpected: "IDX10249:"), + SecurityKey = KeyingMaterial.ExpiredX509SecurityKey_Public, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true }, + SigningKeyValidationResult = new SigningKeyValidationResult( + KeyingMaterial.ExpiredX509SecurityKey_Public, + ValidationFailureType.SigningKeyValidationFailed, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10249, + LogHelper.MarkAsNonPII(utcExpired), + LogHelper.MarkAsNonPII(utcNow)), + typeof(SecurityTokenInvalidSigningKeyException), + new StackFrame(true))) + }, + new SigningKeyValidationTheoryData + { + TestId = "Invalid_SecurityKeyIsNotYetValid", + ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(substringExpected: "IDX10248:"), + SecurityKey = KeyingMaterial.NotYetValidX509SecurityKey_Public, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true }, + SigningKeyValidationResult = new SigningKeyValidationResult( + KeyingMaterial.NotYetValidX509SecurityKey_Public, + ValidationFailureType.SigningKeyValidationFailed, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10248, + LogHelper.MarkAsNonPII(utcNotYetValid), + LogHelper.MarkAsNonPII(utcNow)), + typeof(SecurityTokenInvalidSigningKeyException), + new StackFrame(true))) + }, + new SigningKeyValidationTheoryData + { + TestId = "Invalid_SecurityKeyIsNullAndRequireSignedTokensTrue", + ExpectedException = ExpectedException.ArgumentNullException(substringExpected: "IDX10253:"), + SecurityKey = null, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, RequireSignedTokens = true }, + SigningKeyValidationResult = new SigningKeyValidationResult( + null, + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail(LogMessages.IDX10253), + typeof(ArgumentNullException), + new StackFrame(true))) + }, + new SigningKeyValidationTheoryData + { + TestId = "Invalid_DelegateIsSetAndReturnsFalse", + ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(substringExpected: "IDX10232:"), + SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKeyValidator = (SecurityKey securityKey, SecurityToken token, TokenValidationParameters validationParameters) => false + }, + SigningKeyValidationResult = new SigningKeyValidationResult( + KeyingMaterial.SymmetricSecurityKey2_256, + ValidationFailureType.SigningKeyValidationFailed, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10232, + KeyingMaterial.SymmetricSecurityKey2_256), + typeof(SecurityTokenInvalidSigningKeyException), + new StackFrame(true))) + }, + new SigningKeyValidationTheoryData + { + TestId = "Invalid_DelegateUsingConfigurationSetConfigurationAndReturnsFalse", + ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(substringExpected: "IDX10232:"), + SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKeyValidatorUsingConfiguration = (SecurityKey securityKey, SecurityToken token, TokenValidationParameters validationParameters, BaseConfiguration configuration) => false + }, + BaseConfiguration = new OpenIdConnectConfiguration(), + SigningKeyValidationResult = new SigningKeyValidationResult( + KeyingMaterial.SymmetricSecurityKey2_256, + ValidationFailureType.SigningKeyValidationFailed, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10232, + KeyingMaterial.SymmetricSecurityKey2_256), + typeof(SecurityTokenInvalidSigningKeyException), + new StackFrame(true))) + }, + new SigningKeyValidationTheoryData + { + TestId = "Invalid_DelegateIsSetAndThrows", + ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(substringExpected: "IDX10232:", innerTypeExpected: typeof(SecurityTokenInvalidSigningKeyException)), + SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKeyValidator = (SecurityKey securityKey, SecurityToken token, TokenValidationParameters validationParameters) => throw new SecurityTokenInvalidSigningKeyException() + }, + SigningKeyValidationResult = new SigningKeyValidationResult( + KeyingMaterial.SymmetricSecurityKey2_256, + ValidationFailureType.SigningKeyValidationFailed, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10232, + KeyingMaterial.SymmetricSecurityKey2_256), + typeof(SecurityTokenInvalidSigningKeyException), + new StackFrame(true), + new SecurityTokenInvalidSigningKeyException())) + }, + new SigningKeyValidationTheoryData + { + TestId = "Invalid_DelegateUsingConfigurationSetConfigurationAndThrows", + ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(substringExpected: "IDX10232:", innerTypeExpected: typeof(SecurityTokenInvalidSigningKeyException)), + SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKeyValidatorUsingConfiguration = (SecurityKey securityKey, SecurityToken token, TokenValidationParameters validationParameters, BaseConfiguration configuration) => throw new SecurityTokenInvalidSigningKeyException() + }, + BaseConfiguration = new OpenIdConnectConfiguration(), + SigningKeyValidationResult = new SigningKeyValidationResult( + KeyingMaterial.SymmetricSecurityKey2_256, + ValidationFailureType.SigningKeyValidationFailed, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10232, + KeyingMaterial.SymmetricSecurityKey2_256), + typeof(SecurityTokenInvalidSigningKeyException), + new StackFrame(true), + new SecurityTokenInvalidSigningKeyException())) + }, + }; + } + } + } + + public class SigningKeyValidationTheoryData: TheoryDataBase + { + public SecurityKey SecurityKey { get; set; } + public SecurityToken SecurityToken { get; set; } + public TokenValidationParameters ValidationParameters { get; set; } + public BaseConfiguration BaseConfiguration { get; set; } + internal SigningKeyValidationResult SigningKeyValidationResult { get; set; } + } + } From 3753b5ccce9cfaadfc990712a01a605de1de8166 Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Fri, 28 Jun 2024 15:29:55 +0100 Subject: [PATCH 5/5] Updated test names to match other validator tests. Sorted Valid above Invalid test cases. --- .../SigningKeyValidationResultTests.cs | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs index fbba587543..43a6d3e313 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs @@ -50,56 +50,7 @@ public static TheoryData SigningKeyValidationTes { new SigningKeyValidationTheoryData { - TestId = "Invalid_SecurityKeyIsNull", - ExpectedException = ExpectedException.ArgumentNullException(substringExpected: "IDX10253:"), - SecurityKey = null, - SecurityToken = new JwtSecurityToken(), - ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true }, - SigningKeyValidationResult = new SigningKeyValidationResult( - null, // SecurityKey - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail(LogMessages.IDX10253), - typeof(ArgumentNullException), - new StackFrame(true))) - }, - new SigningKeyValidationTheoryData - { - TestId = "Invalid_SecurityTokenIsNullAndValidateIssuerSigningKeyTrue", - ExpectedException = ExpectedException.ArgumentNullException(), - SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, - SecurityToken = null, - ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true }, - SigningKeyValidationResult = new SigningKeyValidationResult( - KeyingMaterial.SymmetricSecurityKey2_256, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII("securityToken")), - typeof(ArgumentNullException), - new StackFrame(true))) - }, - new SigningKeyValidationTheoryData - { - TestId = "Invalid_ValidationParametersIsNull", - ExpectedException = ExpectedException.ArgumentNullException(), - SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, - SecurityToken = new JwtSecurityToken(), - ValidationParameters = null, - SigningKeyValidationResult = new SigningKeyValidationResult( - KeyingMaterial.SymmetricSecurityKey2_256, - ValidationFailureType.NullArgument, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10000, - LogHelper.MarkAsNonPII("validationParameters")), - typeof(ArgumentNullException), - new StackFrame(true))) - }, - new SigningKeyValidationTheoryData - { - TestId = "Valid_SecurityTokenIsPresentAndValidateIssuerSigningKeyTrue", + TestId = "Valid_SecurityTokenIsPresent_ValidateIssuerSigningKeyIsTrue", ExpectedException = ExpectedException.NoExceptionExpected, SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, SecurityToken = new JwtSecurityToken(), @@ -108,7 +59,7 @@ public static TheoryData SigningKeyValidationTes }, new SigningKeyValidationTheoryData { - TestId = "Valid_SecurityKeyIsNullAndValidateIssuerSigningKeyFalse", + TestId = "Valid_SecurityKeyIsNull_ValidateIssuerSigningKeyIsFalse", ExpectedException = ExpectedException.NoExceptionExpected, SecurityKey = null, SecurityToken = new JwtSecurityToken(), @@ -117,7 +68,7 @@ public static TheoryData SigningKeyValidationTes }, new SigningKeyValidationTheoryData { - TestId = "Valid_SecurityTokenIsNullAndValidateIssuerSigningKeyFalse", + TestId = "Valid_SecurityTokenIsNull_ValidateIssuerSigningKeyIsFalse", ExpectedException = ExpectedException.NoExceptionExpected, SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, SecurityToken = null, @@ -126,7 +77,7 @@ public static TheoryData SigningKeyValidationTes }, new SigningKeyValidationTheoryData { - TestId = "Valid_SecurityKeyIsNullAndRequireSignedTokensFalse", + TestId = "Valid_SecurityKeyIsNull_RequireSignedTokensIsFalse", ExpectedException = ExpectedException.NoExceptionExpected, SecurityKey = null, SecurityToken = new JwtSecurityToken(), @@ -135,7 +86,7 @@ public static TheoryData SigningKeyValidationTes }, new SigningKeyValidationTheoryData { - TestId = "Valid_SecurityKeyIsPresentAndRequireSignedTokensTrue", + TestId = "Valid_SecurityKeyIsPresent_RequireSignedTokensIsTrue", ExpectedException = ExpectedException.NoExceptionExpected, SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, SecurityToken = new JwtSecurityToken(), @@ -144,7 +95,7 @@ public static TheoryData SigningKeyValidationTes }, new SigningKeyValidationTheoryData { - TestId = "Valid_SecurityKeyIsPresentAndRequireSignedTokensFalse", + TestId = "Valid_SecurityKeyIsPresent_RequireSignedTokensIsFalse", ExpectedException = ExpectedException.NoExceptionExpected, SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, SecurityToken = new JwtSecurityToken(), @@ -153,7 +104,7 @@ public static TheoryData SigningKeyValidationTes }, new SigningKeyValidationTheoryData { - TestId = "Valid_DelegateSetAndReturnsTrue", + TestId = "Valid_DelegateSet_ReturnsTrue", ExpectedException = ExpectedException.NoExceptionExpected, SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, SecurityToken = new JwtSecurityToken(), @@ -166,7 +117,7 @@ public static TheoryData SigningKeyValidationTes }, new SigningKeyValidationTheoryData { - TestId = "Valid_DelegateUsingConfigurationSetConfigurationAndReturnsTrue", + TestId = "Valid_DelegateUsingConfigurationSet_ReturnsTrue", ExpectedException = ExpectedException.NoExceptionExpected, SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, SecurityToken = new JwtSecurityToken(), @@ -179,6 +130,55 @@ public static TheoryData SigningKeyValidationTes SigningKeyValidationResult = new SigningKeyValidationResult(KeyingMaterial.SymmetricSecurityKey2_256) }, new SigningKeyValidationTheoryData + { + TestId = "Invalid_SecurityKeyIsNull", + ExpectedException = ExpectedException.ArgumentNullException(substringExpected: "IDX10253:"), + SecurityKey = null, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true }, + SigningKeyValidationResult = new SigningKeyValidationResult( + null, // SecurityKey + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail(LogMessages.IDX10253), + typeof(ArgumentNullException), + new StackFrame(true))) + }, + new SigningKeyValidationTheoryData + { + TestId = "Invalid_SecurityTokenIsNullAndValidateIssuerSigningKeyTrue", + ExpectedException = ExpectedException.ArgumentNullException(), + SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, + SecurityToken = null, + ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true }, + SigningKeyValidationResult = new SigningKeyValidationResult( + KeyingMaterial.SymmetricSecurityKey2_256, + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("securityToken")), + typeof(ArgumentNullException), + new StackFrame(true))) + }, + new SigningKeyValidationTheoryData + { + TestId = "Invalid_ValidationParametersIsNull", + ExpectedException = ExpectedException.ArgumentNullException(), + SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, + SecurityToken = new JwtSecurityToken(), + ValidationParameters = null, + SigningKeyValidationResult = new SigningKeyValidationResult( + KeyingMaterial.SymmetricSecurityKey2_256, + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("validationParameters")), + typeof(ArgumentNullException), + new StackFrame(true))) + }, + new SigningKeyValidationTheoryData { TestId = "Invalid_SecurityKeyIsExpired", ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(substringExpected: "IDX10249:"), @@ -216,7 +216,7 @@ public static TheoryData SigningKeyValidationTes }, new SigningKeyValidationTheoryData { - TestId = "Invalid_SecurityKeyIsNullAndRequireSignedTokensTrue", + TestId = "Invalid_SecurityKeyIsNull_RequireSignedTokensIsTrue", ExpectedException = ExpectedException.ArgumentNullException(substringExpected: "IDX10253:"), SecurityKey = null, SecurityToken = new JwtSecurityToken(), @@ -231,7 +231,7 @@ public static TheoryData SigningKeyValidationTes }, new SigningKeyValidationTheoryData { - TestId = "Invalid_DelegateIsSetAndReturnsFalse", + TestId = "Invalid_DelegateIsSet_ReturnsFalse", ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(substringExpected: "IDX10232:"), SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, SecurityToken = new JwtSecurityToken(), @@ -252,7 +252,7 @@ public static TheoryData SigningKeyValidationTes }, new SigningKeyValidationTheoryData { - TestId = "Invalid_DelegateUsingConfigurationSetConfigurationAndReturnsFalse", + TestId = "Invalid_DelegateUsingConfigurationSet_ReturnsFalse", ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(substringExpected: "IDX10232:"), SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, SecurityToken = new JwtSecurityToken(), @@ -274,7 +274,7 @@ public static TheoryData SigningKeyValidationTes }, new SigningKeyValidationTheoryData { - TestId = "Invalid_DelegateIsSetAndThrows", + TestId = "Invalid_DelegateIsSet_Throws", ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(substringExpected: "IDX10232:", innerTypeExpected: typeof(SecurityTokenInvalidSigningKeyException)), SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, SecurityToken = new JwtSecurityToken(), @@ -296,7 +296,7 @@ public static TheoryData SigningKeyValidationTes }, new SigningKeyValidationTheoryData { - TestId = "Invalid_DelegateUsingConfigurationSetConfigurationAndThrows", + TestId = "Invalid_DelegateUsingConfigurationSet_Throws", ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException(substringExpected: "IDX10232:", innerTypeExpected: typeof(SecurityTokenInvalidSigningKeyException)), SecurityKey = KeyingMaterial.SymmetricSecurityKey2_256, SecurityToken = new JwtSecurityToken(),