diff --git a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs index a1c480c469..7eece8d2bd 100644 --- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs @@ -79,6 +79,7 @@ internal static class LogMessages //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."; + public const string IDX10266 = "IDX10266: IssuerValidatorAsync property of ValidationParameters is defined and the Validation method called is synchronous. https://aka.ms/IdentityModel/TokenValidation"; // 10500 - SignatureValidation public const string IDX10500 = "IDX10500: Signature validation failed. No security keys were provided to validate the signature."; diff --git a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs index f110a31e45..cdd3aaa77a 100644 --- a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs +++ b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs @@ -11,6 +11,7 @@ namespace Microsoft.IdentityModel.Tokens { + #region Delegate Definitions /// /// Definition for AlgorithmValidator /// @@ -84,8 +85,11 @@ namespace Microsoft.IdentityModel.Tokens /// required for validation. /// The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity". /// The delegate should return a non null string that represents the 'issuer'. If null a default value will be used. - /// If both and are set, IssuerValidatorUsingConfiguration takes - /// priority. + /// If the validation method called is asynchronous and is set, this validator will have priority over any other validator. + /// If the validation method called is synchronous and is set, this validator will be last in priority. + /// If the validation method called is synchronous and both and are set, + /// IssuerValidatorUsingConfiguration takes priority. + /// public delegate string IssuerValidator(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters); /// @@ -97,22 +101,27 @@ namespace Microsoft.IdentityModel.Tokens /// required for validation. /// The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity". /// The delegate should return a non null string that represents the 'issuer'. If null a default value will be used. - /// If both and are set, IssuerValidatorUsingConfiguration takes - /// priority. + /// If the validation method called is asynchronous and is set, this validator will have priority over any other validator. + /// If the validation method called is synchronous and is set, this validator will be last in priority. + /// If the validation method called is synchronous and both and are set, + /// IssuerValidatorUsingConfiguration takes priority. /// public delegate string IssuerValidatorUsingConfiguration(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration); /// - /// Definition for IssuerValidatorAsync. Left internal for now while we work out the details of async validation for all delegates. + /// Definition for IssuerValidatorAsync. /// /// The issuer to validate. /// The that is being validated. /// required for validation. /// The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity". /// The delegate should return a non null string that represents the 'issuer'. If null a default value will be used. - /// if set, will be called before or + /// If the validation method called is asynchronous and is set, this validator will have priority over any other validator. + /// If the validation method called is synchronous and is set, this validator will be last in priority. + /// If the validation method called is synchronous and both and are set, + /// IssuerValidatorUsingConfiguration takes priority. /// - internal delegate ValueTask IssuerValidatorAsync(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters); + public delegate Task IssuerValidatorAsync(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters); /// /// Definition for LifetimeValidator. @@ -181,6 +190,8 @@ namespace Microsoft.IdentityModel.Tokens /// A transformed . public delegate SecurityToken TransformBeforeSignatureValidation(SecurityToken token, TokenValidationParameters validationParameters); + #endregion + /// /// Contains a set of parameters that are used by a when validating a . /// @@ -534,12 +545,13 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken, /// If set, this delegate will be called to validate the 'issuer' of the token, instead of default processing. /// This means that no default 'issuer' validation will occur. /// Even if is false, this delegate will still be called. - /// If both and are set, IssuerValidatorUsingConfiguration takes - /// priority. + /// If the validation method called is asynchronous and is set, this validator will have priority over any other validator. + /// If the validation method called is synchronous and is set, this validator will be last in priority. + /// If the validation method called is synchronous and both and are set, + /// IssuerValidatorUsingConfiguration takes priority. /// public IssuerValidator IssuerValidator { get; set; } - /// /// Gets or sets a delegate that will be used to validate the issuer of the token. /// @@ -547,9 +559,12 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken, /// If set, this delegate will be called to validate the 'issuer' of the token, instead of default processing. /// This means that no default 'issuer' validation will occur. /// Even if is false, this delegate will still be called. - /// IssuerValidatorAsync takes precedence over and . + /// If the validation method called is asynchronous and is set, this validator will have priority over any other validator. + /// If the validation method called is synchronous and is set, this validator will be last in priority. + /// If the validation method called is synchronous and both and are set, + /// IssuerValidatorUsingConfiguration takes priority. /// - internal IssuerValidatorAsync IssuerValidatorAsync { get; set; } + public IssuerValidatorAsync IssuerValidatorAsync { get; set; } /// /// Gets or sets a delegate that will be used to validate the issuer of the token. @@ -559,8 +574,10 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken, /// This means that no default 'issuer' validation will occur. /// Even if is false, this delegate will still be called. /// This delegate should be used if properties from the configuration retrieved from the authority are necessary to validate the issuer. - /// If both and are set, IssuerValidatorUsingConfiguration takes - /// priority. + /// If the validation method called is asynchronous and is set, this validator will have priority over any other validator. + /// If the validation method called is synchronous and is set, this validator will be last in priority. + /// If the validation method called is synchronous and both and are set, + /// IssuerValidatorUsingConfiguration takes priority. /// public IssuerValidatorUsingConfiguration IssuerValidatorUsingConfiguration { get; set; } diff --git a/src/Microsoft.IdentityModel.Tokens/Validators.cs b/src/Microsoft.IdentityModel.Tokens/Validators.cs index 2c9f9e6ca1..08ea970489 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validators.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validators.cs @@ -213,16 +213,29 @@ public static string ValidateIssuer(string issuer, SecurityToken securityToken, /// The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity". /// If 'validationParameters' is null. /// If 'issuer' is null or whitespace and is true. - /// If ' configuration' is null. + /// If 'configuration' is null. /// If is null or whitespace and is null and is null. /// If 'issuer' failed to matched either or one of or . /// An EXACT match is required. internal static string ValidateIssuer(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) { - ValueTask vt = ValidateIssuerAsync(issuer, securityToken, validationParameters, configuration); - return vt.IsCompletedSuccessfully ? - vt.Result : - vt.AsTask().ConfigureAwait(false).GetAwaiter().GetResult(); + if (validationParameters == null) + throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + + // Given that this is a synchronous method and to avoid consuming excesive threads + // we will favor the synchronous delegates over the asynchronous one. + if (validationParameters.IssuerValidator == null + && validationParameters.IssuerValidatorUsingConfiguration == null + && validationParameters.IssuerValidatorAsync != null) + { + if (LogHelper.IsEnabled(EventLogLevel.Warning)) + LogHelper.LogWarning(LogMessages.IDX10266); + + return validationParameters.IssuerValidatorAsync(issuer, securityToken, validationParameters) + .ConfigureAwait(false).GetAwaiter().GetResult(); + } + + return ValidateIssuerInternal(issuer, securityToken, validationParameters, configuration); } /// @@ -248,9 +261,15 @@ internal static async ValueTask ValidateIssuerAsync( if (validationParameters == null) throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + // Given that this is an asynchronous method we will favor the asynchronous delegate over the synchronous. if (validationParameters.IssuerValidatorAsync != null) return await validationParameters.IssuerValidatorAsync(issuer, securityToken, validationParameters).ConfigureAwait(false); + return ValidateIssuerInternal(issuer, securityToken, validationParameters, configuration); + } + + private static string ValidateIssuerInternal(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + { if (validationParameters.IssuerValidatorUsingConfiguration != null) return validationParameters.IssuerValidatorUsingConfiguration(issuer, securityToken, validationParameters, configuration); @@ -268,11 +287,13 @@ internal static async ValueTask ValidateIssuerAsync( { InvalidIssuer = issuer }); // Throw if all possible places to validate against are null or empty - if ( string.IsNullOrWhiteSpace(validationParameters.ValidIssuer) + if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer) && validationParameters.ValidIssuers.IsNullOrEmpty() && string.IsNullOrWhiteSpace(configuration?.Issuer)) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX10204) - { InvalidIssuer = issuer }); + { + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX10204) + { InvalidIssuer = issuer }); + } if (configuration != null) { diff --git a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs index cc722e6642..39befc5324 100644 --- a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs +++ b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs @@ -13,7 +13,6 @@ using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Tokens; -using static Microsoft.IdentityModel.Validators.AadIssuerValidator; namespace Microsoft.IdentityModel.Validators { @@ -188,7 +187,7 @@ public string Validate( SecurityToken securityToken, TokenValidationParameters validationParameters) { - ValueTask vt = ValidateAsync(issuer, securityToken, validationParameters); + ValueTask vt = ValidateInternalAsync(issuer, securityToken, validationParameters); return vt.IsCompletedSuccessfully ? vt.Result : vt.AsTask().ConfigureAwait(false).GetAwaiter().GetResult(); @@ -203,7 +202,7 @@ public string Validate( /// Token validation parameters. /// /// AadIssuerValidator aadIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(authority, httpClient); - /// TokenValidationParameters.IssuerValidator = aadIssuerValidator.Validate; + /// TokenValidationParameters.IssuerValidatorAsync = aadIssuerValidator.ValidateAsync; /// /// The issuer is considered as valid if it has the same HTTP scheme and authority as the /// authority from the configuration file, has a tenant ID, and optionally v2.0 (if this web API @@ -212,7 +211,15 @@ public string Validate( /// if is null. /// if is null. /// if the issuer is invalid or if there is a network issue. - internal async ValueTask ValidateAsync( + public Task ValidateAsync( + string issuer, + SecurityToken securityToken, + TokenValidationParameters validationParameters) + { + return ValidateInternalAsync(issuer, securityToken, validationParameters).AsTask(); + } + + private async ValueTask ValidateInternalAsync( string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters) @@ -466,14 +473,14 @@ private BaseConfigurationManager GetEffectiveConfigurationManager(ProtocolVersio if (_configurationManagerProvider != null) { string aadAuthority = GetAuthority(protocolVersion); - var configurationManager = _configurationManagerProvider(aadAuthority); + if (configurationManager != null) return configurationManager; } - // If no provider or provider returned null, fallback to previous strategy + // If no provider or provider returned null, fallback to previous strategy return GetConfigurationManager(protocolVersion); } diff --git a/test/Microsoft.IdentityModel.TestUtils/ValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/ValidationDelegates.cs index f0663cc997..753f4f0fe4 100644 --- a/test/Microsoft.IdentityModel.TestUtils/ValidationDelegates.cs +++ b/test/Microsoft.IdentityModel.TestUtils/ValidationDelegates.cs @@ -87,9 +87,9 @@ public static string IssuerValidatorUsingConfigEcho(string issuer, SecurityToken return issuer; } - public static ValueTask IssuerValidatorAsync(string issuer, SecurityToken token, TokenValidationParameters validationParameters) + public static Task IssuerValidatorAsync(string issuer, SecurityToken token, TokenValidationParameters validationParameters) { - return new ValueTask(issuer); + return Task.FromResult(issuer); } public static string IssuerValidatorReturnsDifferentIssuer(string issuer, SecurityToken token, TokenValidationParameters validationParameters) diff --git a/test/Microsoft.IdentityModel.Validators.Tests/MicrosoftIdentityIssuerValidatorTest.cs b/test/Microsoft.IdentityModel.Validators.Tests/MicrosoftIdentityIssuerValidatorTest.cs index b747c65338..b055409b34 100644 --- a/test/Microsoft.IdentityModel.Validators.Tests/MicrosoftIdentityIssuerValidatorTest.cs +++ b/test/Microsoft.IdentityModel.Validators.Tests/MicrosoftIdentityIssuerValidatorTest.cs @@ -8,6 +8,7 @@ using System.Net.Http; using System.Security.Claims; using System.Threading; +using System.Threading.Tasks; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.TestUtils; @@ -141,27 +142,37 @@ public void GetIssuerValidator_CachedAuthority_ReturnsCachedValidator() } [Fact] - public void Validate_NullOrEmptyParameters_ThrowsException() + public async Task Validate_NullOrEmptyParameters_ThrowsException() { var context = new CompareContext(); var validator = new AadIssuerValidator(_httpClient, ValidatorConstants.AadIssuer); var jwtSecurityToken = new JwtSecurityToken(); var validationParams = new TokenValidationParameters(); + // Null issuer Assert.Throws(ValidatorConstants.Issuer, () => validator.Validate(null, jwtSecurityToken, validationParams)); + await Assert.ThrowsAsync(ValidatorConstants.Issuer, () => validator.ValidateAsync(null, jwtSecurityToken, validationParams)); + // Empty issuer var exception = Assert.Throws(() => validator.Validate(string.Empty, jwtSecurityToken, validationParams)); + IdentityComparer.AreEqual(LogMessages.IDX40003, exception.Message); + exception = await Assert.ThrowsAsync(() => validator.ValidateAsync(string.Empty, jwtSecurityToken, validationParams)); IdentityComparer.AreEqual(LogMessages.IDX40003, exception.Message); + // Null SecurityToken Assert.Throws(ValidatorConstants.SecurityToken, () => validator.Validate(ValidatorConstants.AadIssuer, null, validationParams)); + await Assert.ThrowsAsync(ValidatorConstants.SecurityToken, () => validator.ValidateAsync(ValidatorConstants.AadIssuer, null, validationParams)); + // Null TokenValidationParameters Assert.Throws(ValidatorConstants.ValidationParameters, () => validator.Validate(ValidatorConstants.AadIssuer, jwtSecurityToken, null)); + await Assert.ThrowsAsync(ValidatorConstants.ValidationParameters, () => validator.ValidateAsync(ValidatorConstants.AadIssuer, jwtSecurityToken, null)); + TestUtilities.AssertFailIfErrors(context); } [Fact] - public void Validate_NullOrEmptyTenantId_ThrowsException() + public async Task Validate_NullOrEmptyTenantId_ThrowsException() { var context = new CompareContext(); var validator = new AadIssuerValidator(_httpClient, ValidatorConstants.AadIssuer); @@ -170,14 +181,27 @@ public void Validate_NullOrEmptyTenantId_ThrowsException() var securityToken = Substitute.For(); var validationParameters = new TokenValidationParameters(); + // JwtSecurityToken var exception = Assert.Throws(() => validator.Validate(ValidatorConstants.AadIssuer, jwtSecurityToken, validationParameters)); IdentityComparer.AreEqual(LogMessages.IDX40003, exception.Message, context); + exception = await Assert.ThrowsAsync(() => validator.ValidateAsync(ValidatorConstants.AadIssuer, jwtSecurityToken, validationParameters)); + IdentityComparer.AreEqual(LogMessages.IDX40003, exception.Message, context); + + // JsonWebToken exception = Assert.Throws(() => validator.Validate(ValidatorConstants.AadIssuer, jsonWebToken, validationParameters)); IdentityComparer.AreEqual(LogMessages.IDX40003, exception.Message, context); + exception = await Assert.ThrowsAsync(() => validator.ValidateAsync(ValidatorConstants.AadIssuer, jsonWebToken, validationParameters)); + IdentityComparer.AreEqual(LogMessages.IDX40003, exception.Message, context); + + // SecurityToken exception = Assert.Throws(() => validator.Validate(ValidatorConstants.AadIssuer, securityToken, validationParameters)); IdentityComparer.AreEqual(LogMessages.IDX40003, exception.Message, context); + + exception = await Assert.ThrowsAsync(() => validator.ValidateAsync(ValidatorConstants.AadIssuer, securityToken, validationParameters)); + IdentityComparer.AreEqual(LogMessages.IDX40003, exception.Message, context); + TestUtilities.AssertFailIfErrors(context); } @@ -190,7 +214,7 @@ public void Validate_NullOrEmptyTenantId_ThrowsException() [InlineData(ValidatorConstants.TenantId, ValidatorConstants.AuthorityCommonTenant, ValidatorConstants.AadIssuer, true)] [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.UsGovTenantId, ValidatorConstants.UsGovIssuer, true)] [InlineData(ValidatorConstants.TenantId, ValidatorConstants.UsGovTenantId, ValidatorConstants.UsGovIssuer, true)] - public void Validate_IssuerMatchedInValidIssuer_ReturnsIssuer(string tidClaimType, string tenantId, string issuer, bool useConfigurationManagerProvider) + public async Task Validate_IssuerMatchedInValidIssuer_ReturnsIssuer(string tidClaimType, string tenantId, string issuer, bool useConfigurationManagerProvider) { var context = new CompareContext(); AadIssuerValidator validator = null; @@ -200,15 +224,17 @@ public void Validate_IssuerMatchedInValidIssuer_ReturnsIssuer(string tidClaimTyp validator = new AadIssuerValidator(_httpClient, issuer, x => null); var tidClaim = new Claim(tidClaimType, tenantId); - var issClaim = new Claim(ValidatorConstants.ClaimNameIss, issuer); var jwtSecurityToken = new JwtSecurityToken(issuer: issuer, claims: new[] { issClaim, tidClaim }); validator.AadIssuerV2 = issuer; var actualIssuer = validator.Validate(issuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuer = issuer }); + IdentityComparer.AreEqual(issuer, actualIssuer, context); + actualIssuer = await validator.ValidateAsync(issuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuer = issuer }); IdentityComparer.AreEqual(issuer, actualIssuer, context); + TestUtilities.AssertFailIfErrors(context); } @@ -217,7 +243,7 @@ public void Validate_IssuerMatchedInValidIssuer_ReturnsIssuer(string tidClaimTyp [InlineData(ValidatorConstants.TenantId, ValidatorConstants.TenantIdAsGuid, ValidatorConstants.AadIssuer)] [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.TenantIdAsGuid, ValidatorConstants.V1Issuer)] [InlineData(ValidatorConstants.TenantId, ValidatorConstants.TenantIdAsGuid, ValidatorConstants.V1Issuer)] - public void Validate_NoHttpclientFactory_ReturnsIssuer(string tidClaimType, string tenantId, string issuer) + public async Task Validate_NoHttpclientFactory_ReturnsIssuer(string tidClaimType, string tenantId, string issuer) { var context = new CompareContext(); var validator = new AadIssuerValidator(null, issuer); @@ -229,6 +255,8 @@ public void Validate_NoHttpclientFactory_ReturnsIssuer(string tidClaimType, stri var tokenValidationParams = new TokenValidationParameters() { ConfigurationManager = new MockConfigurationManager(new OpenIdConnectConfiguration() { Issuer = issuer }) }; IdentityComparer.AreEqual(issuer, validator.Validate(issuer, jwtSecurityToken, tokenValidationParams), context); + IdentityComparer.AreEqual(issuer, await validator.ValidateAsync(issuer, jwtSecurityToken, tokenValidationParams), context); + TestUtilities.AssertFailIfErrors(context); } @@ -237,7 +265,7 @@ public void Validate_NoHttpclientFactory_ReturnsIssuer(string tidClaimType, stri [InlineData(ValidatorConstants.TenantId, ValidatorConstants.TenantIdAsGuid, ValidatorConstants.V1Issuer, false)] [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.TenantIdAsGuid, ValidatorConstants.V1Issuer, true)] [InlineData(ValidatorConstants.TenantId, ValidatorConstants.TenantIdAsGuid, ValidatorConstants.V1Issuer, true)] - public void Validate_IssuerMatchedInValidV1Issuer_ReturnsIssuer(string tidClaimType, string tenantId, string issuer, bool useConfigurationProvider) + public async Task Validate_IssuerMatchedInValidV1Issuer_ReturnsIssuer(string tidClaimType, string tenantId, string issuer, bool useConfigurationProvider) { var context = new CompareContext(); @@ -255,12 +283,17 @@ public void Validate_IssuerMatchedInValidV1Issuer_ReturnsIssuer(string tidClaimT validator.AadIssuerV1 = issuer; var actualIssuer = validator.Validate(issuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuer = issuer }); + IdentityComparer.AreEqual(issuer, actualIssuer, context); + actualIssuer = await validator.ValidateAsync(issuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuer = issuer }); IdentityComparer.AreEqual(issuer, actualIssuer, context); var actualIssuers = validator.Validate(issuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuers = new[] { issuer } }); + IdentityComparer.AreEqual(issuer, actualIssuers, context); + actualIssuers = await validator.ValidateAsync(issuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuers = new[] { issuer } }); IdentityComparer.AreEqual(issuer, actualIssuers, context); + TestUtilities.AssertFailIfErrors(context); } @@ -269,7 +302,7 @@ public void Validate_IssuerMatchedInValidV1Issuer_ReturnsIssuer(string tidClaimT [InlineData(ValidatorConstants.TenantId, false)] [InlineData(ValidatorConstants.ClaimNameTid, true)] [InlineData(ValidatorConstants.TenantId, true)] - public void Validate_IssuerMatchedInValidIssuers_ReturnsIssuer(string tidClaimType, bool useConfigurationProvider) + public async Task Validate_IssuerMatchedInValidIssuers_ReturnsIssuer(string tidClaimType, bool useConfigurationProvider) { var context = new CompareContext(); @@ -285,12 +318,17 @@ public void Validate_IssuerMatchedInValidIssuers_ReturnsIssuer(string tidClaimTy var jwtSecurityToken = new JwtSecurityToken(issuer: ValidatorConstants.AadIssuer, claims: new[] { issClaim, tidClaim }); var actualIssuers = validator.Validate(ValidatorConstants.AadIssuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuers = new[] { ValidatorConstants.AadIssuer } }); + IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuers, context); + actualIssuers = await validator.ValidateAsync(ValidatorConstants.AadIssuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuers = new[] { ValidatorConstants.AadIssuer } }); IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuers, context); var actualIssuer = validator.Validate(ValidatorConstants.AadIssuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuer = ValidatorConstants.AadIssuer }); + IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); + actualIssuer = await validator.ValidateAsync(ValidatorConstants.AadIssuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuer = ValidatorConstants.AadIssuer }); IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); + TestUtilities.AssertFailIfErrors(context); } @@ -299,7 +337,7 @@ public void Validate_IssuerMatchedInValidIssuers_ReturnsIssuer(string tidClaimTy [InlineData(ValidatorConstants.TenantId, false)] [InlineData(ValidatorConstants.ClaimNameTid, true)] [InlineData(ValidatorConstants.TenantId, true)] - public void Validate_IssuerNotInTokenValidationParameters_ReturnsIssuer(string tidClaimType, bool useConfigurationProvider) + public async Task Validate_IssuerNotInTokenValidationParameters_ReturnsIssuer(string tidClaimType, bool useConfigurationProvider) { var context = new CompareContext(); AadIssuerValidator validator = null; @@ -314,8 +352,11 @@ public void Validate_IssuerNotInTokenValidationParameters_ReturnsIssuer(string t var jwtSecurityToken = new JwtSecurityToken(issuer: ValidatorConstants.AadIssuer, claims: new[] { issClaim, tidClaim }); var actualIssuer = validator.Validate(ValidatorConstants.AadIssuer, jwtSecurityToken, new TokenValidationParameters()); + IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); + actualIssuer = await validator.ValidateAsync(ValidatorConstants.AadIssuer, jwtSecurityToken, new TokenValidationParameters()); IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); + TestUtilities.AssertFailIfErrors(context); } @@ -328,7 +369,7 @@ public void Validate_IssuerNotInTokenValidationParameters_ReturnsIssuer(string t [InlineData(ValidatorConstants.TenantId, ValidatorConstants.AadIssuer, true)] [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.V1Issuer, true)] [InlineData(ValidatorConstants.TenantId, ValidatorConstants.V1Issuer, true)] - public void ValidateJsonWebToken_ReturnsIssuer(string tidClaimType, string issuer, bool useConfigurationProvider) + public async Task ValidateJsonWebToken_ReturnsIssuer(string tidClaimType, string issuer, bool useConfigurationProvider) { AadIssuerValidator validator = null; if (useConfigurationProvider == false) @@ -345,9 +386,13 @@ public void ValidateJsonWebToken_ReturnsIssuer(string tidClaimType, string issue claims.Add(issClaim); var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor(Default.SymmetricSigningCredentials, claims))); + var actualIssuer = validator.Validate(issuer, jsonWebToken, new TokenValidationParameters()); + IdentityComparer.AreEqual(issuer, actualIssuer, context); + actualIssuer = await validator.ValidateAsync(issuer, jsonWebToken, new TokenValidationParameters()); IdentityComparer.AreEqual(issuer, actualIssuer, context); + TestUtilities.AssertFailIfErrors(context); } @@ -356,7 +401,7 @@ public void ValidateJsonWebToken_ReturnsIssuer(string tidClaimType, string issue [InlineData(ValidatorConstants.TenantId, false)] [InlineData(ValidatorConstants.ClaimNameTid, true)] [InlineData(ValidatorConstants.TenantId, true)] - public void Validate_V1IssuerNotInTokenValidationParameters_ReturnsV1Issuer(string tidClaimType, bool useConfigurationProvider) + public async Task Validate_V1IssuerNotInTokenValidationParameters_ReturnsV1Issuer(string tidClaimType, bool useConfigurationProvider) { AadIssuerValidator validator = null; if (useConfigurationProvider == false) @@ -371,13 +416,16 @@ public void Validate_V1IssuerNotInTokenValidationParameters_ReturnsV1Issuer(stri var jwtSecurityToken = new JwtSecurityToken(issuer: ValidatorConstants.V1Issuer, claims: new[] { issClaim, tidClaim }); var actualIssuer = validator.Validate(ValidatorConstants.V1Issuer, jwtSecurityToken, new TokenValidationParameters()); + IdentityComparer.AreEqual(ValidatorConstants.V1Issuer, actualIssuer, context); + actualIssuer = await validator.ValidateAsync(ValidatorConstants.V1Issuer, jwtSecurityToken, new TokenValidationParameters()); IdentityComparer.AreEqual(ValidatorConstants.V1Issuer, actualIssuer, context); + TestUtilities.AssertFailIfErrors(context); } [Fact] - public void Validate_TenantIdInIssuerNotInToken_ReturnsIssuer() + public async Task Validate_TenantIdInIssuerNotInToken_ReturnsIssuer() { var context = new CompareContext(); var validator = new AadIssuerValidator(_httpClient, ValidatorConstants.AadIssuer); @@ -385,13 +433,16 @@ public void Validate_TenantIdInIssuerNotInToken_ReturnsIssuer() var jwtSecurityToken = new JwtSecurityToken(issuer: ValidatorConstants.AadIssuer, claims: new[] { issClaim }); var actualIssuer = validator.Validate(ValidatorConstants.AadIssuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuer = ValidatorConstants.AadIssuer }); + IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); + actualIssuer = await validator.ValidateAsync(ValidatorConstants.AadIssuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuer = ValidatorConstants.AadIssuer }); IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); + TestUtilities.AssertFailIfErrors(context); } [Fact] - public void Validate_TidClaimInToken_ReturnsIssuer() + public async Task Validate_TidClaimInToken_ReturnsIssuer() { var context = new CompareContext(); var validator = new AadIssuerValidator(_httpClient, ValidatorConstants.AadIssuer); @@ -401,19 +452,24 @@ public void Validate_TidClaimInToken_ReturnsIssuer() var jsonWebToken = new JsonWebToken($"{{}}", $"{{\"{ValidatorConstants.ClaimNameIss}\":\"{ValidatorConstants.AadIssuer}\",\"{ValidatorConstants.ClaimNameTid}\":\"{ValidatorConstants.TenantIdAsGuid}\"}}"); var actualIssuer = validator.Validate(ValidatorConstants.AadIssuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuer = ValidatorConstants.AadIssuer }); + IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); + actualIssuer = await validator.ValidateAsync(ValidatorConstants.AadIssuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuer = ValidatorConstants.AadIssuer }); IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); actualIssuer = validator.Validate(ValidatorConstants.AadIssuer, jsonWebToken, new TokenValidationParameters() { ValidIssuer = ValidatorConstants.AadIssuer }); + IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); + actualIssuer = await validator.ValidateAsync(ValidatorConstants.AadIssuer, jsonWebToken, new TokenValidationParameters() { ValidIssuer = ValidatorConstants.AadIssuer }); IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); + TestUtilities.AssertFailIfErrors(context); } // Regression test for https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2/issues/68 // Similar to Validate_NotMatchedToMultipleIssuers_ThrowsException but uses B2C values [Fact] - public void Validate_InvalidIssuerToValidate_ThrowsException() + public async Task Validate_InvalidIssuerToValidate_ThrowsException() { var context = new CompareContext(); string invalidIssuerToValidate = $"https://badissuer/{ValidatorConstants.TenantIdAsGuid}/v2.0"; @@ -429,11 +485,16 @@ public void Validate_InvalidIssuerToValidate_ThrowsException() var exception = Assert.Throws(() => validator.Validate(invalidIssuerToValidate, jwtSecurityToken, new TokenValidationParameters() { ValidIssuers = new[] { ValidatorConstants.AadIssuer } })); IdentityComparer.AreEqual(expectedErrorMessage, exception.Message, context); + + exception = await Assert.ThrowsAsync(() => + validator.ValidateAsync(invalidIssuerToValidate, jwtSecurityToken, new TokenValidationParameters() { ValidIssuers = new[] { ValidatorConstants.AadIssuer } })); + IdentityComparer.AreEqual(expectedErrorMessage, exception.Message, context); + TestUtilities.AssertFailIfErrors(context); } [Fact] - public void Validate_FromB2CAuthority_WithNoTidClaim_ValidateSuccessfully() + public async Task Validate_FromB2CAuthority_WithNoTidClaim_ValidateSuccessfully() { var context = new CompareContext(); Claim issClaim = new Claim(ValidatorConstants.ClaimNameIss, ValidatorConstants.B2CIssuer); @@ -452,11 +513,25 @@ public void Validate_FromB2CAuthority_WithNoTidClaim_ValidateSuccessfully() IdentityComparer.AreEqual(ValidatorConstants.B2CAuthority, validator.AadAuthorityV1, context); IdentityComparer.AreEqual(ValidatorConstants.B2CAuthorityWithV2, validator.AadAuthorityV2, context); IdentityComparer.AreEqual(ProtocolVersion.V2, validator.AadAuthorityVersion, context); + + validator = CreateIssuerValidator(ValidatorConstants.B2CAuthorityWithV2); + + await validator.ValidateAsync( + ValidatorConstants.B2CIssuer, + jwtSecurityToken, + new TokenValidationParameters() + { + ValidIssuers = new[] { ValidatorConstants.B2CIssuer }, + }); + IdentityComparer.AreEqual(ValidatorConstants.B2CAuthority, validator.AadAuthorityV1, context); + IdentityComparer.AreEqual(ValidatorConstants.B2CAuthorityWithV2, validator.AadAuthorityV2, context); + IdentityComparer.AreEqual(ProtocolVersion.V2, validator.AadAuthorityVersion, context); + TestUtilities.AssertFailIfErrors(context); } [Fact] - public void Validate_FromB2CAuthority_WithTokenValidateParametersValidIssuersUnspecified_ValidateSuccessfully() + public async Task Validate_FromB2CAuthority_WithTokenValidateParametersValidIssuersUnspecified_ValidateSuccessfully() { var context = new CompareContext(); var issClaim = new Claim(ValidatorConstants.ClaimNameIss, ValidatorConstants.B2CIssuer); @@ -474,11 +549,13 @@ public void Validate_FromB2CAuthority_WithTokenValidateParametersValidIssuersUns }; IdentityComparer.AreEqual(ValidatorConstants.B2CIssuer, validator.Validate(ValidatorConstants.B2CIssuer, jwtSecurityToken, tokenValidationParams), context); + IdentityComparer.AreEqual(ValidatorConstants.B2CIssuer, await validator.ValidateAsync(ValidatorConstants.B2CIssuer, jwtSecurityToken, tokenValidationParams), context); + TestUtilities.AssertFailIfErrors(context); } [Fact] - public void Validate_FromB2CAuthority_WithTidClaim_ValidateSuccessfully() + public async Task Validate_FromB2CAuthority_WithTidClaim_ValidateSuccessfully() { var context = new CompareContext(); Claim issClaim = new Claim(ValidatorConstants.ClaimNameIss, ValidatorConstants.B2CIssuer); @@ -498,11 +575,25 @@ public void Validate_FromB2CAuthority_WithTidClaim_ValidateSuccessfully() IdentityComparer.AreEqual(ValidatorConstants.B2CAuthority, validator.AadAuthorityV1, context); IdentityComparer.AreEqual(ValidatorConstants.B2CAuthorityWithV2, validator.AadAuthorityV2, context); IdentityComparer.AreEqual(ProtocolVersion.V2, validator.AadAuthorityVersion, context); + + validator = CreateIssuerValidator(ValidatorConstants.B2CAuthorityWithV2); + + await validator.ValidateAsync( + ValidatorConstants.B2CIssuer, + jwtSecurityToken, + new TokenValidationParameters() + { + ValidIssuers = new[] { ValidatorConstants.B2CIssuer }, + }); + IdentityComparer.AreEqual(ValidatorConstants.B2CAuthority, validator.AadAuthorityV1, context); + IdentityComparer.AreEqual(ValidatorConstants.B2CAuthorityWithV2, validator.AadAuthorityV2, context); + IdentityComparer.AreEqual(ProtocolVersion.V2, validator.AadAuthorityVersion, context); + TestUtilities.AssertFailIfErrors(context); } [Fact] - public void Validate_FromB2CAuthority_InvalidIssuer_Fails() + public async Task Validate_FromB2CAuthority_InvalidIssuer_Fails() { var context = new CompareContext(); Claim issClaim = new Claim(ValidatorConstants.ClaimNameIss, ValidatorConstants.B2CIssuer2); @@ -520,11 +611,22 @@ public void Validate_FromB2CAuthority_InvalidIssuer_Fails() ValidIssuers = new[] { ValidatorConstants.B2CIssuer }, })); IdentityComparer.AreEqual(string.Format(LogMessages.IDX40001, ValidatorConstants.B2CIssuer2), exception.Message, context); + + exception = await Assert.ThrowsAsync(() => + validator.ValidateAsync( + ValidatorConstants.B2CIssuer2, + jwtSecurityToken, + new TokenValidationParameters() + { + ValidIssuers = new[] { ValidatorConstants.B2CIssuer }, + })); + IdentityComparer.AreEqual(string.Format(LogMessages.IDX40001, ValidatorConstants.B2CIssuer2), exception.Message, context); + TestUtilities.AssertFailIfErrors(context); } [Fact] - public void Validate_FromB2CAuthority_InvalidIssuerTid_Fails() + public async Task Validate_FromB2CAuthority_InvalidIssuerTid_Fails() { var context = new CompareContext(); string issuerWithInvalidTid = ValidatorConstants.B2CInstance + "/" + ValidatorConstants.TenantIdAsGuid + "/v2.0"; @@ -542,13 +644,23 @@ public void Validate_FromB2CAuthority_InvalidIssuerTid_Fails() { ValidIssuers = new[] { ValidatorConstants.B2CIssuer }, })); + IdentityComparer.AreEqual(string.Format(LogMessages.IDX40001, issuerWithInvalidTid), exception.Message, context); + exception = await Assert.ThrowsAsync(() => + validator.ValidateAsync( + issuerWithInvalidTid, + jwtSecurityToken, + new TokenValidationParameters() + { + ValidIssuers = new[] { ValidatorConstants.B2CIssuer }, + })); IdentityComparer.AreEqual(string.Format(LogMessages.IDX40001, issuerWithInvalidTid), exception.Message, context); + TestUtilities.AssertFailIfErrors(context); } [Fact] - public void Validate_FromCustomB2CAuthority_ValidateSuccessfully() + public async Task Validate_FromCustomB2CAuthority_ValidateSuccessfully() { var context = new CompareContext(); Claim issClaim = new Claim(ValidatorConstants.ClaimNameIss, ValidatorConstants.B2CCustomDomainIssuer); @@ -568,11 +680,26 @@ public void Validate_FromCustomB2CAuthority_ValidateSuccessfully() IdentityComparer.AreEqual(ValidatorConstants.B2CCustomDomainAuthority, validator.AadAuthorityV1, context); IdentityComparer.AreEqual(ValidatorConstants.B2CCustomDomainAuthorityWithV2, validator.AadAuthorityV2, context); IdentityComparer.AreEqual(ProtocolVersion.V2, validator.AadAuthorityVersion, context); + + validator = CreateIssuerValidator(ValidatorConstants.B2CCustomDomainAuthorityWithV2); + + await validator.ValidateAsync( + ValidatorConstants.B2CCustomDomainIssuer, + jwtSecurityToken, + new TokenValidationParameters() + { + ValidIssuers = new[] { ValidatorConstants.B2CCustomDomainIssuer }, + }); + + IdentityComparer.AreEqual(ValidatorConstants.B2CCustomDomainAuthority, validator.AadAuthorityV1, context); + IdentityComparer.AreEqual(ValidatorConstants.B2CCustomDomainAuthorityWithV2, validator.AadAuthorityV2, context); + IdentityComparer.AreEqual(ProtocolVersion.V2, validator.AadAuthorityVersion, context); + TestUtilities.AssertFailIfErrors(context); } [Fact] - public void Validate_FromB2CAuthority_WithTfpIssuer_ThrowsException() + public async Task Validate_FromB2CAuthority_WithTfpIssuer_ThrowsException() { var context = new CompareContext(); Claim issClaim = new Claim(ValidatorConstants.ClaimNameIss, ValidatorConstants.B2CIssuerTfp); @@ -588,8 +715,18 @@ public void Validate_FromB2CAuthority_WithTfpIssuer_ThrowsException() { ValidIssuers = new[] { ValidatorConstants.B2CIssuerTfp }, })); + IdentityComparer.AreEqual(LogMessages.IDX40002, exception.Message, context); + exception = await Assert.ThrowsAsync(() => + validator.ValidateAsync( + ValidatorConstants.B2CIssuerTfp, + jwtSecurityToken, + new TokenValidationParameters() + { + ValidIssuers = new[] { ValidatorConstants.B2CIssuerTfp }, + })); IdentityComparer.AreEqual(LogMessages.IDX40002, exception.Message, context); + TestUtilities.AssertFailIfErrors(context); } @@ -603,7 +740,7 @@ public void Validate_FromB2CAuthority_WithTfpIssuer_ThrowsException() [InlineData(ProtocolVersion.V2, ProtocolVersion.V1)] [InlineData(ProtocolVersion.V2, ProtocolVersion.V11)] [InlineData(ProtocolVersion.V2, ProtocolVersion.V2)] - public void Validate_WithAuthorityUsingConfigurationProvider(ProtocolVersion authorityVersion, ProtocolVersion tokenVersion) + public async Task Validate_WithAuthorityUsingConfigurationProvider(ProtocolVersion authorityVersion, ProtocolVersion tokenVersion) { var configurationManagerProvider = (string authority) => { @@ -671,8 +808,11 @@ public void Validate_WithAuthorityUsingConfigurationProvider(ProtocolVersion aut var aadIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(authority, _httpClient, configurationManagerProvider); var actualIssuer = aadIssuerValidator.Validate(tokenIssuer, jwtSecurityToken, new TokenValidationParameters()); + IdentityComparer.AreEqual(tokenIssuer, actualIssuer, context); + actualIssuer = await aadIssuerValidator.ValidateAsync(tokenIssuer, jwtSecurityToken, new TokenValidationParameters()); IdentityComparer.AreEqual(tokenIssuer, actualIssuer, context); + TestUtilities.AssertFailIfErrors(context); } @@ -686,7 +826,7 @@ public void Validate_WithAuthorityUsingConfigurationProvider(ProtocolVersion aut [InlineData(ProtocolVersion.V2, ProtocolVersion.V1)] [InlineData(ProtocolVersion.V2, ProtocolVersion.V11)] [InlineData(ProtocolVersion.V2, ProtocolVersion.V2)] - public void Validate_UsesLKGWithoutConfigurationProvider(ProtocolVersion authorityVersion, ProtocolVersion tokenVersion) + public async Task Validate_UsesLKGWithoutConfigurationProvider(ProtocolVersion authorityVersion, ProtocolVersion tokenVersion) { var tokenIssuerProvider = (ProtocolVersion version) => { @@ -773,13 +913,19 @@ public void Validate_UsesLKGWithoutConfigurationProvider(ProtocolVersion authori // set LKG var actualIssuer = aadIssuerValidator.Validate(issuer, jwtSecurityToken, new TokenValidationParameters()); IdentityComparer.AreEqual(issuer, actualIssuer, context); - TestUtilities.AssertFailIfErrors(context); + + actualIssuer = await aadIssuerValidator.ValidateAsync(issuer, jwtSecurityToken, new TokenValidationParameters()); + IdentityComparer.AreEqual(issuer, actualIssuer, context); // replace config with broken issuer and validate with LKG configurationManagerSetter(aadIssuerValidator, true); actualIssuer = aadIssuerValidator.Validate(issuer, jwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); IdentityComparer.AreEqual(issuer, actualIssuer, context); + + actualIssuer = await aadIssuerValidator.ValidateAsync(issuer, jwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); + IdentityComparer.AreEqual(issuer, actualIssuer, context); + TestUtilities.AssertFailIfErrors(context); } @@ -793,7 +939,7 @@ public void Validate_UsesLKGWithoutConfigurationProvider(ProtocolVersion authori [InlineData(ProtocolVersion.V2, ProtocolVersion.V1)] [InlineData(ProtocolVersion.V2, ProtocolVersion.V11)] [InlineData(ProtocolVersion.V2, ProtocolVersion.V2)] - public void Validate_CanFetchMetadataWithoutConfigurationProvider(ProtocolVersion authorityVersion, ProtocolVersion tokenVersion) + public async Task Validate_CanFetchMetadataWithoutConfigurationProvider(ProtocolVersion authorityVersion, ProtocolVersion tokenVersion) { var tokenIssuerProvider = (ProtocolVersion version) => { @@ -830,11 +976,15 @@ public void Validate_CanFetchMetadataWithoutConfigurationProvider(ProtocolVersio // set LKG var actualIssuer = aadIssuerValidator.Validate(issuer, jwtSecurityToken, new TokenValidationParameters()); IdentityComparer.AreEqual(issuer, actualIssuer, context); + + actualIssuer = await aadIssuerValidator.ValidateAsync(issuer, jwtSecurityToken, new TokenValidationParameters()); + IdentityComparer.AreEqual(issuer, actualIssuer, context); + TestUtilities.AssertFailIfErrors(context); } [Fact] - public void Validate_UsesLKGWithConfigurationProvider() + public async Task Validate_UsesLKGWithConfigurationProvider() { var v1Configuration = new OpenIdConnectConfiguration { @@ -890,16 +1040,20 @@ public void Validate_UsesLKGWithConfigurationProvider() // set LKG var actualIssuer = aadIssuerValidator.Validate(v2TokenIssuer, jwtSecurityToken, new TokenValidationParameters()); + IdentityComparer.AreEqual(v2TokenIssuer, actualIssuer, context); + actualIssuer = await aadIssuerValidator.ValidateAsync(v2TokenIssuer, jwtSecurityToken, new TokenValidationParameters()); IdentityComparer.AreEqual(v2TokenIssuer, actualIssuer, context); - TestUtilities.AssertFailIfErrors(context); - + // refresh config to a one with a broken issuer and validate with LKG v2ConfigurationManager.RefreshedConfiguration = v2ConfigurationRefreshed; v2ConfigurationManager.RequestRefresh(); actualIssuer = aadIssuerValidator.Validate(v2TokenIssuer, jwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); IdentityComparer.AreEqual(v2TokenIssuer, actualIssuer, context); + + actualIssuer = await aadIssuerValidator.ValidateAsync(v2TokenIssuer, jwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); + IdentityComparer.AreEqual(v2TokenIssuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); var v1TokenIssuer = ValidatorConstants.V1Issuer; @@ -913,6 +1067,11 @@ public void Validate_UsesLKGWithConfigurationProvider() actualIssuer = v1AadIssuerValidator.Validate(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters()); IdentityComparer.AreEqual(v1TokenIssuer, actualIssuer, context); IdentityComparer.AreEqual(null, v1ConfigurationManager.LastKnownGoodConfiguration, context); + + actualIssuer = await v1AadIssuerValidator.ValidateAsync(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters()); + IdentityComparer.AreEqual(v1TokenIssuer, actualIssuer, context); + IdentityComparer.AreEqual(null, v1ConfigurationManager.LastKnownGoodConfiguration, context); + TestUtilities.AssertFailIfErrors(context); // refresh config to a broken one and validate with LKG @@ -921,12 +1080,21 @@ public void Validate_UsesLKGWithConfigurationProvider() actualIssuer = v1AadIssuerValidator.Validate(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); IdentityComparer.AreEqual(v1TokenIssuer, actualIssuer, context); + + v1ConfigurationManager.RefreshedConfiguration = v1ConfigurationRefreshed; + v1ConfigurationManager.RequestRefresh(); + + actualIssuer = await v1AadIssuerValidator.ValidateAsync(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); + IdentityComparer.AreEqual(v1TokenIssuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); // validating cross versions also validates with LKG actualIssuer = aadIssuerValidator.Validate(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); + IdentityComparer.AreEqual(v1TokenIssuer, actualIssuer, context); + actualIssuer = await aadIssuerValidator.ValidateAsync(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); IdentityComparer.AreEqual(v1TokenIssuer, actualIssuer, context); + TestUtilities.AssertFailIfErrors(context); // if LKG not valid validation fails