From bb9a6d991ab499ed175c6161cbaf575a88e92b16 Mon Sep 17 00:00:00 2001 From: Franco Fung <38921563+FuPingFranco@users.noreply.github.com> Date: Fri, 26 Jul 2024 17:02:40 -0700 Subject: [PATCH] Remove Delegate Checks in Multiple Validators and Prevents Null Setting of Delegates (#2725) * Removed delegate checks for token type validation. * Fix un-referenced message check * Added internal delegate for token type validation * Prevent delegates from being null. * Simplify Valid types getter --------- Co-authored-by: Franco Fung --- .../LogMessages.cs | 1 - .../Validation/ValidationParameters.cs | 110 ++++++++++++++---- .../Validation/Validators.Algorithm.cs | 2 +- .../Validation/Validators.Audience.cs | 2 +- .../Validation/Validators.Lifetime.cs | 59 ++-------- .../Validation/Validators.TokenReplay.cs | 65 ++--------- .../Validation/Validators.TokenType.cs | 64 ++++------ .../LifetimeValidationResultTests.cs | 90 ++------------ .../Validation/ReplayValidationResultTests.cs | 105 ++--------------- .../TokenTypeValidationResultTests.cs | 57 +-------- .../Validation/ValidationParametersTests.cs | 53 +++++++++ 11 files changed, 211 insertions(+), 397 deletions(-) create mode 100644 test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidationParametersTests.cs diff --git a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs index f518880fe3..d7ecd84c59 100644 --- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs @@ -73,7 +73,6 @@ internal static class LogMessages public const string IDX10256 = "IDX10256: Unable to validate the token type. TokenValidationParameters.ValidTypes is set, but the 'typ' header claim is null or empty."; public const string IDX10257 = "IDX10257: Token type validation failed. Type: '{0}'. Did not match: validationParameters.TokenTypes: '{1}'."; public const string IDX10258 = "IDX10258: Token type validated. Type: '{0}'."; - public const string IDX10259 = "IDX10259: Unable to validate the token type, delegate threw an exception."; // public const string IDX10260 = "IDX10260:"; public const string IDX10261 = "IDX10261: Unable to retrieve configuration from authority: '{0}'. \nProceeding with token validation in case the relevant properties have been set manually on the TokenValidationParameters. Exception caught: \n {1}. See https://aka.ms/validate-using-configuration-manager for additional information."; public const string IDX10262 = "IDX10262: One of the issuers in TokenValidationParameters.ValidIssuers was null or an empty string. See https://aka.ms/wilson/tokenvalidation for details."; diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs index f8cebd1d5f..5977ca0660 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Security.Claims; +using System.Threading; using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; @@ -20,8 +21,13 @@ internal class ValidationParameters private string _nameClaimType = ClaimsIdentity.DefaultNameClaimType; private string _roleClaimType = ClaimsIdentity.DefaultRoleClaimType; private Dictionary _instancePropertyBag; + private IList _validTokenTypes = []; - private IssuerValidationDelegateAsync _issuerValidationDelegate = Validators.ValidateIssuerAsync; + private AudienceValidatorDelegate _audienceValidator = Validators.ValidateAudience; + private IssuerValidationDelegateAsync _issuerValidatorAsync = Validators.ValidateIssuerAsync; + private LifetimeValidatorDelegate _lifetimeValidator = Validators.ValidateLifetime; + private TypeValidatorDelegate _typeValidator = Validators.ValidateTokenType; + private TokenReplayValidatorDelegate _tokenReplayValidator = Validators.ValidateTokenReplay; /// /// This is the default value of when creating a . @@ -112,14 +118,25 @@ public ValidationParameters() public AlgorithmValidator AlgorithmValidator { get; set; } /// - /// Gets or sets a delegate that will be used to validate the audience. + /// Allows overriding the delegate that will be used to validate the audience. /// /// - /// If set, this delegate will be called to validate the 'audience', instead of default processing. + /// If set, this delegate will be responsible for validating the 'audience', instead of default processing. /// This means that no default 'audience' validation will occur. - /// Even if is false, this delegate will still be called. /// - public AudienceValidator AudienceValidator { get; set; } + /// Thrown when the value is set as null. + /// The used to validate the issuer of a token + public AudienceValidatorDelegate AudienceValidator + { + get + { + return _audienceValidator; + } + set + { + _audienceValidator = value ?? throw new ArgumentNullException(nameof(value), "AudienceValidator cannot be set as null."); + } + } /// /// Gets or sets the AuthenticationType when creating a . @@ -278,17 +295,19 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken, public IList IssuerSigningKeys { get; } /// - /// Gets or sets a delegate that will be used to validate the issuer of the token. + /// Allows overriding the delegate that will be used to validate the issuer of the token. /// + /// Thrown when the value is set as null. + /// The used to validate the issuer of a token public IssuerValidationDelegateAsync IssuerValidatorAsync { get { - return _issuerValidationDelegate; + return _issuerValidatorAsync; } set { - _issuerValidationDelegate = value ?? throw LogHelper.LogArgumentNullException(nameof(value)); + _issuerValidatorAsync = value ?? throw new ArgumentNullException(nameof(value), "IssuerValidatorAsync cannot be set as null."); } } @@ -298,9 +317,21 @@ public IssuerValidationDelegateAsync IssuerValidatorAsync public TransformBeforeSignatureValidation TransformBeforeSignatureValidation { get; set; } /// - /// Gets or sets a delegate that will be used to validate the lifetime of the token + /// Allows overriding the delegate that will be used to validate the lifetime of the token /// - public LifetimeValidator LifetimeValidator { get; set; } + /// Thrown when the value is set as null. + /// The used to validate the lifetime of a token + public LifetimeValidatorDelegate LifetimeValidator + { + get + { + return _lifetimeValidator; + } + set + { + _lifetimeValidator = value ?? throw new ArgumentNullException(nameof(value), "LifetimeValidator cannot be set as null."); + } + } /// /// Gets or sets a that will decide if the token identifier claim needs to be logged. @@ -430,26 +461,51 @@ public string RoleClaimType public ITokenReplayCache TokenReplayCache { get; set; } /// - /// Gets or sets a delegate that will be used to validate the token replay of the token + /// Allows overriding the delegate that will be used to validate the token replay of the token. /// /// - /// If set, this delegate will be called to validate the token replay of the token, instead of default processing. + /// If no delegate is set, the default implementation will be used. /// This means no default token replay validation will occur. - /// Even if is false, this delegate will still be called. /// - public TokenReplayValidator TokenReplayValidator { get; set; } + /// Thrown when the value is set as null. + /// The used to validate the token replay of the token. + public TokenReplayValidatorDelegate TokenReplayValidator + { + get + { + return _tokenReplayValidator; + } + + set + { + _tokenReplayValidator = value ?? throw new ArgumentNullException(nameof(value), "TokenReplayValidator cannot be set as null."); + } + } /// - /// Gets or sets a delegate that will be used to validate the type of the token. - /// If the token type cannot be validated, an exception MUST be thrown by the delegate. + /// Allows overriding the delegate that will be used to validate the type of the token. + /// If the token type cannot be validated, a MUST be returned by the delegate. /// Note: the 'type' parameter may be null if it couldn't be extracted from its usual location. /// Implementations that need to resolve it from a different location can use the 'token' parameter. /// /// - /// If set, this delegate will be called to validate the 'type' of the token, instead of default processing. - /// This means that no default 'type' validation will occur. + /// If no delegate is set, the default implementation will be used. The default checks the type + /// against the property, if the type is present then, it will succeed. /// - public TypeValidator TypeValidator { get; set; } + /// Thrown when the value is set as null. + /// The used to validate the token type of a token + public TypeValidatorDelegate TypeValidator + { + get + { + return _typeValidator; + } + + set + { + _typeValidator = value ?? throw new ArgumentNullException(nameof(value), "TypeValidator cannot be set as null."); + } + } /// /// Gets or sets a boolean to control if the LKG configuration will be used for token validation. @@ -494,9 +550,21 @@ public string RoleClaimType /// Gets the that contains valid types that will be used to check against the JWT header's 'typ' claim. /// If this property is not set, the 'typ' header claim will not be validated and all types will be accepted. /// In the case of a JWE, this property will ONLY apply to the inner token header. - /// The default is null. + /// The default is an empty collection. /// - public IList ValidTypes { get; } + /// Thrown when the value is set as null. + /// The that contains valid token types that will be used to check against the token's 'typ' claim. + public IList ValidTypes + { + get + { + return _validTokenTypes; + } + set + { + _validTokenTypes = value ?? throw new ArgumentNullException(nameof(value)); + } + } public bool ValidateActor { get; set; } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs index 631660107f..f1ccdaa728 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs @@ -17,7 +17,7 @@ public static partial class Validators /// The algorithm to be validated. /// The that signed the . /// The being validated. - /// required for validation. + /// required for validation. /// #pragma warning disable CA1801 // TODO: remove pragma disable once callContext is used for logging internal static AlgorithmValidationResult ValidateAlgorithm( diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs index ffb299af33..1630c221b8 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs @@ -20,7 +20,7 @@ namespace Microsoft.IdentityModel.Tokens /// /// A that contains the results of validating the issuer. /// This delegate is not expected to throw. - internal delegate AudienceValidationResult ValidateAudience( + internal delegate AudienceValidationResult AudienceValidatorDelegate( IEnumerable audiences, SecurityToken? securityToken, TokenValidationParameters validationParameters, diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs index 41b53dd688..cc4e4f0abf 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs @@ -19,11 +19,11 @@ namespace Microsoft.IdentityModel.Tokens /// /// A that contains the results of validating the issuer. /// This delegate is not expected to throw. - internal delegate LifetimeValidationResult LifetimeValidationDelegate( + internal delegate LifetimeValidationResult LifetimeValidatorDelegate( DateTime? notBefore, DateTime? expires, SecurityToken? securityToken, - TokenValidationParameters validationParameters, + ValidationParameters validationParameters, CallContext callContext); /// @@ -37,18 +37,18 @@ public static partial class Validators /// The 'notBefore' time found in the . /// The 'expiration' time found in the . /// The being validated. - /// The to be used for validating the token. + /// The to be used for validating the token. /// /// A indicating whether validation was successful, and providing a if it was not. /// If 'validationParameters' is null. - /// If 'expires.HasValue' is false and is true. + /// If 'expires.HasValue' is false. /// If 'notBefore' is > 'expires'. /// If 'notBefore' is > DateTime.UtcNow. /// If 'expires' is < DateTime.UtcNow. - /// All time comparisons apply . + /// All time comparisons apply . /// Exceptions are not thrown, but embedded in . #pragma warning disable CA1801 // TODO: remove pragma disable once callContext is used for logging - internal static LifetimeValidationResult ValidateLifetime(DateTime? notBefore, DateTime? expires, SecurityToken? securityToken, TokenValidationParameters validationParameters, CallContext callContext) + internal static LifetimeValidationResult ValidateLifetime(DateTime? notBefore, DateTime? expires, SecurityToken? securityToken, ValidationParameters validationParameters, CallContext callContext) #pragma warning restore CA1801 { if (validationParameters == null) @@ -63,16 +63,7 @@ internal static LifetimeValidationResult ValidateLifetime(DateTime? notBefore, D typeof(ArgumentNullException), new StackFrame(true))); - if (validationParameters.LifetimeValidator != null) - return ValidateLifetimeUsingDelegate(notBefore, expires, securityToken, validationParameters); - - if (!validationParameters.ValidateLifetime) - { - LogHelper.LogInformation(LogMessages.IDX10238); - return new LifetimeValidationResult(notBefore, expires); - } - - if (!expires.HasValue && validationParameters.RequireExpirationTime) + if (!expires.HasValue) return new LifetimeValidationResult( notBefore, expires, @@ -130,42 +121,6 @@ internal static LifetimeValidationResult ValidateLifetime(DateTime? notBefore, D return new LifetimeValidationResult(notBefore, expires); } - - private static LifetimeValidationResult ValidateLifetimeUsingDelegate(DateTime? notBefore, DateTime? expires, SecurityToken? securityToken, TokenValidationParameters validationParameters) - { - try - { - if (!validationParameters.LifetimeValidator(notBefore, expires, securityToken, validationParameters)) - return new LifetimeValidationResult( - notBefore, - expires, - ValidationFailureType.LifetimeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10230, - securityToken), - typeof(SecurityTokenInvalidLifetimeException), - new StackFrame(true))); - - return new LifetimeValidationResult(notBefore, expires); - } -#pragma warning disable CA1031 // Do not catch general exception types - catch (Exception delegateException) -#pragma warning restore CA1031 // Do not catch general exception types - { - return new LifetimeValidationResult( - notBefore, - expires, - ValidationFailureType.LifetimeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10230, - securityToken), - delegateException.GetType(), - new StackFrame(true), - delegateException)); - } - } } } #nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs index c9807ac53e..17186207ef 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs @@ -2,11 +2,7 @@ // Licensed under the MIT License. using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; namespace Microsoft.IdentityModel.Tokens @@ -16,14 +12,14 @@ namespace Microsoft.IdentityModel.Tokens /// /// When does the expire.. /// The security token that is being validated. - /// The to be used for validating the token. + /// The to be used for validating the token. /// /// A that contains the results of validating the token. /// This delegate is not expected to throw. - internal delegate ReplayValidationResult ValidateTokenReplay( + internal delegate ReplayValidationResult TokenReplayValidatorDelegate( DateTime? expirationTime, string securityToken, - TokenValidationParameters validationParameters, + ValidationParameters validationParameters, CallContext callContext); /// @@ -36,15 +32,15 @@ public static partial class Validators /// /// When does the security token expire. /// The being validated. - /// The to be used for validating the token. + /// 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 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 . + /// If the 'securityToken' could not be added to the . #pragma warning disable CA1801 // Review unused parameters - internal static ReplayValidationResult ValidateTokenReplay(DateTime? expirationTime, string securityToken, TokenValidationParameters validationParameters, CallContext callContext) + internal static ReplayValidationResult ValidateTokenReplay(DateTime? expirationTime, string securityToken, ValidationParameters validationParameters, CallContext callContext) #pragma warning restore CA1801 // Review unused parameters { if (string.IsNullOrWhiteSpace(securityToken)) @@ -71,18 +67,6 @@ internal static ReplayValidationResult ValidateTokenReplay(DateTime? expirationT new StackFrame(), null)); - if (validationParameters.TokenReplayValidator != null) - { - return ValidateTokenReplayUsingDelegate(expirationTime, securityToken, validationParameters); - } - - if (!validationParameters.ValidateTokenReplay) - { - LogHelper.LogVerbose(LogMessages.IDX10246); - - return new ReplayValidationResult(expirationTime); - } - // check if token if replay cache is set, then there must be an expiration time. if (validationParameters.TokenReplayCache != null) { @@ -127,40 +111,5 @@ internal static ReplayValidationResult ValidateTokenReplay(DateTime? expirationT LogHelper.LogInformation(LogMessages.IDX10240); return new ReplayValidationResult(expirationTime); } - - private static ReplayValidationResult ValidateTokenReplayUsingDelegate(DateTime? expirationTime, string securityToken, TokenValidationParameters validationParameters) - { - try - { - if (!validationParameters.TokenReplayValidator(expirationTime, securityToken, validationParameters)) - return new ReplayValidationResult( - expirationTime, - ValidationFailureType.TokenReplayValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10228, - LogHelper.MarkAsUnsafeSecurityArtifact(securityToken, t => t.ToString())), - typeof(SecurityTokenReplayDetectedException), - new StackFrame(), - null)); - - return new ReplayValidationResult(expirationTime); - } -#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 ReplayValidationResult( - expirationTime, - ValidationFailureType.TokenReplayValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10228, - LogHelper.MarkAsUnsafeSecurityArtifact(securityToken, t => t.ToString())), - exception.GetType(), - new StackFrame(), - exception)); - } - } } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs index 4cbb6aa701..ef119b6224 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs @@ -10,6 +10,21 @@ #nullable enable namespace Microsoft.IdentityModel.Tokens { + /// + /// Definition for delegate that will validate the token type of a 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. + /// + /// A that contains the results of validating the token type. + /// An EXACT match is required. (case sensitive) is used for comparing against . + internal delegate TokenTypeValidationResult TypeValidatorDelegate( + string? type, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext); + public static partial class Validators { /// @@ -17,15 +32,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. + /// 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 . + /// A that contains the results of validating the token type. + /// An EXACT match is required. (case sensitive) is used for comparing against . #pragma warning disable CA1801 // TODO: remove pragma disable once callContext is used for logging - internal static TokenTypeValidationResult ValidateTokenType(string? type, SecurityToken? securityToken, TokenValidationParameters validationParameters, CallContext callContext) + internal static TokenTypeValidationResult ValidateTokenType( + string? type, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) #pragma warning restore CA1801 // TODO: remove pragma disable once callContext is used for logging { if (securityToken == null) @@ -54,19 +70,12 @@ internal static TokenTypeValidationResult ValidateTokenType(string? type, Securi new StackFrame(true))); } - if (validationParameters.TypeValidator == null && (validationParameters.ValidTypes == null || !validationParameters.ValidTypes.Any())) + if (validationParameters.ValidTypes.Count == 0) { LogHelper.LogVerbose(LogMessages.IDX10255); return new TokenTypeValidationResult(type); } - if (validationParameters.TypeValidator != null) - { - return ValidateTokenTypeUsingDelegate(type, securityToken, validationParameters); - } - - // Note: don't return an invalid TokenTypeValidationResult 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)) { return new TokenTypeValidationResult( @@ -101,31 +110,6 @@ internal static TokenTypeValidationResult ValidateTokenType(string? type, Securi return new TokenTypeValidationResult(type); } - - private static TokenTypeValidationResult ValidateTokenTypeUsingDelegate(string? type, SecurityToken securityToken, TokenValidationParameters validationParameters) - { - try - { - var validatedType = validationParameters.TypeValidator(type, securityToken, validationParameters); - return new TokenTypeValidationResult(validatedType); - } -#pragma warning disable CA1031 // Do not catch general exception types - catch (Exception ex) -#pragma warning restore CA1031 // Do not catch general exception types - { - return new TokenTypeValidationResult( - type, - ValidationFailureType.TokenTypeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10259, - LogHelper.MarkAsNonPII(nameof(validationParameters.TypeValidator)), - LogHelper.MarkAsNonPII(ex.Message)), - ex.GetType(), - new StackFrame(true), - ex)); - } - } } } #nullable restore diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/LifetimeValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/LifetimeValidationResultTests.cs index 49ab54db6d..504817363b 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/LifetimeValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/LifetimeValidationResultTests.cs @@ -58,53 +58,29 @@ public static TheoryData ValidateLifetimeTestCases Expires = oneHourFromNow, NotBefore = oneHourAgo, LifetimeValidationResult = new LifetimeValidationResult(oneHourAgo, oneHourFromNow), - ValidationParameters = new TokenValidationParameters() - }, - new ValidateLifetimeTheoryData("Valid_ValidateLifetimeIsFalse_DatesAreNotNull") - { - Expires = oneHourFromNow, - NotBefore = oneHourAgo, - LifetimeValidationResult = new LifetimeValidationResult(oneHourAgo, oneHourFromNow), - ValidationParameters = new TokenValidationParameters { ValidateLifetime = false } - }, - new ValidateLifetimeTheoryData("Valid_ValidateLifetimeIsFalse_DatesAreNull") - { - Expires = null, - NotBefore = null, - LifetimeValidationResult = new LifetimeValidationResult(null, null), - ValidationParameters = new TokenValidationParameters { ValidateLifetime = false } + ValidationParameters = new ValidationParameters() }, new ValidateLifetimeTheoryData("Valid_NotBeforeIsNull") { Expires = oneHourFromNow, NotBefore = null, LifetimeValidationResult = new LifetimeValidationResult(null, oneHourFromNow), - ValidationParameters = new TokenValidationParameters() + ValidationParameters = new ValidationParameters() }, new ValidateLifetimeTheoryData("Valid_SkewForward") { Expires = oneHourFromNow, NotBefore = twoMinutesFromNow, - ValidationParameters = new TokenValidationParameters { ClockSkew = TimeSpan.FromMinutes(5) }, + ValidationParameters = new ValidationParameters { ClockSkew = TimeSpan.FromMinutes(5) }, LifetimeValidationResult = new LifetimeValidationResult(twoMinutesFromNow, oneHourFromNow), }, new ValidateLifetimeTheoryData("Valid_SkewBackward") { Expires = oneMinuteAgo, NotBefore = twoMinutesAgo, - ValidationParameters = new TokenValidationParameters { ClockSkew = TimeSpan.FromMinutes(5) }, + ValidationParameters = new ValidationParameters { ClockSkew = TimeSpan.FromMinutes(5) }, LifetimeValidationResult = new LifetimeValidationResult(twoMinutesAgo, oneMinuteAgo), }, - new ValidateLifetimeTheoryData("Valid_DelegateReturnsTrue") - { - Expires = oneHourFromNow, - NotBefore = oneHourAgo, - ValidationParameters = new TokenValidationParameters - { - LifetimeValidator = (DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters) => true - }, - LifetimeValidationResult = new LifetimeValidationResult(oneHourAgo, oneHourFromNow), - }, new ValidateLifetimeTheoryData("Invalid_ValidationParametersIsNull") { Expires = oneHourFromNow, @@ -126,7 +102,7 @@ public static TheoryData ValidateLifetimeTestCases new ValidateLifetimeTheoryData("Invalid_ExpiresIsNull") { NotBefore = oneHourAgo, - ValidationParameters = new TokenValidationParameters(), + ValidationParameters = new ValidationParameters(), ExpectedException = ExpectedException.SecurityTokenNoExpirationException("IDX10225:"), LifetimeValidationResult = new LifetimeValidationResult( oneHourAgo, @@ -144,7 +120,7 @@ public static TheoryData ValidateLifetimeTestCases { Expires = oneHourAgo, NotBefore = oneHourFromNow, - ValidationParameters = new TokenValidationParameters(), + ValidationParameters = new ValidationParameters(), ExpectedException = ExpectedException.SecurityTokenInvalidLifetimeException("IDX10224:"), LifetimeValidationResult = new LifetimeValidationResult( oneHourFromNow, // notBefore @@ -163,7 +139,7 @@ public static TheoryData ValidateLifetimeTestCases { Expires = twoHoursFromNow, NotBefore = oneHourFromNow, - ValidationParameters = new TokenValidationParameters(), + ValidationParameters = new ValidationParameters(), ExpectedException = ExpectedException.SecurityTokenNotYetValidException("IDX10222:"), LifetimeValidationResult = new LifetimeValidationResult( oneHourFromNow, @@ -182,7 +158,7 @@ public static TheoryData ValidateLifetimeTestCases { Expires = oneHourAgo, NotBefore = twoHoursAgo, - ValidationParameters = new TokenValidationParameters(), + ValidationParameters = new ValidationParameters(), ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:"), LifetimeValidationResult = new LifetimeValidationResult( twoHoursAgo, @@ -201,7 +177,7 @@ public static TheoryData ValidateLifetimeTestCases { Expires = oneHourFromNow, NotBefore = sixMinutesFromNow, - ValidationParameters = new TokenValidationParameters { ClockSkew = TimeSpan.FromMinutes(5) }, + ValidationParameters = new ValidationParameters { ClockSkew = TimeSpan.FromMinutes(5) }, ExpectedException = ExpectedException.SecurityTokenNotYetValidException("IDX10222:"), LifetimeValidationResult = new LifetimeValidationResult( sixMinutesFromNow, @@ -220,7 +196,7 @@ public static TheoryData ValidateLifetimeTestCases { Expires = sixMinutesAgo, NotBefore = twoHoursAgo, - ValidationParameters = new TokenValidationParameters { ClockSkew = TimeSpan.FromMinutes(5) }, + ValidationParameters = new ValidationParameters { ClockSkew = TimeSpan.FromMinutes(5) }, ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:"), LifetimeValidationResult = new LifetimeValidationResult( twoHoursAgo, @@ -234,49 +210,7 @@ public static TheoryData ValidateLifetimeTestCases typeof(SecurityTokenExpiredException), new StackFrame(true), null)), - }, - new ValidateLifetimeTheoryData("Invalid_DelegateReturnsFalse") - { - Expires = oneHourFromNow, - NotBefore = oneHourAgo, - ValidationParameters = new TokenValidationParameters - { - LifetimeValidator = (DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters) => false - }, - ExpectedException = ExpectedException.SecurityTokenInvalidLifetimeException("IDX10230:"), - LifetimeValidationResult = new LifetimeValidationResult( - oneHourAgo, - oneHourFromNow, - ValidationFailureType.LifetimeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10230, - "null"), - typeof(SecurityTokenInvalidLifetimeException), - new StackFrame(true), - null)), - }, - new ValidateLifetimeTheoryData("Invalid_DelegateThrows") - { - Expires = oneHourFromNow, - NotBefore = oneHourAgo, - ValidationParameters = new TokenValidationParameters - { - LifetimeValidator = (DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters) => throw new SecurityTokenInvalidLifetimeException() - }, - ExpectedException = ExpectedException.SecurityTokenInvalidLifetimeException("IDX10230:", innerTypeExpected: typeof(SecurityTokenInvalidLifetimeException)), - LifetimeValidationResult = new LifetimeValidationResult( - oneHourAgo, - oneHourFromNow, - ValidationFailureType.LifetimeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10230, - "null"), - typeof(SecurityTokenInvalidLifetimeException), - new StackFrame(true), - null)), - }, + } }; } } @@ -294,7 +228,7 @@ public ValidateLifetimeTheoryData(string testId) : base(testId) public SecurityToken SecurityToken { get; set; } - public TokenValidationParameters ValidationParameters { get; set; } + internal ValidationParameters ValidationParameters { get; set; } internal LifetimeValidationResult LifetimeValidationResult { get; set; } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ReplayValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ReplayValidationResultTests.cs index 8f89ccf466..9e97a7ba65 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ReplayValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ReplayValidationResultTests.cs @@ -51,10 +51,9 @@ public static TheoryData TokenReplayValidationTestCases TestId = "Valid_ReplayCache_Null", ExpirationTime = oneHourAgo, SecurityToken = "token", - ValidationParameters = new TokenValidationParameters + ValidationParameters = new ValidationParameters { - TokenReplayCache = null, - ValidateTokenReplay = true + TokenReplayCache = null }, ReplayValidationResult = new ReplayValidationResult(oneHourAgo) }, @@ -63,38 +62,9 @@ public static TheoryData TokenReplayValidationTestCases TestId = "Valid_ReplayCache_NotNull", ExpirationTime = oneHourFromNow, SecurityToken = "token", - ValidationParameters = new TokenValidationParameters + ValidationParameters = new ValidationParameters { TokenReplayCache = new TokenReplayCache { OnAddReturnValue = true, OnFindReturnValue = false }, - ValidateTokenReplay = true - }, - ReplayValidationResult = new ReplayValidationResult(oneHourFromNow) - }, - new TokenReplayTheoryData - { - TestId = "Valid_ValidateTokenReplay_False", - ExpirationTime = oneHourFromNow, - SecurityToken = "token", - ValidationParameters = new TokenValidationParameters - { - TokenReplayCache = new TokenReplayCache - { - OnAddReturnValue = true, - OnFindReturnValue = true // token already exists in cache, if ValidateTokenReplay were true, this would fail. - }, - ValidateTokenReplay = false - }, - ReplayValidationResult = new ReplayValidationResult(oneHourFromNow) - }, - new TokenReplayTheoryData - { - TestId = "Valid_DelegateIsSet_ReturnsTrue", - ExpirationTime = oneHourFromNow, - SecurityToken = "token", - ValidationParameters = new TokenValidationParameters - { - TokenReplayValidator = (token, expirationTime, validationParameters) => true, - ValidateTokenReplay = true }, ReplayValidationResult = new ReplayValidationResult(oneHourFromNow) }, @@ -103,10 +73,7 @@ public static TheoryData TokenReplayValidationTestCases TestId = "Invalid_SecurityToken_Null", ExpirationTime = now, SecurityToken = null, - ValidationParameters = new TokenValidationParameters - { - ValidateTokenReplay = true - }, + ValidationParameters = new ValidationParameters(), ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), ReplayValidationResult = new ReplayValidationResult( now, @@ -124,10 +91,7 @@ public static TheoryData TokenReplayValidationTestCases TestId = "Invalid_SecurityToken_Empty", ExpirationTime = now, SecurityToken = string.Empty, - ValidationParameters = new TokenValidationParameters - { - ValidateTokenReplay = true - }, + ValidationParameters = new ValidationParameters(), ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), ReplayValidationResult = new ReplayValidationResult( now, @@ -159,62 +123,17 @@ public static TheoryData TokenReplayValidationTestCases null)) }, new TokenReplayTheoryData - { - TestId = "Invalid_DelegateIsSet_ReturnsFalse", - ExpirationTime = now, - SecurityToken = "token", - ValidationParameters = new TokenValidationParameters - { - TokenReplayValidator = (token, expirationTime, validationParameters) => false, - ValidateTokenReplay = true - }, - ExpectedException = ExpectedException.SecurityTokenReplayDetected("IDX10228"), - ReplayValidationResult = new ReplayValidationResult( - now, - ValidationFailureType.TokenReplayValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10228, - LogHelper.MarkAsUnsafeSecurityArtifact("token", t => t.ToString())), - typeof(SecurityTokenReplayDetectedException), - new StackFrame(), - null)) - }, - new TokenReplayTheoryData - { - TestId = "Invalid_DelegateIsSet_ThrowsException", - ExpirationTime = now, - SecurityToken = "token", - ValidationParameters = new TokenValidationParameters - { - TokenReplayValidator = (token, expirationTime, validationParameters) => throw new SecurityTokenReplayDetectedException(), - ValidateTokenReplay = true - }, - ExpectedException = ExpectedException.SecurityTokenReplayDetected("IDX10228:", innerTypeExpected: typeof(SecurityTokenReplayDetectedException)), - ReplayValidationResult = new ReplayValidationResult( - now, - ValidationFailureType.TokenReplayValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10228, - LogHelper.MarkAsUnsafeSecurityArtifact("token", t => t.ToString())), - typeof(SecurityTokenReplayDetectedException), - new StackFrame(), - new SecurityTokenReplayDetectedException())) - }, - new TokenReplayTheoryData { TestId = "Invalid_ReplayCacheIsPresent_ExpirationTimeIsNull", ExpirationTime = null, SecurityToken = "token", - ValidationParameters = new TokenValidationParameters + ValidationParameters = new ValidationParameters { TokenReplayCache = new TokenReplayCache { OnAddReturnValue = true, OnFindReturnValue = false - }, - ValidateTokenReplay = true + } }, ExpectedException = ExpectedException.SecurityTokenReplayDetected("IDX10227:"), ReplayValidationResult = new ReplayValidationResult( @@ -233,14 +152,13 @@ public static TheoryData TokenReplayValidationTestCases TestId = "Invalid_ReplayCacheIsPresent_TokenIsAlreadyInCache", ExpirationTime = oneHourFromNow, SecurityToken= "token", - ValidationParameters = new TokenValidationParameters + ValidationParameters = new ValidationParameters { TokenReplayCache = new TokenReplayCache { OnAddReturnValue = true, OnFindReturnValue = true }, - ValidateTokenReplay = true }, ExpectedException = ExpectedException.SecurityTokenReplayDetected("IDX10228:"), ReplayValidationResult = new ReplayValidationResult( @@ -259,14 +177,13 @@ public static TheoryData TokenReplayValidationTestCases TestId = "Invalid_ReplayCacheIsPresent_AddingTokenToCacheFails", ExpirationTime = oneHourFromNow, SecurityToken= "token", - ValidationParameters = new TokenValidationParameters + ValidationParameters = new ValidationParameters { TokenReplayCache = new TokenReplayCache { OnAddReturnValue = false, OnFindReturnValue = false - }, - ValidateTokenReplay = true + } }, ExpectedException = ExpectedException.SecurityTokenReplayAddFailed("IDX10229:"), ReplayValidationResult = new ReplayValidationResult( @@ -291,7 +208,7 @@ public class TokenReplayTheoryData : TheoryDataBase public string SecurityToken { get; set; } - public TokenValidationParameters ValidationParameters { get; set; } + internal ValidationParameters ValidationParameters { get; set; } internal ReplayValidationResult ReplayValidationResult { get; set; } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/TokenTypeValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/TokenTypeValidationResultTests.cs index 08433ef3ac..79dd4fce77 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/TokenTypeValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/TokenTypeValidationResultTests.cs @@ -52,7 +52,7 @@ public static TheoryData TokenTypeValidationTestCases TestId = "Valid_DefaultTokenTypeValidation", Type = "JWT", SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, "JWT"), - ValidationParameters = new TokenValidationParameters + ValidationParameters = new ValidationParameters { ValidTypes = validTypesWithJwt }, @@ -93,57 +93,12 @@ public static TheoryData TokenTypeValidationTestCases new StackFrame(true))) }, new TokenTypeTheoryData - { - TestId = "Valid_ValidateTokenTypeUsingDelegate", - Type = "JWT", - SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, "JWT"), - ValidationParameters = new TokenValidationParameters - { - TypeValidator = (Type, SecurityToken, TokenValidationParameters) => "JWT" - }, - TokenTypeValidationResult = new TokenTypeValidationResult("JWT") - }, - new TokenTypeTheoryData - { - TestId = "Invalid_ValidateTokenTypeUsingDelegate", - ExpectedException = ExpectedException.SecurityTokenInvalidTypeException(substringExpected: "IDX10259:", innerTypeExpected: typeof(SecurityTokenInvalidTypeException)), - Type = "JWT", - SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, "JWT"), - ValidationParameters = new TokenValidationParameters - { - TypeValidator = (Type, SecurityToken, TokenValidationParameters) => throw new SecurityTokenInvalidTypeException() - }, - TokenTypeValidationResult = new TokenTypeValidationResult( - "JWT", - ValidationFailureType.TokenTypeValidationFailed, - new ExceptionDetail( - new MessageDetail( - LogMessages.IDX10259, - LogHelper.MarkAsNonPII("TypeValidator"), - LogHelper.MarkAsNonPII("Delegate message")), - typeof(SecurityTokenInvalidTypeException), - new StackFrame(true), - new SecurityTokenInvalidTypeException())) - }, - new TokenTypeTheoryData - { - TestId = "Valid_TokenValidationParametersTypeValidatorAndValidTypesAreNull", - Type = "JWT", - SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, "JWT"), - ValidationParameters = new TokenValidationParameters - { - TypeValidator = null, - ValidTypes = null - }, - TokenTypeValidationResult = new TokenTypeValidationResult("JWT") - }, - new TokenTypeTheoryData { TestId = "Invalid_TokenTypeIsEmpty", ExpectedException = ExpectedException.SecurityTokenInvalidTypeException("IDX10256:"), Type = String.Empty, SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, String.Empty), - ValidationParameters = new TokenValidationParameters + ValidationParameters = new ValidationParameters { ValidTypes = validTypesNoJwt }, @@ -163,7 +118,7 @@ public static TheoryData TokenTypeValidationTestCases ExpectedException = ExpectedException.SecurityTokenInvalidTypeException("IDX10256:"), Type = null, SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, null), - ValidationParameters = new TokenValidationParameters + ValidationParameters = new ValidationParameters { ValidTypes = validTypesNoJwt }, @@ -179,11 +134,11 @@ public static TheoryData TokenTypeValidationTestCases }, new TokenTypeTheoryData { - TestId = "Invalid_TokenValidationParametersValidTypesDoesNotSupportType", + TestId = "Invalid_ValidationParametersValidTypesDoesNotSupportType", ExpectedException = ExpectedException.SecurityTokenInvalidTypeException("IDX10257:"), Type = "JWT", SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, "JWT"), - ValidationParameters = new TokenValidationParameters + ValidationParameters = new ValidationParameters { ValidTypes = validTypesNoJwt }, @@ -208,7 +163,7 @@ public class TokenTypeTheoryData : TheoryDataBase public SecurityToken SecurityToken { get; set; } - public TokenValidationParameters ValidationParameters { get; set; } + internal ValidationParameters ValidationParameters { get; set; } internal TokenTypeValidationResult TokenTypeValidationResult { get; set; } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidationParametersTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidationParametersTests.cs new file mode 100644 index 0000000000..7d1a98de4f --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidationParametersTests.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using Xunit; + +namespace Microsoft.IdentityModel.Tokens.Tests.Validation +{ + public class ValidationParametersTests + { + [Fact] + public void SetValidators_NullValue_ThrowsArgumentNullException() + { + var validationParameters = new ValidationParameters(); + Assert.Throws(() => validationParameters.IssuerValidatorAsync = null); + Assert.Throws(() => validationParameters.TokenReplayValidator = null); + Assert.Throws(() => validationParameters.LifetimeValidator = null); + Assert.Throws(() => validationParameters.TypeValidator = null); + Assert.Throws(() => validationParameters.AudienceValidator = null); + } + + [Fact] + public void ValidTypes_Get_ReturnsValidTokenTypes() + { + var validationParameters = new ValidationParameters(); + var validTokenTypes = new List { "JWT", "SAML" }; + validationParameters.ValidTypes = validTokenTypes; + + var result = validationParameters.ValidTypes; + + Assert.Equal(validTokenTypes, result); + } + + [Fact] + public void ValidTypes_Set_UpdatesValidTokenTypes() + { + var validationParameters = new ValidationParameters(); + var validTokenTypes = new List { "JWT", "SAML" }; + + validationParameters.ValidTypes = validTokenTypes; + + Assert.Equal(validTokenTypes, validationParameters.ValidTypes); + } + + [Fact] + public void ValidTypes_Set_Null_ThrowsArgumentNullException() + { + var validationParameters = new ValidationParameters(); + Assert.Throws(() => validationParameters.ValidTypes = null); + } + } +}