diff --git a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs index 5d7275baba..f7b084d250 100644 --- a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs +++ b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs @@ -27,6 +27,8 @@ public class AadIssuerValidator internal const string V2EndpointSuffixWithTrailingSlash = $"{V2EndpointSuffix}/"; internal const string TenantIdTemplate = "{tenantid}"; + private Func _configurationManagerProvider; + internal AadIssuerValidator( HttpClient httpClient, string aadAuthority) @@ -36,11 +38,25 @@ internal AadIssuerValidator( IsV2Authority = aadAuthority.Contains(V2EndpointSuffix); } + internal AadIssuerValidator( + HttpClient httpClient, + string aadAuthority, + Func configurationManagerProvider) + : this(httpClient, aadAuthority) + { + if (configurationManagerProvider == null) + throw new ArgumentNullException(nameof(configurationManagerProvider)); + + _configurationManagerProvider = configurationManagerProvider; + } + private HttpClient HttpClient { get; } private string _aadAuthorityV1; private string _aadAuthorityV2; private BaseConfigurationManager _configurationManagerV1; private BaseConfigurationManager _configurationManagerV2; + private IssuerLastKnownGood _issuerLKGV1; + private IssuerLastKnownGood _issuerLKGV2; internal BaseConfigurationManager ConfigurationManagerV1 { @@ -180,25 +196,30 @@ internal async ValueTask ValidateAsync( try { - BaseConfigurationManager effectiveConfigurationManager = GetEffectiveConfigurationManager(securityToken); - if (validationParameters.RefreshBeforeValidation) - effectiveConfigurationManager.RequestRefresh(); + var isV2Issuer = IsV2Issuer(securityToken); + var effectiveConfigurationManager = GetEffectiveConfigurationManager(isV2Issuer); - BaseConfiguration configuration = await effectiveConfigurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false); - string aadIssuer = configuration.Issuer; - - if (!validationParameters.ValidateWithLKG) + string aadIssuer = null; + if (validationParameters.ValidateWithLKG) { - if (IsValidIssuer(aadIssuer, tenantId, issuer)) - { - effectiveConfigurationManager.LastKnownGoodConfiguration = new OpenIdConnectConfiguration() { Issuer = aadIssuer }; - return issuer; - } + // returns null if LKG issuer expired + aadIssuer = GetEffectiveLKGIssuer(isV2Issuer); } else { - if (effectiveConfigurationManager.LastKnownGoodConfiguration != null && - IsValidIssuer(effectiveConfigurationManager.LastKnownGoodConfiguration.Issuer, tenantId, issuer)) + var baseConfiguration = await GetBaseConfiguration(effectiveConfigurationManager, validationParameters).ConfigureAwait(false); + aadIssuer = baseConfiguration.Issuer; + } + + if (aadIssuer != null) + { + var isIssuerValid = IsValidIssuer(aadIssuer, tenantId, issuer); + + // The original LKG assignment behavior for previous self-state management. + if (isIssuerValid && !validationParameters.ValidateWithLKG) + SetEffectiveLKGIssuer(aadIssuer, isV2Issuer, effectiveConfigurationManager.LastKnownGoodLifetime); + + if (isIssuerValid) return issuer; } } @@ -233,17 +254,7 @@ internal async ValueTask ValidateAsync( /// if is null or empty. public static AadIssuerValidator GetAadIssuerValidator(string aadAuthority, HttpClient httpClient) { - if(string.IsNullOrEmpty(aadAuthority)) - throw LogHelper.LogArgumentNullException(nameof(aadAuthority)); - - if (s_issuerValidators.TryGetValue(aadAuthority, out AadIssuerValidator aadIssuerValidator)) - return aadIssuerValidator; - - s_issuerValidators[aadAuthority] = new AadIssuerValidator( - httpClient, - aadAuthority); - - return s_issuerValidators[aadAuthority]; + return GetAadIssuerValidator(aadAuthority, httpClient, null); } /// @@ -258,7 +269,40 @@ public static AadIssuerValidator GetAadIssuerValidator(string aadAuthority, Http /// if is null or empty. public static AadIssuerValidator GetAadIssuerValidator(string aadAuthority) { - return GetAadIssuerValidator(aadAuthority, null); + return GetAadIssuerValidator(aadAuthority, null, null); + } + + /// + /// Gets an for an Azure Active Directory (AAD) authority. + /// + /// The authority to create the validator for, e.g. https://login.microsoftonline.com/. + /// Optional HttpClient to use to retrieve the endpoint metadata (can be null). + /// Configuration manager provider. Injection point for metadata managed outside of the class. + /// + /// AadIssuerValidator aadIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(authority, configurationManagerProvider); + /// TokenValidationParameters.IssuerValidator = aadIssuerValidator.Validate; + /// + /// A for the aadAuthority. + /// if is null or empty. + internal static AadIssuerValidator GetAadIssuerValidator(string aadAuthority, HttpClient httpClient, Func configurationManagerProvider) + { + if (string.IsNullOrEmpty(aadAuthority)) + throw LogHelper.LogArgumentNullException(nameof(aadAuthority)); + + if (configurationManagerProvider != null) + return new AadIssuerValidator( + httpClient, + aadAuthority, + configurationManagerProvider); + + if (s_issuerValidators.TryGetValue(aadAuthority, out AadIssuerValidator aadIssuerValidator)) + return aadIssuerValidator; + + s_issuerValidators[aadAuthority] = new AadIssuerValidator( + httpClient, + aadAuthority); + + return s_issuerValidators[aadAuthority]; } private static string CreateV1Authority(string aadV2Authority) @@ -306,11 +350,58 @@ private static bool IsValidIssuer(string validIssuerTemplate, string tenantId, s } } - private BaseConfigurationManager GetEffectiveConfigurationManager(SecurityToken securityToken) + private void SetEffectiveLKGIssuer(string aadIssuer, bool isV2Issuer, TimeSpan lastKnownGoodLifetime) { - var isV2 = securityToken.Issuer.EndsWith(V2EndpointSuffixWithTrailingSlash, StringComparison.OrdinalIgnoreCase) || + var issuerLKG = new IssuerLastKnownGood + { + Issuer = aadIssuer, + LastKnownGoodLifetime = lastKnownGoodLifetime + }; + + if (isV2Issuer) + _issuerLKGV2 = issuerLKG; + else + _issuerLKGV1 = issuerLKG; + } + + private string GetEffectiveLKGIssuer(bool isV2Issuer) + { + var effectiveLKGIssuer = isV2Issuer ? _issuerLKGV2 : _issuerLKGV1; + if (effectiveLKGIssuer != null && effectiveLKGIssuer.IsValid) + { + return effectiveLKGIssuer.Issuer; + } + + return null; + } + + private static bool IsV2Issuer(SecurityToken securityToken) + { + return securityToken.Issuer.EndsWith(V2EndpointSuffixWithTrailingSlash, StringComparison.OrdinalIgnoreCase) || securityToken.Issuer.EndsWith(V2EndpointSuffix, StringComparison.OrdinalIgnoreCase); - return isV2 ? ConfigurationManagerV2 : ConfigurationManagerV1; + } + + private BaseConfigurationManager GetEffectiveConfigurationManager(bool isV2Issuer) + { + if (_configurationManagerProvider != null) + { + var aadAuthority = isV2Issuer ? AadAuthorityV2 : AadAuthorityV1; + var configurationManager = _configurationManagerProvider(aadAuthority); + + if (configurationManager != null) + return configurationManager; + } + + // If no provider or provider returned null, fallback to previous strategy + return isV2Issuer ? ConfigurationManagerV2 : ConfigurationManagerV1; + } + + private static async Task GetBaseConfiguration(BaseConfigurationManager configurationManager, TokenValidationParameters validationParameters) + { + if (validationParameters.RefreshBeforeValidation) + configurationManager.RequestRefresh(); + + return await configurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false); } /// Gets the tenant ID from a token. diff --git a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/IssuerLastKnownGood.cs b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/IssuerLastKnownGood.cs new file mode 100644 index 0000000000..651b277a9f --- /dev/null +++ b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/IssuerLastKnownGood.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.Tokens; + +namespace Microsoft.IdentityModel.Validators +{ + /// + /// Class representing the last known good for issuer. + /// + internal class IssuerLastKnownGood + { + private string _issuer; + private TimeSpan _lastKnownGoodLifetime; + private DateTime? _lastKnownGoodConfigFirstUse = null; + + /// + /// Gets or sets the issuer value. + /// + public string Issuer + { + get + { + return _issuer; + } + set + { + if (value == null) + throw LogHelper.LogArgumentNullException(nameof(value)); + + _lastKnownGoodConfigFirstUse = DateTime.UtcNow; + _issuer = value; + } + } + + /// + /// Gets or sets the last known good lifetime. + /// + public TimeSpan LastKnownGoodLifetime + { + get { return _lastKnownGoodLifetime; } + set + { + if (value < TimeSpan.Zero) + throw LogHelper.LogExceptionMessage(new ArgumentOutOfRangeException(nameof(value), LogHelper.FormatInvariant(LogMessages.IDX40008, value))); + + _lastKnownGoodLifetime = value; + } + } + + /// + /// Gets an indicator whether the value is still within its lifetime and is valid. + /// + public bool IsValid + { + get + { + return _lastKnownGoodConfigFirstUse + LastKnownGoodLifetime > DateTime.UtcNow; + } + } + + } +} diff --git a/src/Microsoft.IdentityModel.Validators/LogMessages.cs b/src/Microsoft.IdentityModel.Validators/LogMessages.cs index e8edf46300..9279b2e1e4 100644 --- a/src/Microsoft.IdentityModel.Validators/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Validators/LogMessages.cs @@ -23,5 +23,6 @@ internal static class LogMessages public const string IDX40004 = "IDX40004: Token issuer: '{0}', does not contain the `tid` or `tenantId` claim present in the token: '{1}'."; public const string IDX40005 = "IDX40005: Token issuer: '{0}', does not match the signing key issuer: '{1}'."; public const string IDX40007 = "IDX40007: RequireSignedTokens property on ValidationParameters is set to true, but the issuer signing key is null."; + public const string IDX40008 = "IDX40008: When setting LastKnownGoodLifetime, the value must be greater than or equal to zero. value: '{0}'."; } } diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs index 3b792410ea..5075cbf21c 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs @@ -8,6 +8,7 @@ using System.IdentityModel.Tokens.Jwt.Tests; using System.IO; using System.Linq; +using System.Net.Http; using System.Runtime.InteropServices; using System.Security.Claims; using System.Security.Cryptography; @@ -3474,6 +3475,13 @@ public async Task ValidateJWSWithConfigAsync(JwtTheoryData theoryData) var context = TestUtilities.WriteHeader($"{this}.ValidateJWSWithConfigAsync", theoryData); try { + // clear up static state. + AadIssuerValidator.s_issuerValidators[Default.AadV1Authority] = new AadIssuerValidator(null, Default.AadV1Authority); + + // previous instance is captured in a closure during theorydata set setup. + if (theoryData.ValidationParameters.IssuerValidator != null) + theoryData.ValidationParameters.IssuerValidator = AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).Validate; + var handler = new JsonWebTokenHandler(); var jwt = handler.ReadJsonWebToken(theoryData.Token); AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).ConfigurationManagerV1 = theoryData.ValidationParameters.ConfigurationManager; @@ -3539,7 +3547,36 @@ public void ValidateJWSWithLastKnownGood(JwtTheoryData theoryData) var context = TestUtilities.WriteHeader($"{this}.ValidateJWSWithLastKnownGood", theoryData); try { + // clear up static state. + AadIssuerValidator.s_issuerValidators[Default.AadV1Authority] = new AadIssuerValidator(null, Default.AadV1Authority); + + // previous instance is captured in a closure during theorydata set setup. + if (theoryData.ValidationParameters.IssuerValidator != null) + theoryData.ValidationParameters.IssuerValidator = AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).Validate; + var handler = new JsonWebTokenHandler(); + + if (theoryData.SetupIssuerLkg) + { + // make a valid pass to initiate issuer LKG. + var issuerValidator = AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority); + issuerValidator.ConfigurationManagerV1 = theoryData.SetupIssuerLkgConfigurationManager; + + var previousValidateWithLKG = theoryData.ValidationParameters.ValidateWithLKG; + theoryData.ValidationParameters.ValidateWithLKG = false; + + var setupValidationResult = handler.ValidateTokenAsync(theoryData.Token, theoryData.ValidationParameters).Result; + + theoryData.ValidationParameters.ValidateWithLKG = previousValidateWithLKG; + + if (setupValidationResult.Exception != null) + { + if (setupValidationResult.IsValid) + context.AddDiff("setupValidationResult.IsValid, setupValidationResult.Exception != null"); + throw setupValidationResult.Exception; + } + } + AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).ConfigurationManagerV1 = theoryData.ValidationParameters.ConfigurationManager; var validationResult = handler.ValidateTokenAsync(theoryData.Token, theoryData.ValidationParameters).Result; if (validationResult.Exception != null) @@ -3568,7 +3605,35 @@ public void ValidateJWEWithLastKnownGood(JwtTheoryData theoryData) var context = TestUtilities.WriteHeader($"{this}.ValidateJWEWithLastKnownGood", theoryData); try { + // clear up static state. + AadIssuerValidator.s_issuerValidators[Default.AadV1Authority] = new AadIssuerValidator(null, Default.AadV1Authority); + + // previous instance is captured in a closure during theorydata set setup. + if (theoryData.ValidationParameters.IssuerValidator != null) + theoryData.ValidationParameters.IssuerValidator = AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).Validate; + var handler = new JsonWebTokenHandler(); + if (theoryData.SetupIssuerLkg) + { + // make a valid pass to initiate issuer LKG. + var issuerValidator = AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority); + issuerValidator.ConfigurationManagerV1 = theoryData.SetupIssuerLkgConfigurationManager; + + var previousValidateWithLKG = theoryData.ValidationParameters.ValidateWithLKG; + theoryData.ValidationParameters.ValidateWithLKG = false; + + var setupValidationResult = handler.ValidateTokenAsync(theoryData.Token, theoryData.ValidationParameters).Result; + + theoryData.ValidationParameters.ValidateWithLKG = previousValidateWithLKG; + + if (setupValidationResult.Exception != null) + { + if (setupValidationResult.IsValid) + context.AddDiff("setupValidationResult.IsValid, setupValidationResult.Exception != null"); + throw setupValidationResult.Exception; + } + } + AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).ConfigurationManagerV1 = theoryData.ValidationParameters.ConfigurationManager; var validationResult = handler.ValidateTokenAsync(theoryData.Token, theoryData.ValidationParameters).Result; if (validationResult.Exception != null) diff --git a/test/Microsoft.IdentityModel.TestUtils/MockConfigurationManager.cs b/test/Microsoft.IdentityModel.TestUtils/MockConfigurationManager.cs index 0e004c0cf2..82c0360e0a 100644 --- a/test/Microsoft.IdentityModel.TestUtils/MockConfigurationManager.cs +++ b/test/Microsoft.IdentityModel.TestUtils/MockConfigurationManager.cs @@ -20,6 +20,15 @@ public class MockConfigurationManager : BaseConfigurationManager, IConfigurat private bool _firstGet = true; private Exception _exToThrowOnFirstGet; + /// + /// Gets or sets the refreshed configuration. Use with RequestRefresh to simulate data transmission. + /// + public T RefreshedConfiguration + { + get { return _refreshedConfiguration; } + set { _refreshedConfiguration = value; } + } + /// /// Initializes an new instance of with a Configuration instance. /// diff --git a/test/Microsoft.IdentityModel.Validators.Tests/MicrosoftIdentityIssuerValidatorTest.cs b/test/Microsoft.IdentityModel.Validators.Tests/MicrosoftIdentityIssuerValidatorTest.cs index d98fa9a675..945604ae9a 100644 --- a/test/Microsoft.IdentityModel.Validators.Tests/MicrosoftIdentityIssuerValidatorTest.cs +++ b/test/Microsoft.IdentityModel.Validators.Tests/MicrosoftIdentityIssuerValidatorTest.cs @@ -7,10 +7,12 @@ using System.IdentityModel.Tokens.Jwt; using System.Net.Http; using System.Security.Claims; +using System.Threading; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.TestUtils; using Microsoft.IdentityModel.Tokens; +using Newtonsoft.Json.Linq; using NSubstitute; using Xunit; @@ -34,6 +36,11 @@ private AadIssuerValidator CreateIssuerValidator(string authority) return AadIssuerValidator.GetAadIssuerValidator(authority, _httpClient); } + private AadIssuerValidator CreateIssuerValidatorWithConfigurationProvider(string authority, Func configurationProvider) + { + return AadIssuerValidator.GetAadIssuerValidator(authority, _httpClient, configurationProvider); + } + [Fact] public void GetIssuerValidator_NullOrEmptyAuthority_ThrowsException() { @@ -176,14 +183,23 @@ public void Validate_NullOrEmptyTenantId_ThrowsException() } [Theory] - [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.AuthorityCommonTenant, ValidatorConstants.AadIssuer)] - [InlineData(ValidatorConstants.TenantId, ValidatorConstants.AuthorityCommonTenant, ValidatorConstants.AadIssuer)] - [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.UsGovTenantId, ValidatorConstants.UsGovIssuer)] - [InlineData(ValidatorConstants.TenantId, ValidatorConstants.UsGovTenantId, ValidatorConstants.UsGovIssuer)] - public void Validate_IssuerMatchedInValidIssuer_ReturnsIssuer(string tidClaimType, string tenantId, string issuer) + [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.AuthorityCommonTenant, ValidatorConstants.AadIssuer, false)] + [InlineData(ValidatorConstants.TenantId, ValidatorConstants.AuthorityCommonTenant, ValidatorConstants.AadIssuer, false)] + [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.UsGovTenantId, ValidatorConstants.UsGovIssuer, false)] + [InlineData(ValidatorConstants.TenantId, ValidatorConstants.UsGovTenantId, ValidatorConstants.UsGovIssuer, false)] + [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.AuthorityCommonTenant, ValidatorConstants.AadIssuer, true)] + [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) { var context = new CompareContext(); - var validator = new AadIssuerValidator(_httpClient, issuer); + AadIssuerValidator validator = null; + if (!useConfigurationManagerProvider) + validator = new AadIssuerValidator(_httpClient, issuer); + else + validator = new AadIssuerValidator(_httpClient, issuer, x => null); + var tidClaim = new Claim(tidClaimType, tenantId); var issClaim = new Claim(ValidatorConstants.ClaimNameIss, issuer); @@ -218,12 +234,20 @@ public void Validate_NoHttpclientFactory_ReturnsIssuer(string tidClaimType, stri } [Theory] - [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.TenantIdAsGuid, ValidatorConstants.V1Issuer)] - [InlineData(ValidatorConstants.TenantId, ValidatorConstants.TenantIdAsGuid, ValidatorConstants.V1Issuer)] - public void Validate_IssuerMatchedInValidV1Issuer_ReturnsIssuer(string tidClaimType, string tenantId, string issuer) + [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.TenantIdAsGuid, ValidatorConstants.V1Issuer, false)] + [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) { var context = new CompareContext(); - var validator = new AadIssuerValidator(_httpClient, issuer); + + AadIssuerValidator validator = null; + if (useConfigurationProvider == false) + validator = new AadIssuerValidator(_httpClient, issuer); + else + validator = new AadIssuerValidator(_httpClient, issuer, x => null); + var tidClaim = new Claim(tidClaimType, tenantId); var issClaim = new Claim(ValidatorConstants.ClaimNameIss, issuer); @@ -242,12 +266,20 @@ public void Validate_IssuerMatchedInValidV1Issuer_ReturnsIssuer(string tidClaimT } [Theory] - [InlineData(ValidatorConstants.ClaimNameTid)] - [InlineData(ValidatorConstants.TenantId)] - public void Validate_IssuerMatchedInValidIssuers_ReturnsIssuer(string tidClaimType) + [InlineData(ValidatorConstants.ClaimNameTid, false)] + [InlineData(ValidatorConstants.TenantId, false)] + [InlineData(ValidatorConstants.ClaimNameTid, true)] + [InlineData(ValidatorConstants.TenantId, true)] + public void Validate_IssuerMatchedInValidIssuers_ReturnsIssuer(string tidClaimType, bool useConfigurationProvider) { var context = new CompareContext(); - var validator = new AadIssuerValidator(_httpClient, ValidatorConstants.AadIssuer); + + AadIssuerValidator validator = null; + if (useConfigurationProvider == false) + validator = new AadIssuerValidator(_httpClient, ValidatorConstants.AadIssuer); + else + validator = new AadIssuerValidator(_httpClient, ValidatorConstants.AadIssuer, x => null); + var tidClaim = new Claim(tidClaimType, ValidatorConstants.TenantIdAsGuid); var issClaim = new Claim(ValidatorConstants.ClaimNameIss, ValidatorConstants.AadIssuer); @@ -264,33 +296,48 @@ public void Validate_IssuerMatchedInValidIssuers_ReturnsIssuer(string tidClaimTy } [Theory] - [InlineData(ValidatorConstants.ClaimNameTid)] - [InlineData(ValidatorConstants.TenantId)] - public void Validate_IssuerNotInTokenValidationParameters_ReturnsIssuer(string tidClaimType) + [InlineData(ValidatorConstants.ClaimNameTid, false)] + [InlineData(ValidatorConstants.TenantId, false)] + [InlineData(ValidatorConstants.ClaimNameTid, true)] + [InlineData(ValidatorConstants.TenantId, true)] + public void Validate_IssuerNotInTokenValidationParameters_ReturnsIssuer(string tidClaimType, bool useConfigurationProvider) { var context = new CompareContext(); - var validator = new AadIssuerValidator(_httpClient, ValidatorConstants.AadIssuer); + AadIssuerValidator validator = null; + if (useConfigurationProvider == false) + validator = new AadIssuerValidator(_httpClient, ValidatorConstants.AadIssuer); + else + validator = new AadIssuerValidator(_httpClient, ValidatorConstants.AadIssuer, authority => new MockConfigurationManager(new OpenIdConnectConfiguration() { Issuer = ValidatorConstants.AadIssuer })); + var tidClaim = new Claim(tidClaimType, ValidatorConstants.TenantIdAsGuid); var issClaim = new Claim(ValidatorConstants.ClaimNameIss, ValidatorConstants.AadIssuer); var jwtSecurityToken = new JwtSecurityToken(issuer: ValidatorConstants.AadIssuer, claims: new[] { issClaim, tidClaim }); - var tokenValidationParams = new TokenValidationParameters() { ConfigurationManager = new MockConfigurationManager(new OpenIdConnectConfiguration() { Issuer = ValidatorConstants.AadIssuer }) }; - var actualIssuer = validator.Validate(ValidatorConstants.AadIssuer, jwtSecurityToken, tokenValidationParams); + var actualIssuer = validator.Validate(ValidatorConstants.AadIssuer, jwtSecurityToken, new TokenValidationParameters()); IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); } [Theory] - [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.AadIssuer)] - [InlineData(ValidatorConstants.TenantId, ValidatorConstants.AadIssuer)] - [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.V1Issuer)] - [InlineData(ValidatorConstants.TenantId, ValidatorConstants.V1Issuer)] - public void ValidateJsonWebToken_ReturnsIssuer(string tidClaimType, string issuer) + [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.AadIssuer, false)] + [InlineData(ValidatorConstants.TenantId, ValidatorConstants.AadIssuer, false)] + [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.V1Issuer, false)] + [InlineData(ValidatorConstants.TenantId, ValidatorConstants.V1Issuer, false)] + [InlineData(ValidatorConstants.ClaimNameTid, ValidatorConstants.AadIssuer, true)] + [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) { + AadIssuerValidator validator = null; + if (useConfigurationProvider == false) + validator = new AadIssuerValidator(_httpClient, issuer); + else + validator = new AadIssuerValidator(_httpClient, issuer, authority => new MockConfigurationManager(new OpenIdConnectConfiguration() { Issuer = issuer })); + var context = new CompareContext(); - var validator = new AadIssuerValidator(_httpClient, issuer); var tidClaim = new Claim(tidClaimType, ValidatorConstants.TenantIdAsGuid); var issClaim = new Claim(ValidatorConstants.ClaimNameIss, issuer); @@ -299,29 +346,32 @@ public void ValidateJsonWebToken_ReturnsIssuer(string tidClaimType, string issue claims.Add(issClaim); var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor(Default.SymmetricSigningCredentials, claims))); - var tokenValidationParams = new TokenValidationParameters() { ConfigurationManager = new MockConfigurationManager(new OpenIdConnectConfiguration() { Issuer = issuer }) }; - - var actualIssuer = validator.Validate(issuer, jsonWebToken, tokenValidationParams); + var actualIssuer = validator.Validate(issuer, jsonWebToken, new TokenValidationParameters()); IdentityComparer.AreEqual(issuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); } [Theory] - [InlineData(ValidatorConstants.ClaimNameTid)] - [InlineData(ValidatorConstants.TenantId)] - public void Validate_V1IssuerNotInTokenValidationParameters_ReturnsV1Issuer(string tidClaimType) + [InlineData(ValidatorConstants.ClaimNameTid, false)] + [InlineData(ValidatorConstants.TenantId, false)] + [InlineData(ValidatorConstants.ClaimNameTid, true)] + [InlineData(ValidatorConstants.TenantId, true)] + public void Validate_V1IssuerNotInTokenValidationParameters_ReturnsV1Issuer(string tidClaimType, bool useConfigurationProvider) { + AadIssuerValidator validator = null; + if (useConfigurationProvider == false) + validator = new AadIssuerValidator(_httpClient, ValidatorConstants.V1Issuer); + else + validator = new AadIssuerValidator(_httpClient, ValidatorConstants.V1Issuer, authority => new MockConfigurationManager(new OpenIdConnectConfiguration() { Issuer = ValidatorConstants.V1Issuer })); + var context = new CompareContext(); - var validator = new AadIssuerValidator(_httpClient, ValidatorConstants.V1Issuer); var tidClaim = new Claim(tidClaimType, ValidatorConstants.TenantIdAsGuid); var issClaim = new Claim(ValidatorConstants.ClaimNameIss, ValidatorConstants.V1Issuer); var jwtSecurityToken = new JwtSecurityToken(issuer: ValidatorConstants.V1Issuer, claims: new[] { issClaim, tidClaim }); - var tokenValidationParams = new TokenValidationParameters() { ConfigurationManager = new MockConfigurationManager(new OpenIdConnectConfiguration() { Issuer = ValidatorConstants.V1Issuer }) }; - - var actualIssuer = validator.Validate(ValidatorConstants.V1Issuer, jwtSecurityToken, tokenValidationParams); + var actualIssuer = validator.Validate(ValidatorConstants.V1Issuer, jwtSecurityToken, new TokenValidationParameters()); IdentityComparer.AreEqual(ValidatorConstants.V1Issuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); @@ -543,6 +593,218 @@ public void Validate_FromB2CAuthority_WithTfpIssuer_ThrowsException() IdentityComparer.AreEqual(LogMessages.IDX40002, exception.Message, context); TestUtilities.AssertFailIfErrors(context); } + + [Theory] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, false)] + [InlineData(false, true)] + public void Validate_WithAuthorityUsingConfigurationProvider(bool isV2Token, bool isV2Authority) + { + var configurationManagerProvider = (string authority) => + { + var configManagerMap = new Dictionary + { + { + ValidatorConstants.AuthorityV1, + new MockConfigurationManager( + new OpenIdConnectConfiguration() + { + Issuer = ValidatorConstants.AadIssuerV1CommonAuthority + }) + }, + { + ValidatorConstants.AuthorityCommonTenantWithV2, + new MockConfigurationManager( + new OpenIdConnectConfiguration() + { + Issuer = ValidatorConstants.AadIssuerV2CommonAuthority + }) + } + }; + + return configManagerMap[authority]; + }; + + var context = new CompareContext(); + var tidClaim = new Claim(ValidatorConstants.ClaimNameTid, ValidatorConstants.TenantIdAsGuid); + + var tokenIssuer = isV2Token ? ValidatorConstants.AadIssuer : ValidatorConstants.V1Issuer; + var issClaim = new Claim(ValidatorConstants.ClaimNameIss, tokenIssuer); + var jwtSecurityToken = new JwtSecurityToken(issuer: tokenIssuer, claims: new[] { issClaim, tidClaim }); + + var authority = isV2Authority ? ValidatorConstants.AuthorityCommonTenantWithV2 : ValidatorConstants.AuthorityV1; + var aadIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(authority, _httpClient, configurationManagerProvider); + + var actualIssuer = aadIssuerValidator.Validate(tokenIssuer, jwtSecurityToken, new TokenValidationParameters()); + + IdentityComparer.AreEqual(tokenIssuer, actualIssuer, context); + TestUtilities.AssertFailIfErrors(context); + } + + [Fact] + public void Validate_UsesLKGWithoutConfigurationProvider() + { + var context = new CompareContext(); + var tidClaim = new Claim(ValidatorConstants.ClaimNameTid, ValidatorConstants.TenantIdAsGuid); + + var v2TokenIssuer = ValidatorConstants.AadIssuer; + var issClaim = new Claim(ValidatorConstants.ClaimNameIss, v2TokenIssuer); + var jwtSecurityToken = new JwtSecurityToken(issuer: v2TokenIssuer, claims: new[] { issClaim, tidClaim }); + + var v2Authority = ValidatorConstants.AuthorityCommonTenantWithV2; + var aadIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(v2Authority, _httpClient); + + // set config to a mock and assert on LKG being null + var v2Configuration = new OpenIdConnectConfiguration + { + Issuer = ValidatorConstants.AadIssuerV2CommonAuthority + }; + + var v2ConfigurationRefreshed = new OpenIdConnectConfiguration + { + Issuer = "hxxp://brokenissuer/{tenantid}" + }; + + aadIssuerValidator.ConfigurationManagerV2 = new MockConfigurationManager(v2Configuration); + + // set LKG + var actualIssuer = aadIssuerValidator.Validate(v2TokenIssuer, jwtSecurityToken, new TokenValidationParameters()); + IdentityComparer.AreEqual(v2TokenIssuer, actualIssuer, context); + TestUtilities.AssertFailIfErrors(context); + + // replace config with broken issuer and validate with LKG + ((MockConfigurationManager)aadIssuerValidator.ConfigurationManagerV2).RefreshedConfiguration = v2ConfigurationRefreshed; + aadIssuerValidator.ConfigurationManagerV2.RequestRefresh(); + + actualIssuer = aadIssuerValidator.Validate(v2TokenIssuer, jwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); + IdentityComparer.AreEqual(v2TokenIssuer, actualIssuer, context); + TestUtilities.AssertFailIfErrors(context); + } + + [Fact] + public void Validate_UsesLKGWithConfigurationProvider() + { + var v1Configuration = new OpenIdConnectConfiguration + { + Issuer = ValidatorConstants.AadIssuerV1CommonAuthority + }; + + var v1ConfigurationRefreshed = new OpenIdConnectConfiguration + { + Issuer = "hxxp://brokenissuer/{tenantid}" + }; + + var v2Configuration = new OpenIdConnectConfiguration + { + Issuer = ValidatorConstants.AadIssuerV2CommonAuthority + }; + + var v2ConfigurationRefreshed = new OpenIdConnectConfiguration + { + Issuer = "hxxp://brokenissuer/{tenantid}" + }; + + var v1ConfigurationManager = new MockConfigurationManager(v1Configuration); + var v2ConfigurationManager = new MockConfigurationManager(v2Configuration); + + var configurationManagerProvider = (string authority) => + { + var configManagerMap = new Dictionary + { + { + ValidatorConstants.AuthorityV1, + v1ConfigurationManager + }, + { + ValidatorConstants.AuthorityCommonTenantWithV2, + v2ConfigurationManager + } + }; + + return configManagerMap[authority]; + }; + + var context = new CompareContext(); + var tidClaim = new Claim(ValidatorConstants.ClaimNameTid, ValidatorConstants.TenantIdAsGuid); + + var v2TokenIssuer = ValidatorConstants.AadIssuer; + var issClaim = new Claim(ValidatorConstants.ClaimNameIss, v2TokenIssuer); + var jwtSecurityToken = new JwtSecurityToken(issuer: v2TokenIssuer, claims: new[] { issClaim, tidClaim }); + + var v2Authority = ValidatorConstants.AuthorityCommonTenantWithV2; + var v1Authority = ValidatorConstants.AuthorityCommonTenant; + var aadIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(v2Authority, _httpClient, configurationManagerProvider); + var v1AadIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(v1Authority, _httpClient, configurationManagerProvider); + + // set LKG + var actualIssuer = aadIssuerValidator.Validate(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); + TestUtilities.AssertFailIfErrors(context); + + var v1TokenIssuer = ValidatorConstants.V1Issuer; + issClaim = new Claim(ValidatorConstants.ClaimNameIss, v1TokenIssuer); + var v1JwtSecurityToken = new JwtSecurityToken(issuer: v1TokenIssuer, claims: new[] { issClaim, tidClaim }); + + // before testing v1 LKG setup v1 LKG for v2 manager for cross version validation + _ = aadIssuerValidator.Validate(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters()); + + // V1 token and authority behaves like v2 token and authority + actualIssuer = v1AadIssuerValidator.Validate(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 + v1ConfigurationManager.RefreshedConfiguration = v1ConfigurationRefreshed; + v1ConfigurationManager.RequestRefresh(); + + actualIssuer = v1AadIssuerValidator.Validate(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); + TestUtilities.AssertFailIfErrors(context); + + // if LKG not valid validation fails + // set confgimanager lkg lifetime to 1ms + // validate successfully to set LKG + // wait 1ms, validate with expired LKG + v1ConfigurationManager.RefreshedConfiguration = v1Configuration; + v1ConfigurationManager.RequestRefresh(); + + v1ConfigurationManager.LastKnownGoodLifetime = TimeSpan.FromMilliseconds(1); + actualIssuer = aadIssuerValidator.Validate(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters()); + Thread.Sleep(TimeSpan.FromMilliseconds(1)); + + var securityExceptionThrown = false; + var exceptionMessage = string.Empty; + try + { + _ = aadIssuerValidator.Validate(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); + } + catch (SecurityTokenInvalidIssuerException securityException) + { + securityExceptionThrown = true; + exceptionMessage = securityException.Message; + } + + IdentityComparer.AreEqual(true, securityExceptionThrown, context); + IdentityComparer.AreEqual("IDX40001: Issuer: 'https://sts.windows.net/f645ad92-e38d-4d1a-b510-d1b09a74a8ca/', does not match any of the valid issuers provided for this application. ", exceptionMessage, context); + TestUtilities.AssertFailIfErrors(context); + } } } diff --git a/test/Microsoft.IdentityModel.Validators.Tests/ValidatorConstants.cs b/test/Microsoft.IdentityModel.Validators.Tests/ValidatorConstants.cs index 4bfd8dbd52..f6ec7144f8 100644 --- a/test/Microsoft.IdentityModel.Validators.Tests/ValidatorConstants.cs +++ b/test/Microsoft.IdentityModel.Validators.Tests/ValidatorConstants.cs @@ -31,6 +31,7 @@ internal class ValidatorConstants public const string UsGovIssuer = "https://login.microsoftonline.us/" + UsGovTenantId + "/v2.0"; public const string UsGovTenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47"; public const string V1Issuer = "https://sts.windows.net/f645ad92-e38d-4d1a-b510-d1b09a74a8ca/"; + public const string AadIssuerV1CommonAuthority = "https://sts.windows.net/{tenantid}/"; public const string AadIssuerV2CommonAuthority = AadInstance + "/{tenantid}/v2.0"; public const string B2CSignUpSignInUserFlow = "b2c_1_susi"; diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs index 266ab08058..ea2c58a738 100644 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs +++ b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs @@ -2218,8 +2218,38 @@ public void ValidateJWSWithLastKnownGood(JwtTheoryData theoryData) try { + // clear up static state. + AadIssuerValidator.s_issuerValidators[Default.AadV1Authority] = new AadIssuerValidator(null, Default.AadV1Authority); + + // previous instance is captured in a closure during theorydata set setup. + if (theoryData.ValidationParameters.IssuerValidator != null) + theoryData.ValidationParameters.IssuerValidator = AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).Validate; + + var handler = new JwtSecurityTokenHandler(); + + if (theoryData.SetupIssuerLkg) + { + // make a valid pass to initiate issuer LKG. + var issuerValidator = AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority); + issuerValidator.ConfigurationManagerV1 = theoryData.SetupIssuerLkgConfigurationManager; + + var previousValidateWithLKG = theoryData.ValidationParameters.ValidateWithLKG; + theoryData.ValidationParameters.ValidateWithLKG = false; + + var setupValidationResult = handler.ValidateTokenAsync(theoryData.Token, theoryData.ValidationParameters).Result; + + theoryData.ValidationParameters.ValidateWithLKG = previousValidateWithLKG; + + if (setupValidationResult.Exception != null) + { + if (setupValidationResult.IsValid) + context.AddDiff("setupValidationResult.IsValid, setupValidationResult.Exception != null"); + throw setupValidationResult.Exception; + } + } + AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).ConfigurationManagerV1 = theoryData.ValidationParameters.ConfigurationManager; - new JwtSecurityTokenHandler().ValidateToken(theoryData.Token, theoryData.ValidationParameters, out _); + handler.ValidateToken(theoryData.Token, theoryData.ValidationParameters, out _); theoryData.ExpectedException.ProcessNoException(context); } catch (Exception ex) @@ -2239,8 +2269,38 @@ public void ValidateJWEWithLastKnownGood(JwtTheoryData theoryData) try { + // clear up static state. + AadIssuerValidator.s_issuerValidators[Default.AadV1Authority] = new AadIssuerValidator(null, Default.AadV1Authority); + + // previous instance is captured in a closure during theorydata set setup. + if (theoryData.ValidationParameters.IssuerValidator != null) + theoryData.ValidationParameters.IssuerValidator = AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).Validate; + + var handler = new JwtSecurityTokenHandler(); + + if (theoryData.SetupIssuerLkg) + { + // make a valid pass to initiate issuer LKG. + var issuerValidator = AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority); + issuerValidator.ConfigurationManagerV1 = theoryData.SetupIssuerLkgConfigurationManager; + + var previousValidateWithLKG = theoryData.ValidationParameters.ValidateWithLKG; + theoryData.ValidationParameters.ValidateWithLKG = false; + + var setupValidationResult = handler.ValidateTokenAsync(theoryData.Token, theoryData.ValidationParameters).Result; + + theoryData.ValidationParameters.ValidateWithLKG = previousValidateWithLKG; + + if (setupValidationResult.Exception != null) + { + if (setupValidationResult.IsValid) + context.AddDiff("setupValidationResult.IsValid, setupValidationResult.Exception != null"); + throw setupValidationResult.Exception; + } + } + AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).ConfigurationManagerV1 = theoryData.ValidationParameters.ConfigurationManager; - new JwtSecurityTokenHandler().ValidateToken(theoryData.Token, theoryData.ValidationParameters, out _); + handler.ValidateToken(theoryData.Token, theoryData.ValidationParameters, out _); theoryData.ExpectedException.ProcessNoException(context); } catch (Exception ex) diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTestDatasets.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTestDatasets.cs index 81fe3b0bd4..64344d495b 100644 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTestDatasets.cs +++ b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTestDatasets.cs @@ -556,6 +556,8 @@ public static TheoryData ValidateJwsWithLastKnownGoodTheoryData { TestId = nameof(Default.AsymmetricJws) + "_ConfigIssuerInvalid_AadIssuerValidatorThrow_LKGValid", Token = Default.AadAsymmetricJws, + SetupIssuerLkg = true, + SetupIssuerLkgConfigurationManager = new MockConfigurationManager(validConfig), ValidationParameters = new TokenValidationParameters { ConfigurationManager = new MockConfigurationManager(invalidIssuerConfig, validConfig), @@ -650,6 +652,8 @@ public static TheoryData ValidateJwsWithLastKnownGoodTheoryData { TestId = nameof(Default.AsymmetricJws) + "_ConfigInvalid_AadIssuerValidatorThrow_LKGSucceeds_RequestRefreshIssuerInvalid", Token = Default.AadAsymmetricJws, + SetupIssuerLkg = true, + SetupIssuerLkgConfigurationManager = new MockConfigurationManager(validConfig), ValidationParameters = new TokenValidationParameters { ConfigurationManager = new MockConfigurationManager(invalidIssuerConfig, validConfig, invalidIssuerConfig), @@ -962,6 +966,8 @@ public static TheoryData ValidateJWEWithLastKnownGoodTheoryData { TestId = nameof(aadJwe) + "_ConfigIssuerInvalid_AadIssuerValidatorThrow_LKGValid", Token = aadJwe, + SetupIssuerLkg = true, + SetupIssuerLkgConfigurationManager = new MockConfigurationManager(validConfig), ValidationParameters = new TokenValidationParameters { ConfigurationManager = new MockConfigurationManager(invalidIssuerConfig, validConfig), @@ -1047,6 +1053,8 @@ public static TheoryData ValidateJWEWithLastKnownGoodTheoryData { TestId = nameof(aadJwe) + "_ConfigInvalid_AadIssuerValidatorThrow_LKGSucceeds_RequestRefreshIssuerInvalid", Token = aadJwe, + SetupIssuerLkg = true, + SetupIssuerLkgConfigurationManager = new MockConfigurationManager(validConfig), ValidationParameters = new TokenValidationParameters { ConfigurationManager = new MockConfigurationManager(invalidIssuerConfig, validConfig, invalidIssuerConfig), diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTheoryData.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTheoryData.cs index b00ea98864..28590221a0 100644 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTheoryData.cs +++ b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTheoryData.cs @@ -34,5 +34,9 @@ public JwtTheoryData() { } public string TokenTypeHeader { get; set; } public bool ShouldSetLastKnownConfiguration { get; set; } + + public bool SetupIssuerLkg { get; set; } = false; + + public BaseConfigurationManager SetupIssuerLkgConfigurationManager { get; set; } } }