From 9e9a05dd3ba6eaf144f4406dccc66be85bfa13e8 Mon Sep 17 00:00:00 2001 From: ciaozhang Date: Thu, 19 Jan 2023 09:52:00 -0800 Subject: [PATCH] Introdduce a LKG configuration cache to store each valid base configuration instead of a single entry of configuration. --- .../JsonWebTokenHandler.cs | 27 +- .../Configuration/ConfigurationManager.cs | 48 ++- .../LastKnownGoodConfigurationCacheOptions.cs | 50 ++++ .../StaticConfigurationManager.cs | 6 + .../Saml/SamlSecurityTokenHandler.cs | 10 +- .../Saml2/Saml2SecurityTokenHandler.cs | 10 +- .../BaseConfigurationComparer.cs | 40 +++ .../BaseConfigurationManager.cs | 16 + .../EventBasedLRUCache.cs | 6 + .../InternalValidators.cs | 127 +------- .../LogMessages.cs | 10 +- .../TokenUtilities.cs | 52 ++-- .../JwtSecurityTokenHandler.cs | 35 ++- .../JsonWebTokenHandlerTests.cs | 15 +- .../ConfigurationManagerTests.cs | 48 ++- .../MockConfigurationManager.cs | 32 +- .../TokenTheoryData.cs | 3 + .../CreateTokenTheoryData.cs | 40 +++ .../Properties/AssemblyInfo.cs | 2 +- .../Saml2SecurityTokenHandlerTests.cs | 263 +++------------- .../Saml2TheoryData.cs | 4 + .../SamlSecurityTokenHandlerTests.cs | 281 ++++-------------- .../SamlSecurityTokenTests.cs | 5 - .../SamlTestData.cs | 95 ++++++ .../SamlTheoryData.cs | 4 + .../CreateAndValidateTokens.cs | 4 +- .../JwtSecurityTokenHandlerTests.cs | 16 +- .../JwtTestDatasets.cs | 150 +++++++--- 28 files changed, 698 insertions(+), 701 deletions(-) create mode 100644 src/Microsoft.IdentityModel.Protocols/Configuration/LastKnownGoodConfigurationCacheOptions.cs create mode 100644 src/Microsoft.IdentityModel.Tokens/BaseConfigurationComparer.cs create mode 100644 test/Microsoft.IdentityModel.Tokens.Saml.Tests/CreateTokenTheoryData.cs create mode 100644 test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlTestData.cs diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs index 7094ad3194..49b4c84ebf 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs @@ -1230,8 +1230,6 @@ private async Task ValidateTokenAsync(JsonWebToken jsonWe return tokenValidationResult; } - // using 'GetType()' instead of 'is' as SecurityTokenUnableToValidException (and others) extend SecurityTokenInvalidSignatureException - // we want to make sure that the clause for SecurityTokenUnableToValidateException is hit so that the ValidationFailure is checked else if (TokenUtilities.IsRecoverableException(tokenValidationResult.Exception)) { // If we were still unable to validate, attempt to refresh the configuration and validate using it @@ -1258,14 +1256,22 @@ private async Task ValidateTokenAsync(JsonWebToken jsonWe } } - if (TokenUtilities.IsRecoverableConfiguration(validationParameters, currentConfiguration, out currentConfiguration)) + if (validationParameters.ConfigurationManager.UseLastKnownGoodConfiguration) { validationParameters.RefreshBeforeValidation = false; validationParameters.ValidateWithLKG = true; - tokenValidationResult = ValidateToken(jsonWebToken, validationParameters, currentConfiguration); + var recoverableException = tokenValidationResult.Exception; - if (tokenValidationResult.IsValid) - return tokenValidationResult; + foreach (BaseConfiguration lkgConfiguration in validationParameters.ConfigurationManager.GetValidLkgConfigurations()) + { + if (!lkgConfiguration.Equals(currentConfiguration) && TokenUtilities.IsRecoverableConfiguration(jsonWebToken.Kid, currentConfiguration, lkgConfiguration, recoverableException)) + { + tokenValidationResult = ValidateToken(jsonWebToken, validationParameters, lkgConfiguration); + + if (tokenValidationResult.IsValid) + return tokenValidationResult; + } + } } } } @@ -1534,16 +1540,13 @@ private static JsonWebToken ValidateSignature(JsonWebToken jwtToken, TokenValida if (!validationParameters.ValidateSignatureLast) { - InternalValidators.ValidateLifetimeAndIssuerAfterSignatureNotValidatedJwt( + InternalValidators.ValidateAfterSignatureFailed( jwtToken, notBefore, expires, - jwtToken.Kid, + jwtToken.Audiences, validationParameters, - configuration, - exceptionStrings, - numKeysInTokenValidationParameters, - numKeysInConfiguration); + configuration); } } diff --git a/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs b/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs index d737734b88..229f5b8cda 100644 --- a/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs +++ b/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.Protocols.Configuration; using Microsoft.IdentityModel.Tokens; namespace Microsoft.IdentityModel.Protocols @@ -33,7 +34,7 @@ public class ConfigurationManager : BaseConfigurationManager, IConfigurationM /// Static initializer for a new object. Static initializers run before the first instance of the type is created. /// static ConfigurationManager() - { + { } /// @@ -42,7 +43,7 @@ static ConfigurationManager() /// The address to obtain configuration. /// The public ConfigurationManager(string metadataAddress, IConfigurationRetriever configRetriever) - : this(metadataAddress, configRetriever, new HttpDocumentRetriever()) + : this(metadataAddress, configRetriever, new HttpDocumentRetriever(), new LastKnownGoodConfigurationCacheOptions()) { } @@ -53,7 +54,7 @@ public ConfigurationManager(string metadataAddress, IConfigurationRetriever c /// The /// The client to use when obtaining configuration. public ConfigurationManager(string metadataAddress, IConfigurationRetriever configRetriever, HttpClient httpClient) - : this(metadataAddress, configRetriever, new HttpDocumentRetriever(httpClient)) + : this(metadataAddress, configRetriever, new HttpDocumentRetriever(httpClient), new LastKnownGoodConfigurationCacheOptions()) { } @@ -67,6 +68,22 @@ public ConfigurationManager(string metadataAddress, IConfigurationRetriever c /// If 'configRetriever' is null. /// If 'docRetriever' is null. public ConfigurationManager(string metadataAddress, IConfigurationRetriever configRetriever, IDocumentRetriever docRetriever) + : this(metadataAddress, configRetriever, docRetriever, new LastKnownGoodConfigurationCacheOptions()) + { + } + + /// + /// Instantiates a new that manages automatic and controls refreshing on configuration data. + /// + /// The address to obtain configuration. + /// The + /// The that reaches out to obtain the configuration. + /// The + /// If 'metadataAddress' is null or empty. + /// If 'configRetriever' is null. + /// If 'docRetriever' is null. + /// If 'lkgCacheOptions' is null. + public ConfigurationManager(string metadataAddress, IConfigurationRetriever configRetriever, IDocumentRetriever docRetriever, LastKnownGoodConfigurationCacheOptions lkgCacheOptions) { if (string.IsNullOrWhiteSpace(metadataAddress)) throw LogHelper.LogArgumentNullException(nameof(metadataAddress)); @@ -77,10 +94,19 @@ public ConfigurationManager(string metadataAddress, IConfigurationRetriever c if (docRetriever == null) throw LogHelper.LogArgumentNullException(nameof(docRetriever)); + if (lkgCacheOptions == null) + throw LogHelper.LogArgumentNullException(nameof(lkgCacheOptions)); + MetadataAddress = metadataAddress; _docRetriever = docRetriever; _configRetriever = configRetriever; _refreshLock = new SemaphoreSlim(1); + + _lastKnownGoodConfigurationCache = new EventBasedLRUCache( + lkgCacheOptions.LastKnownGoodConfigurationSizeLimit, + TaskCreationOptions.None, + lkgCacheOptions.BaseConfigurationComparer, + true); } /// @@ -92,7 +118,21 @@ public ConfigurationManager(string metadataAddress, IConfigurationRetriever c /// The /// If 'configValidator' is null. public ConfigurationManager(string metadataAddress, IConfigurationRetriever configRetriever, IDocumentRetriever docRetriever, IConfigurationValidator configValidator) - :this(metadataAddress, configRetriever, docRetriever) + : this(metadataAddress, configRetriever, docRetriever, configValidator, new LastKnownGoodConfigurationCacheOptions()) + { + } + + /// + /// Instantiates a new with cinfiguration validator that manages automatic and controls refreshing on configuration data. + /// + /// The address to obtain configuration. + /// The + /// The that reaches out to obtain the configuration. + /// The + /// The + /// If 'configValidator' is null. + public ConfigurationManager(string metadataAddress, IConfigurationRetriever configRetriever, IDocumentRetriever docRetriever, IConfigurationValidator configValidator, LastKnownGoodConfigurationCacheOptions lkgCacheOptions) + : this(metadataAddress, configRetriever, docRetriever, lkgCacheOptions) { if (configValidator == null) throw LogHelper.LogArgumentNullException(nameof(configValidator)); diff --git a/src/Microsoft.IdentityModel.Protocols/Configuration/LastKnownGoodConfigurationCacheOptions.cs b/src/Microsoft.IdentityModel.Protocols/Configuration/LastKnownGoodConfigurationCacheOptions.cs new file mode 100644 index 0000000000..b10f1666d9 --- /dev/null +++ b/src/Microsoft.IdentityModel.Protocols/Configuration/LastKnownGoodConfigurationCacheOptions.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Logging; + +namespace Microsoft.IdentityModel.Protocols.Configuration +{ + /// + /// Specifies the LastKnownGoodConfigurationCacheOptions which can be used to configure the internal LKG configuration cache. + /// See for more details. + /// + public class LastKnownGoodConfigurationCacheOptions + { + private IEqualityComparer _baseConfigurationComparer = new BaseConfigurationComparer(); + private int _lastKnownGoodConfigurationSizeLimit = DefaultLastKnownGoodConfigurationSizeLimit; + + /// + /// 10 is the default size limit of the cache (in number of items) for last known good configuration. + /// + public static readonly int DefaultLastKnownGoodConfigurationSizeLimit = 10; + + /// + /// Gets or sets the BaseConfgiurationComparer that to compare . + /// + public IEqualityComparer BaseConfigurationComparer + { + get { return _baseConfigurationComparer; } + set + { + _baseConfigurationComparer = value ?? throw LogHelper.LogExceptionMessage(new ArgumentNullException(nameof(value))); + } + } + + /// + /// The size limit of the cache (in number of items) for last known good configuration. + /// + public int LastKnownGoodConfigurationSizeLimit + { + get { return _lastKnownGoodConfigurationSizeLimit; } + set + { + _lastKnownGoodConfigurationSizeLimit = (value > 0) ? value : throw LogHelper.LogExceptionMessage(new ArgumentOutOfRangeException(nameof(value))); + } + } + } +} diff --git a/src/Microsoft.IdentityModel.Protocols/Configuration/StaticConfigurationManager.cs b/src/Microsoft.IdentityModel.Protocols/Configuration/StaticConfigurationManager.cs index 1d5f66d777..0ee2b32f71 100644 --- a/src/Microsoft.IdentityModel.Protocols/Configuration/StaticConfigurationManager.cs +++ b/src/Microsoft.IdentityModel.Protocols/Configuration/StaticConfigurationManager.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.Protocols.Configuration; using Microsoft.IdentityModel.Tokens; using System; using System.Threading; @@ -28,6 +29,11 @@ public StaticConfigurationManager(T configuration) throw LogHelper.LogExceptionMessage(new ArgumentNullException(nameof(configuration), LogHelper.FormatInvariant(LogMessages.IDX20000, LogHelper.MarkAsNonPII(nameof(configuration))))); _configuration = configuration; + _lastKnownGoodConfigurationCache = new EventBasedLRUCache( + LastKnownGoodConfigurationCacheOptions.DefaultLastKnownGoodConfigurationSizeLimit, + TaskCreationOptions.None, + new BaseConfigurationComparer(), + true); } /// diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.cs index 6e23ec4f88..065eed3884 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.cs @@ -1084,14 +1084,8 @@ private SamlSecurityToken ValidateSignature(SamlSecurityToken samlToken, string if (keyMatched) throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10514, keysAttempted, samlToken.Assertion.Signature.KeyInfo, exceptionStrings, samlToken))); - if (samlToken.Assertion.Conditions != null) - InternalValidators.ValidateLifetimeAndIssuerAfterSignatureNotValidatedSaml( - samlToken, - samlToken.Assertion.Conditions.NotBefore, - samlToken.Assertion.Conditions.NotOnOrAfter, - samlToken.Assertion.Signature.KeyInfo.ToString(), - validationParameters, - exceptionStrings); + ValidateIssuer(samlToken.Issuer, samlToken, validationParameters); + ValidateConditions(samlToken, validationParameters); } if (keysAttempted.Length > 0) diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.cs index 085747c10f..7f9bd5dd3b 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.cs @@ -466,14 +466,8 @@ private Saml2SecurityToken ValidateSignature(Saml2SecurityToken samlToken, strin if (keyMatched) throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10514, keysAttempted, samlToken.Assertion.Signature.KeyInfo, exceptionStrings, samlToken))); - if (samlToken.Assertion.Conditions != null) - InternalValidators.ValidateLifetimeAndIssuerAfterSignatureNotValidatedSaml( - samlToken, - samlToken.Assertion.Conditions.NotBefore, - samlToken.Assertion.Conditions.NotOnOrAfter, - samlToken.Assertion.Signature.KeyInfo.ToString(), - validationParameters, - exceptionStrings); + ValidateIssuer(samlToken.Issuer, samlToken, validationParameters); + ValidateConditions(samlToken, validationParameters); } if (keysAttempted.Length > 0) diff --git a/src/Microsoft.IdentityModel.Tokens/BaseConfigurationComparer.cs b/src/Microsoft.IdentityModel.Tokens/BaseConfigurationComparer.cs new file mode 100644 index 0000000000..b4578d9ec8 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/BaseConfigurationComparer.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Comparison class for a . + /// + internal class BaseConfigurationComparer : IEqualityComparer + { + public bool Equals(BaseConfiguration config1, BaseConfiguration config2) + { + if (config1 == null && config2 == null) + return true; + else if (config1 == null || config2 == null) + return false; + else if (config1.Issuer == config2.Issuer && config1.SigningKeys.Count == config2.SigningKeys.Count + && !config1.SigningKeys.Select(x => x.InternalId).Except(config2.SigningKeys.Select(x => x.InternalId)).Any()) + return true; + else + return false; + } + + public int GetHashCode(BaseConfiguration config) + { + int defaultHash = string.Empty.GetHashCode(); + int hashCode = defaultHash; + hashCode ^= string.IsNullOrEmpty(config.Issuer) ? defaultHash : config.Issuer.GetHashCode(); + foreach(string internalId in config.SigningKeys.Select(x => x.InternalId)) + { + hashCode ^= internalId.GetHashCode(); + } + + return hashCode; + } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/BaseConfigurationManager.cs b/src/Microsoft.IdentityModel.Tokens/BaseConfigurationManager.cs index 7d49a7421c..27783be087 100644 --- a/src/Microsoft.IdentityModel.Tokens/BaseConfigurationManager.cs +++ b/src/Microsoft.IdentityModel.Tokens/BaseConfigurationManager.cs @@ -3,6 +3,8 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.IdentityModel.Logging; @@ -20,6 +22,8 @@ public abstract class BaseConfigurationManager private BaseConfiguration _lastKnownGoodConfiguration; private DateTime? _lastKnownGoodConfigFirstUse = null; + internal EventBasedLRUCache _lastKnownGoodConfigurationCache; + /// /// Gets or sets the that controls how often an automatic metadata refresh should occur. /// @@ -63,6 +67,15 @@ public virtual Task GetBaseConfigurationAsync(CancellationTok throw new NotImplementedException(); } + /// + /// Gets all valid last known good configurations. + /// + /// A collection of all valid last known good configurations. + internal ICollection GetValidLkgConfigurations() + { + return _lastKnownGoodConfigurationCache.ToArray().Where(x => x.Value.Value > DateTime.UtcNow).Select(x => x.Key).ToArray(); + } + /// /// The last known good configuration or LKG (a configuration retrieved in the past that we were able to successfully validate a token against). /// @@ -76,6 +89,9 @@ public BaseConfiguration LastKnownGoodConfiguration { _lastKnownGoodConfiguration = value ?? throw LogHelper.LogArgumentNullException(nameof(value)); _lastKnownGoodConfigFirstUse = DateTime.UtcNow; + + // LRU cache will remove the expired configuration + _lastKnownGoodConfigurationCache.SetValue(_lastKnownGoodConfiguration, DateTime.UtcNow + LastKnownGoodLifetime, DateTime.UtcNow + LastKnownGoodLifetime); } } diff --git a/src/Microsoft.IdentityModel.Tokens/EventBasedLRUCache.cs b/src/Microsoft.IdentityModel.Tokens/EventBasedLRUCache.cs index 60d362bd26..fa417848d4 100644 --- a/src/Microsoft.IdentityModel.Tokens/EventBasedLRUCache.cs +++ b/src/Microsoft.IdentityModel.Tokens/EventBasedLRUCache.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.IdentityModel.Logging; @@ -471,6 +472,11 @@ private void StartEventQueueTaskIfNotRunning() } } + internal KeyValuePair>[] ToArray() + { + return _map.ToArray(); + } + /// Each time a node gets accessed, it gets moved to the beginning (head) of the list if the _maintainLRU == true public bool TryGetValue(TKey key, out TValue value) { diff --git a/src/Microsoft.IdentityModel.Tokens/InternalValidators.cs b/src/Microsoft.IdentityModel.Tokens/InternalValidators.cs index 46a3820f13..334281a2f7 100644 --- a/src/Microsoft.IdentityModel.Tokens/InternalValidators.cs +++ b/src/Microsoft.IdentityModel.Tokens/InternalValidators.cs @@ -2,9 +2,7 @@ // Licensed under the MIT License. using System; -using System.Text; -using Microsoft.IdentityModel.Logging; -using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; +using System.Collections.Generic; namespace Microsoft.IdentityModel.Tokens { @@ -14,128 +12,19 @@ namespace Microsoft.IdentityModel.Tokens internal static class InternalValidators { /// - /// Called after signature validation has failed. Will always throw an exception. + /// Called after signature validation has failed to avoid a metadata refresh /// - /// - /// If the lifetime and issuer are valid - /// - /// - /// If the lifetime or issuer are invalid - /// - internal static void ValidateLifetimeAndIssuerAfterSignatureNotValidatedJwt( + internal static void ValidateAfterSignatureFailed( SecurityToken securityToken, DateTime? notBefore, DateTime? expires, - string kid, + IEnumerable audiences, TokenValidationParameters validationParameters, - BaseConfiguration configuration, - StringBuilder exceptionStrings, - int numKeysInConfiguration, - int numKeysInTokenValidationParameters) + BaseConfiguration configuration) { - bool validIssuer = false; - bool validLifetime = false; - - try - { - Validators.ValidateLifetime(notBefore, expires, securityToken, validationParameters); - validLifetime = true; - } - catch (Exception) - { - // validLifetime will remain false - } - - try - { - Validators.ValidateIssuer(securityToken.Issuer, securityToken, validationParameters, configuration); - validIssuer = true; - } - catch (Exception) - { - // validIssuer will remain false - } - - if (validLifetime && validIssuer) - throw LogHelper.LogExceptionMessage(new SecurityTokenSignatureKeyNotFoundException(LogHelper.FormatInvariant(TokenLogMessages.IDX10501, - LogHelper.MarkAsNonPII(kid), - LogHelper.MarkAsNonPII(numKeysInTokenValidationParameters), - LogHelper.MarkAsNonPII(numKeysInConfiguration), - exceptionStrings, - securityToken))); - else - { - var validationFailure = ValidationFailure.None; - - if (!validLifetime) - validationFailure |= ValidationFailure.InvalidLifetime; - - if (!validIssuer) - validationFailure |= ValidationFailure.InvalidIssuer; - - throw LogHelper.LogExceptionMessage(new SecurityTokenUnableToValidateException( - validationFailure, - LogHelper.FormatInvariant(TokenLogMessages.IDX10516, - LogHelper.MarkAsNonPII(kid), - LogHelper.MarkAsNonPII(numKeysInTokenValidationParameters), - LogHelper.MarkAsNonPII(numKeysInConfiguration), - exceptionStrings, - securityToken, - LogHelper.MarkAsNonPII(validLifetime), - LogHelper.MarkAsNonPII(validIssuer)))); - } - } - - /// - /// Called after signature validation has failed. Will always throw an exception. - /// - /// - /// If the lifetime and issuer are valid - /// - /// - /// If the lifetime or issuer are invalid - /// - internal static void ValidateLifetimeAndIssuerAfterSignatureNotValidatedSaml(SecurityToken securityToken, DateTime? notBefore, DateTime? expires, string keyInfo, TokenValidationParameters validationParameters, StringBuilder exceptionStrings) - { - bool validIssuer = false; - bool validLifetime = false; - - try - { - Validators.ValidateLifetime(notBefore, expires, securityToken, validationParameters); - validLifetime = true; - } - catch (Exception) - { - // validLifetime will remain false - } - - try - { - Validators.ValidateIssuer(securityToken.Issuer, securityToken, validationParameters); - validIssuer = true; - } - catch (Exception) - { - // validIssuer will remain false - } - - if (validLifetime && validIssuer) - throw LogHelper.LogExceptionMessage(new SecurityTokenSignatureKeyNotFoundException(LogHelper.FormatInvariant(TokenLogMessages.IDX10513, keyInfo, exceptionStrings, securityToken))); - else - { - var validationFailure = ValidationFailure.None; - - if (!validLifetime) - validationFailure |= ValidationFailure.InvalidLifetime; - - if (!validIssuer) - validationFailure |= ValidationFailure.InvalidIssuer; - - throw LogHelper.LogExceptionMessage(new SecurityTokenUnableToValidateException( - validationFailure, - LogHelper.FormatInvariant(TokenLogMessages.IDX10515, keyInfo, exceptionStrings, securityToken, LogHelper.MarkAsNonPII(validLifetime), LogHelper.MarkAsNonPII(validIssuer)))); - } + Validators.ValidateLifetime(notBefore, expires, securityToken, validationParameters); + Validators.ValidateIssuer(securityToken.Issuer, securityToken, validationParameters, configuration); + Validators.ValidateAudience(audiences, securityToken, validationParameters); } } } diff --git a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs index e8f1b33d7c..d5a720d9e8 100644 --- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs @@ -76,13 +76,13 @@ internal static class LogMessages // 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."; - public const string IDX10263 = "IDX10263: Unable to re-validate with ConfigurationManager.LastKnownGoodConfiguration as it is expired."; + //public const string IDX10263 = "IDX10263: Unable to re-validate with ConfigurationManager.LastKnownGoodConfiguration as it is expired."; public const string IDX10264 = "IDX10264: Reading issuer signing keys from validation parameters and configuration."; public const string IDX10265 = "IDX10265: Reading issuer signing keys from configuration."; // 10500 - SignatureValidation public const string IDX10500 = "IDX10500: Signature validation failed. No security keys were provided to validate the signature."; - public const string IDX10501 = "IDX10501: Signature validation failed. Unable to match key: \nkid: '{0}'. \nNumber of keys in TokenValidationParameters: '{1}'. \nNumber of keys in Configuration: '{2}'. \nExceptions caught:\n '{3}'. \ntoken: '{4}'."; + //public const string IDX10501 = "IDX10501: Signature validation failed. Unable to match key: \nkid: '{0}'. \nNumber of keys in TokenValidationParameters: '{1}'. \nNumber of keys in Configuration: '{2}'. \nExceptions caught:\n '{3}'. \ntoken: '{4}'."; public const string IDX10503 = "IDX10503: Signature validation failed. Token does not have a kid. Keys tried: '{0}'. Number of keys in TokenValidationParameters: '{1}'. \nNumber of keys in Configuration: '{2}'. \nExceptions caught:\n '{3}'.\ntoken: '{4}'. See https://aka.ms/IDX10503 for details."; public const string IDX10504 = "IDX10504: Unable to validate signature, token does not have a signature: '{0}'."; public const string IDX10505 = "IDX10505: Signature validation failed. The user defined 'Delegate' specified on TokenValidationParameters returned null when validating token: '{0}'."; @@ -93,10 +93,10 @@ internal static class LogMessages public const string IDX10510 = "IDX10510: Token validation failed. The user defined 'Delegate' set on TokenValidationParameters.TokenReader returned null when reading token: '{0}'."; public const string IDX10511 = "IDX10511: Signature validation failed. Keys tried: '{0}'. \nNumber of keys in TokenValidationParameters: '{1}'. \nNumber of keys in Configuration: '{2}'. \nMatched key was in '{3}'. \nkid: '{4}'. \nExceptions caught:\n '{5}'.\ntoken: '{6}'. See https://aka.ms/IDX10511 for details."; public const string IDX10512 = "IDX10512: Signature validation failed. Token does not have KeyInfo. Keys tried: '{0}'.\nExceptions caught:\n '{1}'.\ntoken: '{2}'."; - public const string IDX10513 = "IDX10513: Signature validation failed. Unable to match key: \nKeyInfo: '{0}'.\nExceptions caught:\n '{1}'. \ntoken: '{2}'."; + //public const string IDX10513 = "IDX10513: Signature validation failed. Unable to match key: \nKeyInfo: '{0}'.\nExceptions caught:\n '{1}'. \ntoken: '{2}'."; public const string IDX10514 = "IDX10514: Signature validation failed. Keys tried: '{0}'. \nKeyInfo: '{1}'. \nExceptions caught:\n '{2}'.\ntoken: '{3}'."; - public const string IDX10515 = "IDX10515: Signature validation failed. Unable to match key: \nKeyInfo: '{0}'.\nExceptions caught:\n '{1}'. \ntoken: '{2}'. Valid Lifetime: '{3}'. Valid Issuer: '{4}'"; - public const string IDX10516 = "IDX10516: Signature validation failed. Unable to match key: \nkid: '{0}'. \nNumber of keys in TokenValidationParameters: '{1}'. \nNumber of keys in Configuration: '{2}'. \nExceptions caught:\n '{3}'. \ntoken: '{4}'. Valid Lifetime: '{5}'. Valid Issuer: '{6}'"; + //public const string IDX10515 = "IDX10515: Signature validation failed. Unable to match key: \nKeyInfo: '{0}'.\nExceptions caught:\n '{1}'. \ntoken: '{2}'. Valid Lifetime: '{3}'. Valid Issuer: '{4}'"; + //public const string IDX10516 = "IDX10516: Signature validation failed. Unable to match key: \nkid: '{0}'. \nNumber of keys in TokenValidationParameters: '{1}'. \nNumber of keys in Configuration: '{2}'. \nExceptions caught:\n '{3}'. \ntoken: '{4}'. Valid Lifetime: '{5}'. Valid Issuer: '{6}'"; // encryption / decryption // public const string IDX10600 = "IDX10600:"; diff --git a/src/Microsoft.IdentityModel.Tokens/TokenUtilities.cs b/src/Microsoft.IdentityModel.Tokens/TokenUtilities.cs index 37775dbb5f..260d21cd76 100644 --- a/src/Microsoft.IdentityModel.Tokens/TokenUtilities.cs +++ b/src/Microsoft.IdentityModel.Tokens/TokenUtilities.cs @@ -187,43 +187,43 @@ internal static IEnumerable MergeClaims(IEnumerable claims, IEnume /// true if the exception is certain types of exceptions otherwise, false. internal static bool IsRecoverableException(Exception exception) { - // using 'GetType()' instead of 'is' as SecurityTokenUnableToValidException (and others) extend SecurityTokenInvalidSignatureException - // we want to make sure that the clause for SecurityTokenUnableToValidateException is hit so that the ValidationFailure is checked - return exception.GetType().Equals(typeof(SecurityTokenInvalidSignatureException)) - || exception is SecurityTokenInvalidSigningKeyException - || exception is SecurityTokenInvalidIssuerException - // we should not try to revalidate with the LKG or request a refresh if the token has an invalid lifetime - || (exception as SecurityTokenUnableToValidateException)?.ValidationFailure != ValidationFailure.InvalidLifetime - || exception is SecurityTokenSignatureKeyNotFoundException; + return exception is SecurityTokenInvalidSignatureException + || exception is SecurityTokenInvalidIssuerException + || exception is SecurityTokenSignatureKeyNotFoundException; } /// /// Check whether the given configuration is recoverable by LKG. /// - /// The to be used for validation. - /// The to check. - /// The updated . + /// The kid from token."/> + /// The to check. + /// The LKG exception to check. + /// The exception to check. /// true if the configuration is recoverable otherwise, false. - internal static bool IsRecoverableConfiguration(TokenValidationParameters validationParameters, BaseConfiguration configuration, out BaseConfiguration currentConfiguration) + internal static bool IsRecoverableConfiguration(string kid, BaseConfiguration currentConfiguration, BaseConfiguration lkgConfiguration, Exception currentException) { - bool isRecoverableConfiguration = (validationParameters.ConfigurationManager.UseLastKnownGoodConfiguration - && validationParameters.ConfigurationManager.LastKnownGoodConfiguration != null - && !ReferenceEquals(configuration, validationParameters.ConfigurationManager.LastKnownGoodConfiguration)); + Lazy isRecoverableIssuer = new Lazy(() => currentConfiguration.Issuer != lkgConfiguration.Issuer); + Lazy isRecoverableSigningKey = new Lazy(() => lkgConfiguration.SigningKeys.Any(signingKey => signingKey.KeyId == kid)); - currentConfiguration = configuration; - if (isRecoverableConfiguration) + if (currentException is SecurityTokenInvalidIssuerException) { - // Inform the user that the LKG is expired. - if (!validationParameters.ConfigurationManager.IsLastKnownGoodValid) - { - LogHelper.LogInformation(TokenLogMessages.IDX10263); - return false; - } - else - currentConfiguration = validationParameters.ConfigurationManager.LastKnownGoodConfiguration; + return isRecoverableIssuer.Value; + } + else if (currentException is SecurityTokenSignatureKeyNotFoundException) + { + return isRecoverableSigningKey.Value; + } + else if (currentException is SecurityTokenInvalidSignatureException) + { + SecurityKey currentSigningKey = currentConfiguration.SigningKeys.FirstOrDefault(x => x.KeyId == kid); + if (currentSigningKey == null) + return isRecoverableSigningKey.Value; + + SecurityKey lkgSigningKey = lkgConfiguration.SigningKeys.FirstOrDefault(signingKey => signingKey.KeyId == kid); + return lkgSigningKey != null && currentSigningKey.InternalId != lkgSigningKey.InternalId; } - return isRecoverableConfiguration; + return false; } } } diff --git a/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs b/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs index 16b5bd543e..8da9918013 100644 --- a/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs +++ b/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs @@ -929,15 +929,25 @@ private ClaimsPrincipal ValidateToken(string token, JwtSecurityToken outerToken, } } - if (TokenUtilities.IsRecoverableConfiguration(validationParameters, currentConfiguration, out currentConfiguration)) + if (validationParameters.ConfigurationManager.UseLastKnownGoodConfiguration) { - validationParameters.ValidateWithLKG = true; validationParameters.RefreshBeforeValidation = false; - claimsPrincipal = outerToken != null ? ValidateJWE(token, outerToken, validationParameters, currentConfiguration, out signatureValidatedToken, out exceptionThrown) : - ValidateJWS(token, validationParameters, currentConfiguration, out signatureValidatedToken, out exceptionThrown); + validationParameters.ValidateWithLKG = true; + var recoverableException = exceptionThrown.SourceException; + string kid = outerToken != null ? outerToken.Header.Kid : + (ValidateSignatureUsingDelegates(token, validationParameters, null) ?? GetJwtSecurityTokenFromToken(token, validationParameters)).Header.Kid; + + foreach (BaseConfiguration lkgConfiguration in validationParameters.ConfigurationManager.GetValidLkgConfigurations()) + { + if (!lkgConfiguration.Equals(currentConfiguration) && TokenUtilities.IsRecoverableConfiguration(kid, currentConfiguration, lkgConfiguration, recoverableException)) + { + claimsPrincipal = outerToken != null ? ValidateJWE(token, outerToken, validationParameters, lkgConfiguration, out signatureValidatedToken, out exceptionThrown) : + ValidateJWS(token, validationParameters, lkgConfiguration, out signatureValidatedToken, out exceptionThrown); - if (claimsPrincipal != null) - return claimsPrincipal; + if (claimsPrincipal != null) + return claimsPrincipal; + } + } } } } @@ -1261,10 +1271,6 @@ private static bool ValidateSignature(byte[] encodedBytes, byte[] signature, Sec /// If the has a key identifier and none of the (s) provided result in a validated signature. /// This can indicate that a key refresh is required. /// - /// - /// If the has a key identifier and none of the (s) provided result in a validated signature as well as the token - /// had validation errors or lifetime or issuer. This is not intended to be a signal to refresh keys. - /// /// If after trying all the (s), none result in a validated signature AND the does not have a key identifier. /// A that has the signature validated if token was signed. /// If the is signed, the signature is validated even if is false. @@ -1388,16 +1394,13 @@ private JwtSecurityToken ValidateSignature(string token, JwtSecurityToken jwtTok if (!validationParameters.ValidateSignatureLast) { - InternalValidators.ValidateLifetimeAndIssuerAfterSignatureNotValidatedJwt( + InternalValidators.ValidateAfterSignatureFailed( jwtToken, notBefore, expires, - jwtToken.Header.Kid, + jwtToken.Audiences, validationParameters, - configuration, - exceptionStrings, - numKeysInConfiguration, - numKeysInTokenValidationParameters); + configuration); } } diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs index 79d09a7f7a..bba60f627a 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs @@ -2194,7 +2194,7 @@ public static TheoryData RoundTripJWEDirectTestCases Payload = Default.PayloadString, SigningCredentials = Default.SymmetricSigningCredentials, EncryptingCredentials = Default.SymmetricEncryptingCredentials, - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10516:") + ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10204:") }, new CreateTokenTheoryData() { @@ -2682,7 +2682,7 @@ public static TheoryData ValidateJwsTestCases new JwtTheoryData("SymmetricJws_RequireSignedTokens_KeyNotFound") { Token = Default.SymmetricJws, - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10501"), + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500"), ValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, @@ -2707,7 +2707,7 @@ public static TheoryData ValidateJwsTestCases }, new JwtTheoryData("SymmetricJws_RequireSignedTokensNullSigningKey") { - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10501:"), + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), Token = Default.SymmetricJws, ValidationParameters = new TokenValidationParameters { @@ -3308,7 +3308,7 @@ public static TheoryData SecurityKeyNotFoundExceptionTest IssuerSigningKey = Default.SymmetricSigningKey, ValidIssuer = Default.Issuer }, - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10516:") + ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:") }, new CreateTokenTheoryData { @@ -3322,7 +3322,7 @@ public static TheoryData SecurityKeyNotFoundExceptionTest { IssuerSigningKey = Default.SymmetricSigningKey, }, - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10516:") + ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10204:") }, new CreateTokenTheoryData { @@ -3339,7 +3339,7 @@ public static TheoryData SecurityKeyNotFoundExceptionTest { IssuerSigningKey = Default.SymmetricSigningKey, }, - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10516:") + ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:") }, new CreateTokenTheoryData { @@ -3353,8 +3353,9 @@ public static TheoryData SecurityKeyNotFoundExceptionTest { IssuerSigningKey = Default.SymmetricSigningKey, ValidIssuer = Default.Issuer, + ValidateAudience = false }, - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10501:") + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10503:") }, }; } diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/ConfigurationManagerTests.cs b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/ConfigurationManagerTests.cs index f1627eb53f..1663231d73 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/ConfigurationManagerTests.cs +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/ConfigurationManagerTests.cs @@ -141,11 +141,12 @@ public void GetSets() { TestUtilities.WriteHeader($"{this}.GetSets", "GetSets", true); + int ExpectedPropertyCount = 7; var configManager = new ConfigurationManager("OpenIdConnectMetadata.json", new OpenIdConnectConfigurationRetriever(), new FileDocumentRetriever()); Type type = typeof(ConfigurationManager); PropertyInfo[] properties = type.GetProperties(); - if (properties.Length != 7) - Assert.True(false, "Number of properties has changed from 7 to: " + properties.Length + ", adjust tests"); + if (properties.Length != ExpectedPropertyCount) + Assert.True(false, $"Number of properties has changed from {ExpectedPropertyCount} to: " + properties.Length + ", adjust tests"); var defaultAutomaticRefreshInterval = ConfigurationManager.DefaultAutomaticRefreshInterval; var defaultRefreshInterval = ConfigurationManager.DefaultRefreshInterval; @@ -366,6 +367,49 @@ public void ResetLastKnownGoodLifetime() TestUtilities.AssertFailIfErrors(context); } + [Fact] + public void TestConfigurationComparer() + { + TestUtilities.WriteHeader($"{this}.TestConfigurationComparer", "TestConfigurationComparer", true); + var context = new CompareContext(); + + var config = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer }; + config.SigningKeys.Add(KeyingMaterial.DefaultX509Key_2048); + config.SigningKeys.Add(KeyingMaterial.DefaultRsaSecurityKey1); + config.SigningKeys.Add(KeyingMaterial.DefaultRsaSecurityKey2); + + var configWithSameKeysDiffOrder = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer }; + configWithSameKeysDiffOrder.SigningKeys.Add(KeyingMaterial.DefaultRsaSecurityKey1); + configWithSameKeysDiffOrder.SigningKeys.Add(KeyingMaterial.DefaultX509Key_2048); + configWithSameKeysDiffOrder.SigningKeys.Add(KeyingMaterial.DefaultRsaSecurityKey2); + + var configWithOverlappingKey = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer }; + configWithOverlappingKey.SigningKeys.Add(Default.SymmetricSigningKey256); + + var configWithOverlappingKeyDiffissuer = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer + "1" }; + configWithOverlappingKeyDiffissuer.SigningKeys.Add(Default.SymmetricSigningKey256); + + var configWithSameKidDiffKeyMaterial = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer }; + configWithSameKidDiffKeyMaterial.SigningKeys.Add(new SymmetricSecurityKey(KeyingMaterial.DefaultSymmetricSecurityKey_128.Key) { KeyId = KeyingMaterial.DefaultSymmetricSecurityKey_256.KeyId }); + + var configurationManager = new MockConfigurationManager(config, config); + IdentityComparer.AreEqual(configurationManager.GetValidLkgConfigurations().Count, 1, context); + + configurationManager.LastKnownGoodConfiguration = configWithSameKeysDiffOrder; + IdentityComparer.AreEqual(configurationManager.GetValidLkgConfigurations().Count, 1, context); + + configurationManager.LastKnownGoodConfiguration = configWithOverlappingKey; + IdentityComparer.AreEqual(configurationManager.GetValidLkgConfigurations().Count, 2, context); + + configurationManager.LastKnownGoodConfiguration = configWithOverlappingKeyDiffissuer; + IdentityComparer.AreEqual(configurationManager.GetValidLkgConfigurations().Count, 3, context); + + configurationManager.LastKnownGoodConfiguration = configWithSameKidDiffKeyMaterial; + IdentityComparer.AreEqual(configurationManager.GetValidLkgConfigurations().Count, 4, context); + + TestUtilities.AssertFailIfErrors(context); + } + [Theory, MemberData(nameof(ValidateOpenIdConnectConfigurationTestCases), DisableDiscoveryEnumeration = true)] public void ValidateOpenIdConnectConfigurationTests(ConfigurationManagerTheoryData theoryData) { diff --git a/test/Microsoft.IdentityModel.TestUtils/MockConfigurationManager.cs b/test/Microsoft.IdentityModel.TestUtils/MockConfigurationManager.cs index ee371de0b6..efd0a27a13 100644 --- a/test/Microsoft.IdentityModel.TestUtils/MockConfigurationManager.cs +++ b/test/Microsoft.IdentityModel.TestUtils/MockConfigurationManager.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Protocols; +using Microsoft.IdentityModel.Protocols.Configuration; using Microsoft.IdentityModel.Tokens; namespace Microsoft.IdentityModel.TestUtils @@ -14,7 +15,7 @@ namespace Microsoft.IdentityModel.TestUtils /// This type is used for testing the functionality of using a last known good configuration, as well /// as a refreshed configuration. /// - /// must be a class. + /// must be a class inherit from . public class MockConfigurationManager : BaseConfigurationManager, IConfigurationManager where T : class { private T _configuration; @@ -32,6 +33,11 @@ public MockConfigurationManager(T configuration) throw LogHelper.LogExceptionMessage(new ArgumentNullException(nameof(configuration))); _configuration = configuration; + _lastKnownGoodConfigurationCache = new EventBasedLRUCache( + LastKnownGoodConfigurationCacheOptions.DefaultLastKnownGoodConfigurationSizeLimit, + TaskCreationOptions.None, + new BaseConfigurationComparer(), + true); } /// @@ -74,6 +80,30 @@ public MockConfigurationManager(T configuration, T lkgConfiguration, T refreshed _refreshedConfiguration = refreshedConfiguration; } + /// + /// Initializes an new instance of with a Configuration instance and a LKG Configuration instance. + /// + /// Configuration of type OpenIdConnectConfiguration or WsFederationConfiguration. + /// The LKG configuration lifetime. + public MockConfigurationManager(T configuration, TimeSpan lkgLifetime) : this(configuration) + { + LastKnownGoodLifetime = lkgLifetime; + } + + /// + /// Initializes an new instance of with a Configuration instance and a LKG Configuration instance. + /// + /// Configuration of type OpenIdConnectConfiguration or WsFederationConfiguration. + /// Configuration of type OpenIdConnectConfiguration or WsFederationConfiguration. + /// The LKG configuration lifetime. + public MockConfigurationManager(T configuration, T lkgConfiguration, TimeSpan lkgLifetime) : this(configuration, lkgLifetime) + { + if (lkgConfiguration == null) + throw LogHelper.LogExceptionMessage(new ArgumentNullException(nameof(lkgConfiguration))); + + LastKnownGoodConfiguration = lkgConfiguration as BaseConfiguration; + } + /// /// Obtains an updated version of Configuration. /// diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/TokenTheoryData.cs index 976b4ca5dc..2390c1c8d2 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TokenTheoryData.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TokenTheoryData.cs @@ -12,7 +12,10 @@ public class TokenTheoryData : TheoryDataBase { public TokenTheoryData() { + } + public TokenTheoryData(string testId) : base(testId) + { } public TokenTheoryData(TokenTheoryData other) diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/CreateTokenTheoryData.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/CreateTokenTheoryData.cs new file mode 100644 index 0000000000..6c1ecd9b51 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/CreateTokenTheoryData.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; +using Microsoft.IdentityModel.TestUtils; +using Microsoft.IdentityModel.Tokens.Saml2; + +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant + +namespace Microsoft.IdentityModel.Tokens.Saml.Tests +{ + public class CreateTokenTheoryData : TheoryDataBase + { + public Dictionary AdditionalHeaderClaims { get; set; } + + public string Payload { get; set; } + + public string CompressionAlgorithm { get; set; } + + public CompressionProviderFactory CompressionProviderFactory { get; set; } + + public EncryptingCredentials EncryptingCredentials { get; set; } + + public bool IsValid { get; set; } = true; + + public SigningCredentials SigningCredentials { get; set; } + + public SecurityTokenDescriptor TokenDescriptor { get; set; } + + public SamlSecurityTokenHandler SamlSecurityTokenHandler { get; set; } + + public Saml2SecurityTokenHandler Saml2SecurityTokenHandler { get; set; } + + public string SamlToken { get; set; } + + public TokenValidationParameters ValidationParameters { get; set; } + } +} + +#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Properties/AssemblyInfo.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Properties/AssemblyInfo.cs index d3fea223aa..9822268a7b 100644 --- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Properties/AssemblyInfo.cs +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Properties/AssemblyInfo.cs @@ -4,5 +4,5 @@ using System; using System.Runtime.InteropServices; -[assembly: CLSCompliant(true)] +[assembly: CLSCompliant(false)] [assembly: ComVisible(false)] diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.cs index b444093b3a..7c7915d2f0 100644 --- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.cs @@ -10,8 +10,6 @@ using Microsoft.IdentityModel.Xml; using Xunit; -#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant - namespace Microsoft.IdentityModel.Tokens.Saml2.Tests { public class Saml2SecurityTokenHandlerTests @@ -596,10 +594,8 @@ public static TheoryData ValidateTokenTheoryData return new TheoryData { - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid") { - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_Valid), Token = ReferenceTokens.Saml2Token_Valid, ValidationParameters = new TokenValidationParameters { @@ -609,99 +605,75 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new Saml2TheoryData + new Saml2TheoryData("Null_SecurityToken") { ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - First = true, - Handler = new Saml2SecurityTokenHandler(), - TestId = "Null-SecurityToken", Token = null, ValidationParameters = new TokenValidationParameters() }, - new Saml2TheoryData + new Saml2TheoryData("NULL_TokenValidationParameters") { ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - Handler = new Saml2SecurityTokenHandler(), - TestId = "NULL-TokenValidationParameters", Token = "s", ValidationParameters = null, }, - new Saml2TheoryData + new Saml2TheoryData("SecurityTokenTooLarge") { ExpectedException = ExpectedException.ArgumentException("IDX10209:"), Handler = new Saml2SecurityTokenHandler { MaximumTokenSizeInBytes = 1 }, - TestId = "SecurityTokenTooLarge", Token = "ss", ValidationParameters = new TokenValidationParameters(), }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_MissingVersion") { ExpectedException = new ExpectedException(typeof(Saml2SecurityTokenReadException), "IDX13106:"), - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_MissingVersion), Token = ReferenceTokens.Saml2Token_MissingVersion, ValidationParameters = new TokenValidationParameters(), }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_VersionNotV20") { ExpectedException = new ExpectedException(typeof(Saml2SecurityTokenReadException), "IDX13137:"), - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_VersionNotV20), Token = ReferenceTokens.Saml2Token_VersionNotV20, ValidationParameters = new TokenValidationParameters(), }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_IdMissing") { ExpectedException = new ExpectedException(typeof(Saml2SecurityTokenReadException), "IDX13106:"), - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_IdMissing), Token = ReferenceTokens.Saml2Token_IdMissing, ValidationParameters = new TokenValidationParameters(), }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_IssueInstantMissing") { ExpectedException = new ExpectedException(typeof(Saml2SecurityTokenReadException), "IDX13106:"), - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_IssueInstantMissing), Token = ReferenceTokens.Saml2Token_IssueInstantMissing, ValidationParameters = new TokenValidationParameters(), }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_IssueInstantFormatError") { ExpectedException = new ExpectedException(typeof(Saml2SecurityTokenReadException), "IDX13102:", typeof(FormatException)), - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_IssueInstantFormatError), Token = ReferenceTokens.Saml2Token_IssueInstantFormatError, ValidationParameters = new TokenValidationParameters(), }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_IssuerMissing") { ExpectedException = new ExpectedException(typeof(Saml2SecurityTokenReadException), "IDX13102:", typeof(XmlReadException)), - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_IssuerMissing), Token = ReferenceTokens.Saml2Token_IssuerMissing, ValidationParameters = new TokenValidationParameters(), }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_NoSubjectNoStatements") { ExpectedException = new ExpectedException(typeof(Saml2SecurityTokenReadException), "IDX13108:"), - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_NoSubjectNoStatements), Token = ReferenceTokens.Saml2Token_NoSubjectNoStatements, ValidationParameters = new TokenValidationParameters(), }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_NoAttributes") { ExpectedException = new ExpectedException(typeof(Saml2SecurityTokenReadException), "IDX13138:"), - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_NoAttributes), Token = ReferenceTokens.Saml2Token_NoAttributes, ValidationParameters = new TokenValidationParameters(), }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_IssuerSigningKey_set") { - Handler = new Saml2SecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid)} IssuerSigningKey set", Token = ReferenceTokens.Saml2Token_Valid, ValidationParameters = new TokenValidationParameters { @@ -711,10 +683,8 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_Spaces_Added") { - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_Valid_Spaces_Added), Token = ReferenceTokens.Saml2Token_Valid_Spaces_Added, ValidationParameters = new TokenValidationParameters { @@ -724,21 +694,17 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Formated") { ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10514:"), - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_Formated), Token = ReferenceTokens.Saml2Token_Formated, ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_IssuerSigningKey_Rsa") { - Handler = new Saml2SecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid)} IssuerSigningKey Rsa", Token = ReferenceTokens.Saml2Token_Valid_WithRsaKeyValue, ValidationParameters = new TokenValidationParameters { @@ -748,10 +714,8 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_IssuerSigningKey_JsonWithCertificate") { - Handler = new Saml2SecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid)} IssuerSigningKey JsonWithCertificate", Token = ReferenceTokens.Saml2Token_Valid, ValidationParameters = new TokenValidationParameters { @@ -761,10 +725,8 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_IssuerSigningKey_JsonWithParameters") { - Handler = new Saml2SecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid)} IssuerSigningKey JsonWithParameters", Token = ReferenceTokens.Saml2Token_Valid, ValidationParameters = new TokenValidationParameters { @@ -774,22 +736,18 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_AttributeTampered") { ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10514:"), - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_AttributeTampered), Token = ReferenceTokens.Saml2Token_AttributeTampered, ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_DigestTampered") { ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10514:"), - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_DigestTampered), Token = ReferenceTokens.Saml2Token_DigestTampered, ValidationParameters = new TokenValidationParameters { @@ -797,32 +755,26 @@ public static TheoryData ValidateTokenTheoryData } }, // Removed until we have a way of matching a SecurityKey with a KeyInfo. - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_AttributeTampered_NoKeyMatch") { - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10515:"), - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_AttributeTampered_NoKeyMatch), + ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10204:"), Token = ReferenceTokens.Saml2Token_AttributeTampered_NoKeyMatch, ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_SignatureTampered") { ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10514:"), - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_SignatureTampered), Token = ReferenceTokens.Saml2Token_SignatureTampered, ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_SignatureMissing") { - Handler = new Saml2SecurityTokenHandler(), - TestId = nameof(ReferenceTokens.Saml2Token_SignatureMissing), Token = ReferenceTokens.Saml2Token_SignatureMissing, ValidationParameters = new TokenValidationParameters { @@ -833,10 +785,8 @@ public static TheoryData ValidateTokenTheoryData RequireSignedTokens = false, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_Issuer_SigningKeyResolver") { - Handler = new Saml2SecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid)}IssuerSigningKeyResolver", Token = ReferenceTokens.Saml2Token_Valid, ValidationParameters = new TokenValidationParameters { @@ -846,10 +796,8 @@ public static TheoryData ValidateTokenTheoryData IssuerSigningKeyResolver = (token, securityToken, keyIdentifier, tvp) => { return new List { KeyingMaterial.DefaultAADSigningKey }; }, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_RequireSignedTokens") { - Handler = new Saml2SecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid)}RequireSignedTokens", Token = ReferenceTokens.Saml2Token_Valid, ValidationParameters = new TokenValidationParameters { @@ -861,11 +809,9 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_RequireSignedTokensNullSigningKey") { - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10513:"), - Handler = new Saml2SecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid)}RequireSignedTokensNullSigningKey", + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), Token = ReferenceTokens.Saml2Token_Valid, ValidationParameters = new TokenValidationParameters { @@ -877,10 +823,8 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_DontRequireSignedTokens") { - Handler = new Saml2SecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid)}DontRequireSignedTokens", Token = ReferenceTokens.Saml2Token_Valid, ValidationParameters = new TokenValidationParameters { @@ -892,10 +836,8 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_SignatureMissing_DontRequireSignedTokensNullSigningKey") { - Handler = new Saml2SecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.Saml2Token_SignatureMissing)}DontRequireSignedTokensNullSigningKey", Token = ReferenceTokens.Saml2Token_SignatureMissing, ValidationParameters = new TokenValidationParameters { @@ -907,12 +849,11 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_NoAudienceRestrictions_NoSignature_RequireAudienceTrue") { Audiences = new List(), Token = ReferenceTokens.Saml2Token_NoAudienceRestrictions_NoSignature, ExpectedException = new ExpectedException(typeof(Saml2SecurityTokenException), "IDX13002:"), - TestId = $"{nameof(ReferenceTokens.Saml2Token_NoAudienceRestrictions_NoSignature)}RequireAudienceTrue", ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, @@ -922,11 +863,10 @@ public static TheoryData ValidateTokenTheoryData RequireSignedTokens = false } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_NoAudienceRestrictions_NoSignature_RequireAudienceFalse") { Audiences = new List(), Token = ReferenceTokens.Saml2Token_NoAudienceRestrictions_NoSignature, - TestId = $"{nameof(ReferenceTokens.Saml2Token_NoAudienceRestrictions_NoSignature)}RequireAudienceFalse", ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, @@ -937,12 +877,11 @@ public static TheoryData ValidateTokenTheoryData RequireSignedTokens = false }, }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_NoAudienceRestrictions_NoSignature_RequireAudienceFalseValidateLifetimeTrue") { Audiences = new List(), Token = ReferenceTokens.Saml2Token_NoAudienceRestrictions_NoSignature, ExpectedException = new ExpectedException(typeof(SecurityTokenExpiredException), "IDX10223:"), - TestId = $"{nameof(ReferenceTokens.Saml2Token_NoAudienceRestrictions_NoSignature)}RequireAudienceFalseValidateLifetimeTrue", ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, @@ -953,12 +892,11 @@ public static TheoryData ValidateTokenTheoryData RequireSignedTokens = false }, }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_NoConditions_NoSignature_RequireAudienceTrue") { Audiences = new List(), Token = ReferenceTokens.Saml2Token_NoConditions_NoSignature, ExpectedException = new ExpectedException(typeof(Saml2SecurityTokenException), "IDX13002:"), - TestId = $"{nameof(ReferenceTokens.Saml2Token_NoConditions_NoSignature)}RequireAudienceTrue", ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, @@ -968,11 +906,10 @@ public static TheoryData ValidateTokenTheoryData RequireSignedTokens = false } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_NoConditions_NoSignature_RequireAudienceFalse") { Audiences = new List(), Token = ReferenceTokens.Saml2Token_NoConditions_NoSignature, - TestId = $"{nameof(ReferenceTokens.Saml2Token_NoConditions_NoSignature)}RequireAudienceFalse", ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, @@ -983,11 +920,9 @@ public static TheoryData ValidateTokenTheoryData RequireSignedTokens = false }, }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_AttributeTampered_NoKeyMatch_NotTryAllIssuerSigningKeys") { - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10515:"), - Handler = new Saml2SecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.Saml2Token_AttributeTampered_NoKeyMatch)}NotTryAllIssuerSigningKeys", + ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10204:"), Token = ReferenceTokens.Saml2Token_AttributeTampered_NoKeyMatch, ValidationParameters = new TokenValidationParameters { @@ -995,9 +930,8 @@ public static TheoryData ValidateTokenTheoryData TryAllIssuerSigningKeys = false } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_SpecifyAlgorithm_AlgorithnInList") { - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid)}_SpecifyAlgorithm_AlgorithnInList", Token = ReferenceTokens.Saml2Token_Valid, ValidationParameters = new TokenValidationParameters { @@ -1008,9 +942,8 @@ public static TheoryData ValidateTokenTheoryData ValidAlgorithms = new List { SecurityAlgorithms.RsaSha256Signature } } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_SpecifyAlgorithm_EmptyList") { - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid)}_SpecifyAlgorithm_EmptyList", Token = ReferenceTokens.Saml2Token_Valid, ValidationParameters = new TokenValidationParameters { @@ -1021,9 +954,8 @@ public static TheoryData ValidateTokenTheoryData ValidAlgorithms = new List() } }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_SpecifyAlgorithm_AlgorithnNotList") { - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid)}_SpecifyAlgorithm_AlgorithnNotList", Token = ReferenceTokens.Saml2Token_Valid, ValidationParameters = new TokenValidationParameters { @@ -1035,9 +967,8 @@ public static TheoryData ValidateTokenTheoryData }, ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10514") }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_SpecifyAlgorithm_AlgorithmValidationFails") { - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid)}_SpecifyAlgorithm_AlgorithmValidationFails", Token = ReferenceTokens.Saml2Token_Valid, ValidationParameters = new TokenValidationParameters { @@ -1049,9 +980,8 @@ public static TheoryData ValidateTokenTheoryData }, ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10514") }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_SpecifyAlgorithm_AlgorithmValidationValidates") { - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid)}_SpecifyAlgorithm_AlgorithmValidationValidates", Token = ReferenceTokens.Saml2Token_Valid, ValidationParameters = new TokenValidationParameters { @@ -1062,9 +992,8 @@ public static TheoryData ValidateTokenTheoryData AlgorithmValidator = ValidationDelegates.AlgorithmValidatorBuilder(true) }, }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_WithNoKeyInfo_NullSigningKey") { - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid_WithNoKeyInfo)}NullSigningKey", Token = ReferenceTokens.Saml2Token_Valid_WithNoKeyInfo, ValidationParameters = new TokenValidationParameters { @@ -1077,9 +1006,8 @@ public static TheoryData ValidateTokenTheoryData }, ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:") }, - new Saml2TheoryData + new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_WithNoKeyInfo_NotNullSigningKey") { - TestId = $"{nameof(ReferenceTokens.Saml2Token_Valid_WithNoKeyInfo)}NotNullSigningKey", Token = ReferenceTokens.Saml2Token_Valid_WithNoKeyInfo, ValidationParameters = new TokenValidationParameters { @@ -1326,7 +1254,8 @@ public void Saml2SecurityKeyNotFoundExceptionTest(CreateTokenTheoryData theoryDa { var handler = new Saml2SecurityTokenHandler(); var token = handler.CreateToken(theoryData.TokenDescriptor); - handler.ValidateToken(handler.WriteToken(token), theoryData.ValidationParameters, out var validationResult); + string samlToken = handler.WriteToken(token); + handler.ValidateToken(samlToken, theoryData.ValidationParameters, out var validationResult); } catch (Exception ex) { @@ -1338,78 +1267,7 @@ public void Saml2SecurityKeyNotFoundExceptionTest(CreateTokenTheoryData theoryDa public static TheoryData SecurityKeyNotFoundExceptionTestTheoryData() { - return new TheoryData() - { - new CreateTokenTheoryData - { - First = true, - TestId = "TokenExpired", - TokenDescriptor = new SecurityTokenDescriptor - { - Subject = new ClaimsIdentity(Default.SamlClaims), - Expires = DateTime.UtcNow.Subtract(new TimeSpan(0, 10, 0)), - IssuedAt = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)), - NotBefore = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)), - SigningCredentials = Default.AsymmetricSigningCredentials, - Issuer = Default.Issuer, - }, - ValidationParameters = new TokenValidationParameters - { - IssuerSigningKey = Default.SymmetricSigningKey, - ValidIssuer = Default.Issuer, - }, - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10515:") - }, - new CreateTokenTheoryData - { - TestId = "InvalidIssuer", - TokenDescriptor = new SecurityTokenDescriptor - { - Subject = new ClaimsIdentity(Default.SamlClaims), - SigningCredentials = Default.AsymmetricSigningCredentials, - Issuer = Default.Issuer, - }, - ValidationParameters = new TokenValidationParameters - { - IssuerSigningKey = Default.SymmetricSigningKey, - }, - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10515:") - }, - new CreateTokenTheoryData - { - TestId = "ExpiredAndInvalidIssuer", - TokenDescriptor = new SecurityTokenDescriptor - { - Subject = new ClaimsIdentity(Default.SamlClaims), - Expires = DateTime.UtcNow.Subtract(new TimeSpan(0, 10, 0)), - IssuedAt = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)), - NotBefore = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)), - SigningCredentials = Default.AsymmetricSigningCredentials, - Issuer = Default.Issuer, - }, - ValidationParameters = new TokenValidationParameters - { - IssuerSigningKey = Default.SymmetricSigningKey, - }, - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10515:") - }, - new CreateTokenTheoryData - { - TestId = "KeysDontMatchValidLifetimeAndIssuer", - TokenDescriptor = new SecurityTokenDescriptor - { - Subject = new ClaimsIdentity(Default.SamlClaims), - SigningCredentials = Default.AsymmetricSigningCredentials, - Issuer = Default.Issuer, - }, - ValidationParameters = new TokenValidationParameters - { - IssuerSigningKey = Default.SymmetricSigningKey, - ValidIssuer = Default.Issuer, - }, - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10513:") - } - }; + return SamlTestData.SecurityKeyNotFoundExceptionTestTheoryData(); } } @@ -1448,31 +1306,4 @@ public Saml2SecurityTokenPublic(Saml2Assertion assertion) { } } - - public class CreateTokenTheoryData : TheoryDataBase - { - public Dictionary AdditionalHeaderClaims { get; set; } - - public string Payload { get; set; } - - public string CompressionAlgorithm { get; set; } - - public CompressionProviderFactory CompressionProviderFactory { get; set; } - - public EncryptingCredentials EncryptingCredentials { get; set; } - - public bool IsValid { get; set; } = true; - - public SigningCredentials SigningCredentials { get; set; } - - public SecurityTokenDescriptor TokenDescriptor { get; set; } - - public Saml2SecurityTokenHandler Saml2SecurityTokenHandler { get; set; } - - public string SamlToken { get; set; } - - public TokenValidationParameters ValidationParameters { get; set; } - } } - -#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2TheoryData.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2TheoryData.cs index b33582fc87..3214b67841 100644 --- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2TheoryData.cs +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2TheoryData.cs @@ -13,6 +13,10 @@ public Saml2TheoryData() { } + public Saml2TheoryData(string testId) : base(testId) + { + } + public Saml2TheoryData(TokenTheoryData tokenTheoryData) : base(tokenTheoryData) { diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs index b808b610bb..96bb7ebd79 100644 --- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs @@ -6,6 +6,7 @@ using System.IdentityModel.Tokens.Jwt; using System.IO; using System.Security.Claims; +using System.Security.Cryptography; using System.Xml; using Microsoft.IdentityModel.TestUtils; using Microsoft.IdentityModel.Xml; @@ -327,139 +328,107 @@ public static TheoryData ValidateTokenTheoryData return new TheoryData { - new SamlTheoryData + new SamlTheoryData("Null_SecurityToken") { ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), First = true, - Handler = new SamlSecurityTokenHandler(), - TestId = "Null-SecurityToken", Token = null, ValidationParameters = new TokenValidationParameters() }, - new SamlTheoryData + new SamlTheoryData("NULL_TokenValidationParameters") { ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), - Handler = new SamlSecurityTokenHandler(), - TestId = "NULL-TokenValidationParameters", + Handler = new SamlSecurityTokenHandler { MaximumTokenSizeInBytes = 1 }, Token = "s", ValidationParameters = null, }, - new SamlTheoryData + new SamlTheoryData("SecurityTokenTooLarge") { ExpectedException = ExpectedException.ArgumentException("IDX10209:"), Handler = new SamlSecurityTokenHandler { MaximumTokenSizeInBytes = 1 }, - TestId = "SecurityTokenTooLarge", Token = "ss", ValidationParameters = new TokenValidationParameters(), }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_MissingMajorVersion") { ExpectedException = new ExpectedException(typeof(SamlSecurityTokenReadException), "IDX11115:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_MissingMajorVersion), Token = ReferenceTokens.SamlToken_MissingMajorVersion, ValidationParameters = new TokenValidationParameters(), }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_MissingMinorVersion") { ExpectedException = new ExpectedException(typeof(SamlSecurityTokenReadException), "IDX11115:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_MissingMinorVersion), Token = ReferenceTokens.SamlToken_MissingMinorVersion, ValidationParameters = new TokenValidationParameters(), }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_MajorVersionNotV1") { ExpectedException = new ExpectedException(typeof(SamlSecurityTokenReadException), "IDX11116:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_MajorVersionNotV1), Token = ReferenceTokens.SamlToken_MajorVersionNotV1, ValidationParameters = new TokenValidationParameters(), }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_MinorVersionNotV1") { ExpectedException = new ExpectedException(typeof(SamlSecurityTokenReadException), "IDX11117:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_MinorVersionNotV1), Token = ReferenceTokens.SamlToken_MinorVersionNotV1, ValidationParameters = new TokenValidationParameters(), }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_IdMissing") { ExpectedException = new ExpectedException(typeof(SamlSecurityTokenReadException), "IDX11115:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_IdMissing), Token = ReferenceTokens.SamlToken_IdMissing, ValidationParameters = new TokenValidationParameters(), }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_IdFormatError") { ExpectedException = new ExpectedException(typeof(SamlSecurityTokenReadException), "IDX11121:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_IdFormatError), Token = ReferenceTokens.SamlToken_IdFormatError, ValidationParameters = new TokenValidationParameters(), }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_IssuerMissing") { ExpectedException = new ExpectedException(typeof(SamlSecurityTokenReadException), "IDX11115:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_IssuerMissing), Token = ReferenceTokens.SamlToken_IssuerMissing, ValidationParameters = new TokenValidationParameters(), }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_IssueInstantMissing") { ExpectedException = new ExpectedException(typeof(SamlSecurityTokenReadException), "IDX11115:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_IssueInstantMissing), Token = ReferenceTokens.SamlToken_IssueInstantMissing, ValidationParameters = new TokenValidationParameters(), }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_IssueInstantFormatError") { ExpectedException = new ExpectedException(typeof(SamlSecurityTokenReadException), "IDX11122:", typeof(FormatException)), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_IssueInstantFormatError), Token = ReferenceTokens.SamlToken_IssueInstantFormatError, ValidationParameters = new TokenValidationParameters(), }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_AudienceMissing") { ExpectedException = new ExpectedException(typeof(SamlSecurityTokenReadException), "IDX11120:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_AudienceMissing), Token = ReferenceTokens.SamlToken_AudienceMissing, ValidationParameters = new TokenValidationParameters(), }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_NoStatements") { ExpectedException = new ExpectedException(typeof(SamlSecurityTokenReadException), "IDX11130:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_NoStatements), Token = ReferenceTokens.SamlToken_NoStatements, ValidationParameters = new TokenValidationParameters(), }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_NoSubject") { ExpectedException = new ExpectedException(typeof(SamlSecurityTokenReadException), "IDX11112:", typeof(XmlReadException)), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_NoSubject), Token = ReferenceTokens.SamlToken_NoSubject, ValidationParameters = new TokenValidationParameters(), }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_NoAttributes") { ExpectedException = new ExpectedException(typeof(SamlSecurityTokenReadException), "IDX11131:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_NoAttributes), Token = ReferenceTokens.SamlToken_NoAttributes, ValidationParameters = new TokenValidationParameters(), }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_IssuerSigningKey_Set") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid)} IssuerSigningKey set", Token = ReferenceTokens.SamlToken_Valid, ValidationParameters = new TokenValidationParameters { @@ -469,10 +438,8 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_IssuerSigningKey_Rsa") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid)} IssuerSigningKey Rsa", Token = ReferenceTokens.SamlToken_Valid_WithRsaKeyValue, ValidationParameters = new TokenValidationParameters { @@ -482,10 +449,8 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_IssuerSigningKey_JsonWithCertificate") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid)} IssuerSigningKey JsonWithCertificate", Token = ReferenceTokens.SamlToken_Valid, ValidationParameters = new TokenValidationParameters { @@ -495,10 +460,8 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_IssuerSigningKey_JsonWithParameters") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid)} IssuerSigningKey JsonWithParameters", Token = ReferenceTokens.SamlToken_Valid, ValidationParameters = new TokenValidationParameters { @@ -508,10 +471,8 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_Spaces_Added") { - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_Valid_Spaces_Added), Token = ReferenceTokens.SamlToken_Valid_Spaces_Added, ValidationParameters = new TokenValidationParameters { @@ -521,22 +482,18 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_AttributeTampered") { ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10514:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_AttributeTampered), Token = ReferenceTokens.SamlToken_AttributeTampered, ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_DigestTampered") { ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10514:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_DigestTampered), Token = ReferenceTokens.SamlToken_DigestTampered, ValidationParameters = new TokenValidationParameters { @@ -544,32 +501,26 @@ public static TheoryData ValidateTokenTheoryData } }, // Removed until we have a way of matching a KeyInfo with a SecurityKey. - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid") { - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10515:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_Valid), + ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10204:"), Token = ReferenceTokens.SamlToken_Valid, ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_SignatureTampered") { ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10514:"), - Handler = new SamlSecurityTokenHandler(), - TestId = nameof(ReferenceTokens.SamlToken_SignatureTampered), Token = ReferenceTokens.SamlToken_SignatureTampered, ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_IssuerSigningKeyResolver") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid)}IssuerSigningKeyResolver", Token = ReferenceTokens.SamlToken_Valid, ValidationParameters = new TokenValidationParameters { @@ -579,10 +530,8 @@ public static TheoryData ValidateTokenTheoryData IssuerSigningKeyResolver = (token, securityToken, keyIdentifier, tvp) => { return new List { KeyingMaterial.DefaultJsonWebKeyWithCertificate1 }; }, } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_RequireSignedTokens") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid)}RequireSignedTokens", Token = ReferenceTokens.SamlToken_Valid, ValidationParameters = new TokenValidationParameters { @@ -594,11 +543,9 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_RequireSignedTokensNullSigningKey") { - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10513:"), - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid)}RequireSignedTokensNullSigningKey", + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), Token = ReferenceTokens.SamlToken_Valid, ValidationParameters = new TokenValidationParameters { @@ -610,10 +557,8 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_DontRequireSignedTokens") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid)}DontRequireSignedTokens", Token = ReferenceTokens.SamlToken_Valid, ValidationParameters = new TokenValidationParameters { @@ -625,10 +570,8 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_SignatureMissing_DontRequireSignedTokensNullSigningKey") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_SignatureMissing)}DontRequireSignedTokensNullSigningKey", Token = ReferenceTokens.SamlToken_SignatureMissing, ValidationParameters = new TokenValidationParameters { @@ -640,12 +583,11 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_NoAudienceRestrictions_NoSignature_RequireAudienceTrue") { Audiences = new List(), Token = ReferenceTokens.SamlToken_NoAudienceRestrictions_NoSignature, ExpectedException = new ExpectedException(typeof(SamlSecurityTokenException), "IDX11401:"), - TestId = $"{nameof(ReferenceTokens.SamlToken_NoAudienceRestrictions_NoSignature)}RequireAudienceTrue", ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, @@ -655,11 +597,10 @@ public static TheoryData ValidateTokenTheoryData RequireSignedTokens = false } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_NoAudienceRestrictions_NoSignature_RequireAudienceFalse") { Audiences = new List(), Token = ReferenceTokens.SamlToken_NoAudienceRestrictions_NoSignature, - TestId = $"{nameof(ReferenceTokens.SamlToken_NoAudienceRestrictions_NoSignature)}RequireAudienceFalse", ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, @@ -670,12 +611,11 @@ public static TheoryData ValidateTokenTheoryData RequireSignedTokens = false }, }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_NoAudienceRestrictions_NoSignature_RequireAudienceFalseValidateLifetimeTrue") { Audiences = new List(), Token = ReferenceTokens.SamlToken_NoAudienceRestrictions_NoSignature, ExpectedException = new ExpectedException(typeof(SecurityTokenExpiredException), "IDX10223:"), - TestId = $"{nameof(ReferenceTokens.SamlToken_NoAudienceRestrictions_NoSignature)}RequireAudienceFalseValidateLifetimeTrue", ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, @@ -686,12 +626,11 @@ public static TheoryData ValidateTokenTheoryData RequireSignedTokens = false }, }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_NoConditions_NoSignature_RequireAudienceTrue") { Audiences = new List(), Token = ReferenceTokens.SamlToken_NoConditions_NoSignature, ExpectedException = new ExpectedException(typeof(SamlSecurityTokenException), "IDX11401:"), - TestId = $"{nameof(ReferenceTokens.SamlToken_NoConditions_NoSignature)}RequireAudienceTrue", ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, @@ -701,11 +640,10 @@ public static TheoryData ValidateTokenTheoryData RequireSignedTokens = false } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_NoConditions_NoSignature_RequireAudienceFalse") { Audiences = new List(), Token = ReferenceTokens.SamlToken_NoConditions_NoSignature, - TestId = $"{nameof(ReferenceTokens.SamlToken_NoConditions_NoSignature)}RequireAudienceFalse", ValidationParameters = new TokenValidationParameters { IssuerSigningKey = KeyingMaterial.DefaultAADSigningKey, @@ -716,11 +654,9 @@ public static TheoryData ValidateTokenTheoryData RequireSignedTokens = false }, }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_NotTryAllIssuerSigningKeys") { - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10515:"), - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid)}NotTryAllIssuerSigningKeys", + ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10204:"), Token = ReferenceTokens.SamlToken_Valid, ValidationParameters = new TokenValidationParameters { @@ -728,10 +664,8 @@ public static TheoryData ValidateTokenTheoryData TryAllIssuerSigningKeys = false } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_SpecifyAlgorithm_AlgorithmInList") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid)}_SpecifyAlgorithm_AlgorithnInList", Token = ReferenceTokens.SamlToken_Valid_WithRsaKeyValue, ValidationParameters = new TokenValidationParameters { @@ -742,10 +676,8 @@ public static TheoryData ValidateTokenTheoryData ValidAlgorithms = new List { SecurityAlgorithms.RsaSha256Signature } } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_SpecifyAlgorithm_EmptyList") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid)}_SpecifyAlgorithm_EmptyList", Token = ReferenceTokens.SamlToken_Valid_WithRsaKeyValue, ValidationParameters = new TokenValidationParameters { @@ -756,10 +688,8 @@ public static TheoryData ValidateTokenTheoryData ValidAlgorithms = new List(), } }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_SpecifyAlgorithm_AlgorithnNotList") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid)}_SpecifyAlgorithm_AlgorithnNotList", Token = ReferenceTokens.SamlToken_Valid_WithRsaKeyValue, ValidationParameters = new TokenValidationParameters { @@ -769,12 +699,10 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, ValidAlgorithms = new List { SecurityAlgorithms.RsaSha512Signature } }, - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10513") + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10512") }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_SpecifyAlgorithm_AlgorithmValidationFails") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid)}_SpecifyAlgorithm_AlgorithmValidationFails", Token = ReferenceTokens.SamlToken_Valid_WithRsaKeyValue, ValidationParameters = new TokenValidationParameters { @@ -784,12 +712,10 @@ public static TheoryData ValidateTokenTheoryData ValidateLifetime = false, AlgorithmValidator = ValidationDelegates.AlgorithmValidatorBuilder(false) }, - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10513") + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10512") }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_SpecifyAlgorithm_AlgorithmValidationValidates") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid)}_SpecifyAlgorithm_AlgorithmValidationValidates", Token = ReferenceTokens.SamlToken_Valid_WithRsaKeyValue, ValidationParameters = new TokenValidationParameters { @@ -800,10 +726,8 @@ public static TheoryData ValidateTokenTheoryData AlgorithmValidator = ValidationDelegates.AlgorithmValidatorBuilder(true) }, }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_WithNoKeyInfo_NullSigningKey") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid_WithNoKeyInfo)}NullSigningKey", Token = ReferenceTokens.SamlToken_Valid_WithNoKeyInfo, ValidationParameters = new TokenValidationParameters { @@ -816,10 +740,8 @@ public static TheoryData ValidateTokenTheoryData }, ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:") }, - new SamlTheoryData + new SamlTheoryData("ReferenceTokens_SamlToken_Valid_WithNoKeyInfo_NotNullSigningKey") { - Handler = new SamlSecurityTokenHandler(), - TestId = $"{nameof(ReferenceTokens.SamlToken_Valid_WithNoKeyInfo)}NotNullSigningKey", Token = ReferenceTokens.SamlToken_Valid_WithNoKeyInfo, ValidationParameters = new TokenValidationParameters { @@ -1148,7 +1070,7 @@ public static TheoryData CreateSamlTokenUsingTokenDescrip ValidationParameters = validationParameters }, new CreateTokenTheoryData - { + { TestId = "OnlySubjectClaims", TokenDescriptor = new SecurityTokenDescriptor { @@ -1270,7 +1192,8 @@ public void SamlSecurityKeyNotFoundExceptionTest(CreateTokenTheoryData theoryDat { var handler = new SamlSecurityTokenHandler(); var token = handler.CreateToken(theoryData.TokenDescriptor); - handler.ValidateToken(handler.WriteToken(token), theoryData.ValidationParameters, out var validationResult); + string samlToken = handler.WriteToken(token); + handler.ValidateToken(samlToken, theoryData.ValidationParameters, out var validationResult); } catch (Exception ex) { @@ -1282,78 +1205,7 @@ public void SamlSecurityKeyNotFoundExceptionTest(CreateTokenTheoryData theoryDat public static TheoryData SecurityKeyNotFoundExceptionTestTheoryData() { - return new TheoryData() - { - new CreateTokenTheoryData - { - First = true, - TestId = "TokenExpired", - TokenDescriptor = new SecurityTokenDescriptor - { - Subject = new ClaimsIdentity(Default.SamlClaims), - Expires = DateTime.UtcNow.Subtract(new TimeSpan(0, 10, 0)), - IssuedAt = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)), - NotBefore = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)), - SigningCredentials = Default.AsymmetricSigningCredentials, - Issuer = Default.Issuer, - }, - ValidationParameters = new TokenValidationParameters - { - IssuerSigningKey = Default.SymmetricSigningKey, - ValidIssuer = Default.Issuer, - }, - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10515:") - }, - new CreateTokenTheoryData - { - TestId = "InvalidIssuer", - TokenDescriptor = new SecurityTokenDescriptor - { - Subject = new ClaimsIdentity(Default.SamlClaims), - SigningCredentials = Default.AsymmetricSigningCredentials, - Issuer = Default.Issuer, - }, - ValidationParameters = new TokenValidationParameters - { - IssuerSigningKey = Default.SymmetricSigningKey, - }, - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10515:") - }, - new CreateTokenTheoryData - { - TestId = "ExpiredAndInvalidIssuer", - TokenDescriptor = new SecurityTokenDescriptor - { - Subject = new ClaimsIdentity(Default.SamlClaims), - Expires = DateTime.UtcNow.Subtract(new TimeSpan(0, 10, 0)), - IssuedAt = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)), - NotBefore = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)), - SigningCredentials = Default.AsymmetricSigningCredentials, - Issuer = Default.Issuer, - }, - ValidationParameters = new TokenValidationParameters - { - IssuerSigningKey = Default.SymmetricSigningKey, - }, - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10515:") - }, - new CreateTokenTheoryData - { - TestId = "KeysDontMatchValidLifetimeAndIssuer", - TokenDescriptor = new SecurityTokenDescriptor - { - Subject = new ClaimsIdentity(Default.SamlClaims), - SigningCredentials = Default.AsymmetricSigningCredentials, - Issuer = Default.Issuer, - }, - ValidationParameters = new TokenValidationParameters - { - IssuerSigningKey = Default.SymmetricSigningKey, - ValidIssuer = Default.Issuer, - }, - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10513:") - } - }; + return SamlTestData.SecurityKeyNotFoundExceptionTestTheoryData(); } private class SamlSecurityTokenHandlerPublic : SamlSecurityTokenHandler @@ -1373,31 +1225,6 @@ public string ValidateIssuerPublic(string issuer, SecurityToken token, TokenVali return base.ValidateIssuer(issuer, token, validationParameters); } } - - public class CreateTokenTheoryData : TheoryDataBase - { - public Dictionary AdditionalHeaderClaims { get; set; } - - public string Payload { get; set; } - - public string CompressionAlgorithm { get; set; } - - public CompressionProviderFactory CompressionProviderFactory { get; set; } - - public EncryptingCredentials EncryptingCredentials { get; set; } - - public bool IsValid { get; set; } = true; - - public SigningCredentials SigningCredentials { get; set; } - - public SecurityTokenDescriptor TokenDescriptor { get; set; } - - public SamlSecurityTokenHandler SamlSecurityTokenHandler { get; set; } - - public string SamlToken { get; set; } - - public TokenValidationParameters ValidationParameters { get; set; } - } } } diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenTests.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenTests.cs index 68db35e572..fce3c79d9a 100644 --- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenTests.cs @@ -28,11 +28,6 @@ public void Defaults() if (!token.Assertion.Issuer.Equals(ClaimsIdentity.DefaultIssuer)) context.Diffs.Add("token.Assertion.Issuer.Equals(ClaimsIdentity.DefaultIssuer)"); - // It's possible that DateTime.UtcNow will be slightly different from token.Assertion.IssueInstant, so we can't compare them directly. - var timeDiff = DateTime.UtcNow.Subtract(token.Assertion.IssueInstant).TotalMilliseconds; - if (Math.Abs(timeDiff) >= 100) - context.Diffs.Add("Math.Abs(DateTime.UtcNow.Subtract(token.Assertion.IssueInstant).TotalMilliseconds) >= 100"); - if (token.Assertion.Conditions == null) context.Diffs.Add("token.Assertion.Conditions == null"); diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlTestData.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlTestData.cs new file mode 100644 index 0000000000..0244bbd194 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlTestData.cs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Security.Claims; +using Microsoft.IdentityModel.TestUtils; +using Xunit; + +namespace Microsoft.IdentityModel.Tokens.Saml.Tests +{ + public class SamlTestData + { + public static TheoryData SecurityKeyNotFoundExceptionTestTheoryData() + { + return new TheoryData() + { + new CreateTokenTheoryData + { + First = true, + TestId = "TokenExpired", + TokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(Default.SamlClaims), + Expires = DateTime.UtcNow.Subtract(new TimeSpan(0, 10, 0)), + IssuedAt = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)), + NotBefore = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)), + SigningCredentials = Default.AsymmetricSigningCredentials, + Issuer = Default.Issuer, + }, + ValidationParameters = new TokenValidationParameters + { + IssuerSigningKey = Default.SymmetricSigningKey, + ValidIssuer = Default.Issuer, + }, + ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:") + }, + new CreateTokenTheoryData + { + TestId = "InvalidIssuer", + TokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(Default.SamlClaims), + SigningCredentials = Default.AsymmetricSigningCredentials, + Issuer = Default.Issuer, + }, + ValidationParameters = new TokenValidationParameters + { + IssuerSigningKey = Default.SymmetricSigningKey, + }, + ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10204:") + }, + new CreateTokenTheoryData + { + TestId = "ExpiredAndInvalidIssuer", + TokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(Default.SamlClaims), + Expires = DateTime.UtcNow.Subtract(new TimeSpan(0, 10, 0)), + IssuedAt = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)), + NotBefore = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)), + SigningCredentials = Default.AsymmetricSigningCredentials, + Issuer = Default.Issuer, + }, + ValidationParameters = new TokenValidationParameters + { + IssuerSigningKey = Default.SymmetricSigningKey, + ValidateIssuer = false, + ValidateAudience = false + }, + ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:") + }, + new CreateTokenTheoryData + { + TestId = "KeysDontMatchValidLifetimeAndIssuer", + TokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(Default.SamlClaims), + SigningCredentials = Default.AsymmetricSigningCredentials, + Issuer = Default.Issuer, + }, + ValidationParameters = new TokenValidationParameters + { + IssuerSigningKey = KeyingMaterial.X509SecurityKey_AAD_Public, + ValidIssuer = Default.Issuer, + ValidateIssuer = false, + ValidateAudience = false, + RequireAudience = false, + ValidateLifetime = false + }, + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10512:") + } + }; + } + } +} diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlTheoryData.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlTheoryData.cs index 2dce9a66b8..ce72e87a6f 100644 --- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlTheoryData.cs +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlTheoryData.cs @@ -12,6 +12,10 @@ public SamlTheoryData() { } + public SamlTheoryData(string testId) : base(testId) + { + } + public SamlTheoryData(TokenTheoryData tokenTheoryData) : base(tokenTheoryData) { diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/CreateAndValidateTokens.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/CreateAndValidateTokens.cs index 8363ba624a..b27a188ddb 100644 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/CreateAndValidateTokens.cs +++ b/test/System.IdentityModel.Tokens.Jwt.Tests/CreateAndValidateTokens.cs @@ -836,7 +836,7 @@ public static TheoryData ActorTheoryData new JwtTheoryData { TestId = "ActorValidationUsingActorTVP - ExceptionExpected", - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10501"), + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10503"), Token = handler.CreateEncodedJwt(Default.Issuer, Default.Audience, claimsIdentity, null, null, null, Default.AsymmetricSigningCredentials), TokenHandler = handler, ValidationParameters = validationParameters @@ -472,7 +472,7 @@ public static TheoryData ActorTheoryData new JwtTheoryData { TestId = "ActorValidationUsingTVP - ExceptionExpected", - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10501"), + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10503"), Token = handler.CreateEncodedJwt(Default.Issuer, Default.Audience, claimsIdentity, null, null, null, Default.AsymmetricSigningCredentials), TokenHandler = handler, ValidationParameters = validationParameters @@ -493,7 +493,7 @@ public static TheoryData ActorTheoryData new JwtTheoryData { TestId = "ActorValidationUsingTVP - NotTryingAllIssuerSigningKeys - ExceptionExpected", - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10501"), + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500"), Token = handler.CreateEncodedJwt(Default.Issuer, Default.Audience, claimsIdentity, null, null, null, Default.AsymmetricSigningCredentials), TokenHandler = handler, ValidationParameters = validationParameters @@ -2228,7 +2228,7 @@ public static TheoryData ValidateTokenTheoryData }, new JwtTheoryData { - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10501:"), + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), TestId = nameof(Default.SymmetricJws) + "_" + "RequireSignedTokensNullSigningKey", Token = Default.SymmetricJws, ValidationParameters = new TokenValidationParameters @@ -2794,7 +2794,7 @@ public static TheoryData SecurityKeyNotFoundExceptionTest IssuerSigningKey = Default.SymmetricSigningKey, ValidIssuer = Default.Issuer }, - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10516:") + ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:") }, new CreateTokenTheoryData { @@ -2808,7 +2808,7 @@ public static TheoryData SecurityKeyNotFoundExceptionTest { IssuerSigningKey = Default.SymmetricSigningKey, }, - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10516:") + ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10204:") }, new CreateTokenTheoryData { @@ -2825,7 +2825,7 @@ public static TheoryData SecurityKeyNotFoundExceptionTest { IssuerSigningKey = Default.SymmetricSigningKey, }, - ExpectedException = ExpectedException.SecurityTokenUnableToValidateException("IDX10516:") + ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:") }, new CreateTokenTheoryData { @@ -2840,7 +2840,7 @@ public static TheoryData SecurityKeyNotFoundExceptionTest IssuerSigningKey = Default.SymmetricSigningKey, ValidIssuer = Default.Issuer, }, - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10501:") + ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10208:") }, }; } diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTestDatasets.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTestDatasets.cs index c420e48dee..81fe3b0bd4 100644 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTestDatasets.cs +++ b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTestDatasets.cs @@ -143,7 +143,7 @@ public static List ValidateJwsWithConfigTheoryData ValidateAudience = false, ValidateLifetime = false, }, - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10501: "), + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10503: "), }, new JwtTheoryData { @@ -205,7 +205,7 @@ public static List ValidateJwsWithConfigTheoryData ValidateAudience = false, ValidateLifetime = false, }, - ExpectedException = new ExpectedException(typeof(SecurityTokenUnableToValidateException), "IDX10516: ") + ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidIssuerException), "IDX10204: ") }, new JwtTheoryData { @@ -279,26 +279,32 @@ public static TheoryData ValidateJwsWithLastKnownGoodTheoryData { get { - var validConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "oauth/token", Issuer = Default.Issuer }; + var validConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer }; validConfig.SigningKeys.Add(KeyingMaterial.DefaultX509Key_2048); // a special IssuerSigningKeyValidator in the tests below is set to fail if this configuration is used in order // to mock issuer signing key validation failure - var validConfigKeyValidationFails = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "oauth/token", Issuer = Default.Issuer }; + var validConfigKeyValidationFails = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer }; validConfigKeyValidationFails.SigningKeys.Add(KeyingMaterial.DefaultX509Key_2048); - var invalidIssuerConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "oauth/token", Issuer = Default.Issuer + "2" }; + var invalidIssuerConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer + "1" }; invalidIssuerConfig.SigningKeys.Add(KeyingMaterial.DefaultX509Key_2048); var incorrectSigningKeysConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "oauth/token", Issuer = Default.Issuer }; incorrectSigningKeysConfig.SigningKeys.Add(KeyingMaterial.X509SecurityKey2); - var incorrectIssuerAndSigningKeysConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "oauth/token", Issuer = Default.Issuer + "2" }; + var incorrectIssuerAndSigningKeysConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer + "1" }; incorrectIssuerAndSigningKeysConfig.SigningKeys.Add(KeyingMaterial.X509SecurityKey2); - var incorrectSigningKeysConfigWithMatchingKid = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "oauth/token", Issuer = Default.Issuer }; + var incorrectIssuerAndSigningKeysConfig2 = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer + "2" }; + incorrectIssuerAndSigningKeysConfig.SigningKeys.Add(KeyingMaterial.X509SecurityKey2); + + var incorrectSigningKeysConfigWithMatchingKid = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer }; incorrectSigningKeysConfigWithMatchingKid.SigningKeys.Add(KeyingMaterial.CreateJsonWebKeyEC(JsonWebKeyECTypes.P256, Default.X509AsymmetricSigningCredentials.Key.KeyId, KeyingMaterial.P256_D, KeyingMaterial.P256_X, KeyingMaterial.P256_Y)); + var incorrectIssuerAndIncorrectSigningKeysConfigWithMatchingKid = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer + "1" }; + incorrectSigningKeysConfigWithMatchingKid.SigningKeys.Add(KeyingMaterial.CreateJsonWebKeyEC(JsonWebKeyECTypes.P521, Default.X509AsymmetricSigningCredentials.Key.KeyId, KeyingMaterial.P521_D, KeyingMaterial.P521_X, KeyingMaterial.P521_Y)); + var expiredSecurityTokenDescriptor = Default.X509SecurityTokenDescriptor(Default.X509AsymmetricSigningCredentials); expiredSecurityTokenDescriptor.NotBefore = DateTime.UtcNow + TimeSpan.FromDays(1); expiredSecurityTokenDescriptor.Expires = DateTime.UtcNow + System.TimeSpan.FromDays(2); @@ -368,6 +374,7 @@ public static TheoryData ValidateJwsWithLastKnownGoodTheoryData }, new JwtTheoryData { + // SecurityTokenInvalidSigningKeyException is no longer a recoverable exception TestId = nameof(Default.AsymmetricJws) + "_ConfigInvalid_IssuerSigningKeyValidationFails_LKGValid", Token = Default.AsymmetricJws, ValidationParameters = new TokenValidationParameters @@ -386,7 +393,8 @@ public static TheoryData ValidateJwsWithLastKnownGoodTheoryData else return true; }, - } + }, + ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidSigningKeyException)) }, new JwtTheoryData { @@ -394,7 +402,7 @@ public static TheoryData ValidateJwsWithLastKnownGoodTheoryData Token = Default.AsymmetricJws, ValidationParameters = new TokenValidationParameters { - ConfigurationManager = new MockConfigurationManager(incorrectSigningKeysConfig, validConfig) {LastKnownGoodLifetime = TimeSpan.FromMilliseconds(.000001) }, + ConfigurationManager = new MockConfigurationManager(incorrectSigningKeysConfig, validConfig, TimeSpan.FromMilliseconds(.000001)), ValidateIssuerSigningKey = true, RequireSignedTokens = true, ValidateIssuer = true, @@ -409,7 +417,7 @@ public static TheoryData ValidateJwsWithLastKnownGoodTheoryData Token = Default.AsymmetricJws, ValidationParameters = new TokenValidationParameters { - ConfigurationManager = new MockConfigurationManager(invalidIssuerConfig, validConfig) {LastKnownGoodLifetime = TimeSpan.FromMilliseconds(.000001) }, + ConfigurationManager = new MockConfigurationManager(invalidIssuerConfig, validConfig, TimeSpan.FromMilliseconds(.000001)), ValidateIssuerSigningKey = true, RequireSignedTokens = true, ValidateIssuer = true, @@ -424,7 +432,7 @@ public static TheoryData ValidateJwsWithLastKnownGoodTheoryData Token = Default.AsymmetricJws, ValidationParameters = new TokenValidationParameters { - ConfigurationManager = new MockConfigurationManager(incorrectSigningKeysConfigWithMatchingKid, validConfig) {LastKnownGoodLifetime = TimeSpan.FromMilliseconds(.000001) }, + ConfigurationManager = new MockConfigurationManager(incorrectSigningKeysConfigWithMatchingKid, validConfig, TimeSpan.FromMilliseconds(.000001)), ValidateIssuerSigningKey = true, RequireSignedTokens = true, ValidateIssuer = true, @@ -439,14 +447,14 @@ public static TheoryData ValidateJwsWithLastKnownGoodTheoryData Token = Default.AsymmetricJws, ValidationParameters = new TokenValidationParameters { - ConfigurationManager = new MockConfigurationManager(incorrectIssuerAndSigningKeysConfig, validConfig) {LastKnownGoodLifetime = TimeSpan.FromMilliseconds(.000001) }, + ConfigurationManager = new MockConfigurationManager(incorrectIssuerAndSigningKeysConfig, validConfig, TimeSpan.FromMilliseconds(.000001)), ValidateIssuerSigningKey = true, RequireSignedTokens = true, ValidateIssuer = true, ValidateAudience = false, ValidateLifetime = false, }, - ExpectedException = new ExpectedException(typeof(SecurityTokenUnableToValidateException)) + ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidIssuerException)) }, new JwtTheoryData { @@ -454,7 +462,7 @@ public static TheoryData ValidateJwsWithLastKnownGoodTheoryData Token = Default.AsymmetricJws, ValidationParameters = new TokenValidationParameters { - ConfigurationManager = new MockConfigurationManager(validConfigKeyValidationFails, validConfig) {LastKnownGoodLifetime = TimeSpan.FromMilliseconds(.000001) }, + ConfigurationManager = new MockConfigurationManager(validConfigKeyValidationFails, validConfig, TimeSpan.FromMilliseconds(.000001)), ValidateIssuerSigningKey = true, RequireSignedTokens = true, ValidateIssuer = true, @@ -542,7 +550,7 @@ public static TheoryData ValidateJwsWithLastKnownGoodTheoryData ValidateAudience = false, ValidateLifetime = true, }, - ExpectedException = new ExpectedException(typeof(SecurityTokenUnableToValidateException)) + ExpectedException = new ExpectedException(typeof(SecurityTokenNotYetValidException)) }, new JwtTheoryData { @@ -561,7 +569,7 @@ public static TheoryData ValidateJwsWithLastKnownGoodTheoryData }, new JwtTheoryData { - TestId = nameof(Default.AsymmetricJws) + "_ConfigIssuerInvalid_AadIssuerValidatorThrow_LKGInvalid", + TestId = nameof(Default.AsymmetricJws) + "_ConfigIssuerInvalid_AadIssuerValidatorThrow_LKGSameInvalidIssuer", Token = Default.AadAsymmetricJws, ValidationParameters = new TokenValidationParameters { @@ -573,7 +581,39 @@ public static TheoryData ValidateJwsWithLastKnownGoodTheoryData ValidateAudience = false, ValidateLifetime = false, }, - ExpectedException = new ExpectedException(typeof(SecurityTokenUnableToValidateException)) + ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidIssuerException)) + }, + new JwtTheoryData + { + TestId = nameof(Default.AsymmetricJws) + "_ConfigIssuerInvalid_AadIssuerValidatorThrow_LKGDiffInvalidIssuer", + Token = Default.AadAsymmetricJws, + ValidationParameters = new TokenValidationParameters + { + ConfigurationManager = new MockConfigurationManager(invalidIssuerConfig, incorrectIssuerAndSigningKeysConfig2), + ValidateIssuerSigningKey = true, + RequireSignedTokens = true, + ValidateIssuer = true, + IssuerValidator = AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).Validate, + ValidateAudience = false, + ValidateLifetime = false, + }, + ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidIssuerException)) + }, + new JwtTheoryData + { + TestId = nameof(Default.AsymmetricJws) + "_ConfigInvalidSigningKeyMatchingKid_AadIssuerValidatorThrow_LKGDiffInvalidSigningKeyMatchingKidAndInvalidIssuer", + Token = Default.AadAsymmetricJws, + ValidationParameters = new TokenValidationParameters + { + ConfigurationManager = new MockConfigurationManager(incorrectSigningKeysConfigWithMatchingKid, incorrectIssuerAndSigningKeysConfig), + ValidateIssuerSigningKey = true, + RequireSignedTokens = true, + ValidateIssuer = true, + IssuerValidator = AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).Validate, + ValidateAudience = false, + ValidateLifetime = false, + }, + ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidSignatureException)) }, new JwtTheoryData { @@ -636,26 +676,32 @@ public static TheoryData ValidateJWEWithLastKnownGoodTheoryData // a special IssuerSigningKeyValidator in the tests below is set to fail if this configuration is used in order // to mock issuer signing key validation failure - var validConfigKeyValidationFails = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "oauth/token", Issuer = Default.Issuer }; + var validConfigKeyValidationFails = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer }; validConfigKeyValidationFails.SigningKeys.Add(Default.SymmetricSigningKey256); - var invalidIssuerConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "oauth/token", Issuer = Default.Issuer + "2" }; + var invalidIssuerConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer + "1" }; invalidIssuerConfig.SigningKeys.Add(Default.SymmetricSigningKey256); - var incorrectSigningKeysConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "oauth/token", Issuer = Default.Issuer }; + var incorrectSigningKeysConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer }; incorrectSigningKeysConfig.SigningKeys.Add(KeyingMaterial.X509SecurityKey2); - var incorrectIssuerAndSigningKeysConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "oauth/token", Issuer = Default.Issuer + "2" }; + var incorrectIssuerAndSigningKeysConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer + "1" }; incorrectIssuerAndSigningKeysConfig.SigningKeys.Add(KeyingMaterial.X509SecurityKey2); - var incorrectSigningKeysConfigWithMatchingKid = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "oauth/token", Issuer = Default.Issuer }; + var incorrectIssuerAndSigningKeysConfig2 = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer + "2" }; + incorrectIssuerAndSigningKeysConfig.SigningKeys.Add(KeyingMaterial.X509SecurityKey2); + + var incorrectSigningKeysConfigWithMatchingKid = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer }; incorrectSigningKeysConfigWithMatchingKid.SigningKeys.Add(new SymmetricSecurityKey(KeyingMaterial.DefaultSymmetricSecurityKey_128.Key) { KeyId = KeyingMaterial.DefaultSymmetricSecurityKey_256.KeyId }); + var incorrectIssuerAndIncorrectSigningKeysConfigWithMatchingKid = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer + "1" }; + incorrectSigningKeysConfigWithMatchingKid.SigningKeys.Add(new SymmetricSecurityKey(KeyingMaterial.DefaultSymmetricSecurityKey_64.Key) { KeyId = KeyingMaterial.DefaultSymmetricSecurityKey_256.KeyId }); + var notYetValidSecurityTokenDescriptor = Default.X509SecurityTokenDescriptor(Default.SymmetricEncryptingCredentials, Default.X509AsymmetricSigningCredentials, null); notYetValidSecurityTokenDescriptor.NotBefore = DateTime.UtcNow + TimeSpan.FromDays(1); notYetValidSecurityTokenDescriptor.Expires = DateTime.UtcNow + TimeSpan.FromDays(2); var notYetValidJwe = Default.Jwt(notYetValidSecurityTokenDescriptor); - var notYetValidJweConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "oauth/token", Issuer = Default.Issuer }; + var notYetValidJweConfig = new OpenIdConnectConfiguration() { TokenEndpoint = Default.Issuer + "/oauth/token", Issuer = Default.Issuer }; notYetValidJweConfig.SigningKeys.Add(Default.X509AsymmetricSigningCredentials.Key); return new TheoryData @@ -722,6 +768,7 @@ public static TheoryData ValidateJWEWithLastKnownGoodTheoryData }, new JwtTheoryData { + // SecurityTokenInvalidSigningKeyException is no longer a recoverable exception TestId = nameof(jwe) + "_ConfigIssuerSigningKeyValidationFails_LKGValid", Token = jwe, ValidationParameters = new TokenValidationParameters @@ -741,7 +788,9 @@ public static TheoryData ValidateJWEWithLastKnownGoodTheoryData return true; }, TokenDecryptionKey = KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes128_Sha2.Key - } + }, + ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidSigningKeyException)) + }, new JwtTheoryData { @@ -749,7 +798,7 @@ public static TheoryData ValidateJWEWithLastKnownGoodTheoryData Token = jwe, ValidationParameters = new TokenValidationParameters { - ConfigurationManager = new MockConfigurationManager(incorrectSigningKeysConfig, validConfig) {LastKnownGoodLifetime = TimeSpan.FromMilliseconds(.000001) }, + ConfigurationManager = new MockConfigurationManager(incorrectSigningKeysConfig, validConfig, TimeSpan.FromMilliseconds(.000001)), ValidateIssuerSigningKey = true, RequireSignedTokens = true, ValidateIssuer = true, @@ -765,7 +814,7 @@ public static TheoryData ValidateJWEWithLastKnownGoodTheoryData Token = jwe, ValidationParameters = new TokenValidationParameters { - ConfigurationManager = new MockConfigurationManager(invalidIssuerConfig, validConfig) {LastKnownGoodLifetime = TimeSpan.FromMilliseconds(.000001) }, + ConfigurationManager = new MockConfigurationManager(invalidIssuerConfig, validConfig, TimeSpan.FromMilliseconds(.000001)), ValidateIssuerSigningKey = true, RequireSignedTokens = true, ValidateIssuer = true, @@ -781,7 +830,7 @@ public static TheoryData ValidateJWEWithLastKnownGoodTheoryData Token = jwe, ValidationParameters = new TokenValidationParameters { - ConfigurationManager = new MockConfigurationManager(incorrectSigningKeysConfigWithMatchingKid, validConfig) {LastKnownGoodLifetime = TimeSpan.FromMilliseconds(.000001) }, + ConfigurationManager = new MockConfigurationManager(incorrectSigningKeysConfigWithMatchingKid, validConfig, TimeSpan.FromMilliseconds(.000001)), ValidateIssuerSigningKey = true, RequireSignedTokens = true, ValidateIssuer = true, @@ -797,7 +846,7 @@ public static TheoryData ValidateJWEWithLastKnownGoodTheoryData Token = jwe, ValidationParameters = new TokenValidationParameters { - ConfigurationManager = new MockConfigurationManager(incorrectIssuerAndSigningKeysConfig, validConfig) {LastKnownGoodLifetime = TimeSpan.FromMilliseconds(.000001) }, + ConfigurationManager = new MockConfigurationManager(incorrectIssuerAndSigningKeysConfig, validConfig, TimeSpan.FromMilliseconds(.000001)), ValidateIssuerSigningKey = true, RequireSignedTokens = true, ValidateIssuer = true, @@ -805,7 +854,7 @@ public static TheoryData ValidateJWEWithLastKnownGoodTheoryData ValidateLifetime = false, TokenDecryptionKey = KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes128_Sha2.Key }, - ExpectedException = new ExpectedException(typeof(SecurityTokenUnableToValidateException)) + ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidIssuerException)) }, new JwtTheoryData { @@ -813,7 +862,7 @@ public static TheoryData ValidateJWEWithLastKnownGoodTheoryData Token = jwe, ValidationParameters = new TokenValidationParameters { - ConfigurationManager = new MockConfigurationManager(validConfigKeyValidationFails, validConfig) {LastKnownGoodLifetime = TimeSpan.FromMilliseconds(.000001) }, + ConfigurationManager = new MockConfigurationManager(validConfigKeyValidationFails, validConfig, TimeSpan.FromMilliseconds(.000001)), ValidateIssuerSigningKey = true, RequireSignedTokens = true, ValidateIssuer = true, @@ -907,7 +956,7 @@ public static TheoryData ValidateJWEWithLastKnownGoodTheoryData ValidateLifetime = true, TokenDecryptionKey = Default.SymmetricEncryptingCredentials.Key }, - ExpectedException = new ExpectedException(typeof(SecurityTokenUnableToValidateException)) + ExpectedException = new ExpectedException(typeof(SecurityTokenNotYetValidException)) }, new JwtTheoryData { @@ -927,7 +976,7 @@ public static TheoryData ValidateJWEWithLastKnownGoodTheoryData }, new JwtTheoryData { - TestId = nameof(aadJwe) + "_ConfigIssuerInvalid_AadIssuerValidatorThrow_LKGInvalid", + TestId = nameof(aadJwe) + "_ConfigIssuerInvalid_AadIssuerValidatorThrow_LKGSameInvalidIssuer", Token = aadJwe, ValidationParameters = new TokenValidationParameters { @@ -939,9 +988,42 @@ public static TheoryData ValidateJWEWithLastKnownGoodTheoryData ValidateAudience = false, ValidateLifetime = false, TokenDecryptionKey = KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes128_Sha2.Key - }, - ExpectedException = new ExpectedException(typeof(SecurityTokenUnableToValidateException)) + ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidIssuerException)) + }, + new JwtTheoryData + { + TestId = nameof(aadJwe) + "_ConfigIssuerInvalid_AadIssuerValidatorThrow_LKGDiffInvalidIssuer", + Token = aadJwe, + ValidationParameters = new TokenValidationParameters + { + ConfigurationManager = new MockConfigurationManager(invalidIssuerConfig, incorrectIssuerAndSigningKeysConfig2), + ValidateIssuerSigningKey = true, + RequireSignedTokens = true, + ValidateIssuer = true, + IssuerValidator = AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).Validate, + ValidateAudience = false, + ValidateLifetime = false, + TokenDecryptionKey = KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes128_Sha2.Key + }, + ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidIssuerException)) + }, + new JwtTheoryData + { + TestId = nameof(aadJwe) + "_ConfigInvalidSigningKeyMatchingKid_AadIssuerValidatorThrow_LKGDiffInvalidSigningKeyMatchingKidAndInvalidIssuer", + Token = aadJwe, + ValidationParameters = new TokenValidationParameters + { + ConfigurationManager = new MockConfigurationManager(incorrectSigningKeysConfigWithMatchingKid, incorrectIssuerAndIncorrectSigningKeysConfigWithMatchingKid), + ValidateIssuerSigningKey = true, + RequireSignedTokens = true, + ValidateIssuer = true, + IssuerValidator = AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).Validate, + ValidateAudience = false, + ValidateLifetime = false, + TokenDecryptionKey = KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes128_Sha2.Key + }, + ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidSignatureException)) }, new JwtTheoryData {