diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/InternalAPI.Unshipped.txt b/src/Microsoft.IdentityModel.JsonWebTokens/InternalAPI.Unshipped.txt index cb23d777b2..c3c3a93b29 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/InternalAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.JsonWebTokens/InternalAPI.Unshipped.txt @@ -1,3 +1,5 @@ +override Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.SecurityToken token, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> +override Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateTokenAsync(string token, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.CreateToken(string payload, Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor tokenDescriptor) -> string static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.EncryptToken(byte[] innerTokenUtf8Bytes, Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, string compressionAlgorithm, System.Collections.Generic.IDictionary additionalHeaderClaims, string tokenType, bool includeKeyIdInHeader) -> string static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.EncryptToken(byte[] innerTokenUtf8Bytes, Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor tokenDescriptor) -> string diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs index 1bb3e578ca..8a2e1bda0e 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs @@ -34,7 +34,7 @@ public partial class JsonWebTokenHandler : TokenHandler /// Returned if 'token.Length' is greater than . /// Returned if is not a valid , /// Returned if the validationParameters.TokenReader delegate is not able to parse/read the token as a valid , - internal async Task> ValidateTokenAsync( + internal override async Task> ValidateTokenAsync( string token, ValidationParameters validationParameters, CallContext callContext, @@ -91,7 +91,7 @@ internal async Task> ValidateTokenAsync( } /// - internal async Task> ValidateTokenAsync( + internal override async Task> ValidateTokenAsync( SecurityToken token, ValidationParameters validationParameters, CallContext callContext, @@ -420,7 +420,7 @@ await ValidateJWSAsync(actorToken, actorParameters, configuration, callContext, try { issuerSigningKeyValidationResult = validationParameters.IssuerSigningKeyValidator( - jsonWebToken.SigningKey, jsonWebToken, validationParameters, configuration, callContext); + jsonWebToken.SigningKey, jsonWebToken, validationParameters, callContext); if (!issuerSigningKeyValidationResult.IsValid) return issuerSigningKeyValidationResult.UnwrapError().AddCurrentStackFrame(); diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt index 55944f8e39..bede622517 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt @@ -9,19 +9,19 @@ Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions.ValidatedConditions(string ValidatedAudience, Microsoft.IdentityModel.Tokens.ValidatedLifetime? ValidatedLifetime) -> void Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions.ValidatedLifetime.get -> Microsoft.IdentityModel.Tokens.ValidatedLifetime? Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions.ValidatedLifetime.set -> void -Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> -Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidateTokenAsync(string token, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> Microsoft.IdentityModel.Tokens.Saml.SamlValidationError Microsoft.IdentityModel.Tokens.Saml.SamlValidationError.SamlValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Exception innerException = null) -> void Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.CreateClaimsIdentity(Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken samlToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, string issuer) -> System.Security.Claims.ClaimsIdentity Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.StackFrames -Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> -Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateTokenAsync(string token, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> Microsoft.IdentityModel.Tokens.Saml2.Saml2ValidationError Microsoft.IdentityModel.Tokens.Saml2.Saml2ValidationError.Saml2ValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Exception innerException = null) -> void override Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.CreateClaimsIdentityInternal(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, string issuer) -> System.Security.Claims.ClaimsIdentity +override Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> +override Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidateTokenAsync(string token, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> override Microsoft.IdentityModel.Tokens.Saml.SamlValidationError.GetException() -> System.Exception override Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.CreateClaimsIdentityInternal(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, string issuer) -> System.Security.Claims.ClaimsIdentity +override Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> +override Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateTokenAsync(string token, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> override Microsoft.IdentityModel.Tokens.Saml2.Saml2ValidationError.GetException() -> System.Exception static Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.StackFrames.IssuerValidationFailed -> System.Diagnostics.StackFrame static Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.StackFrames.SignatureValidationFailed -> System.Diagnostics.StackFrame diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs index 2e073084f8..a18aa8eb1a 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs @@ -15,7 +15,7 @@ namespace Microsoft.IdentityModel.Tokens.Saml /// public partial class SamlSecurityTokenHandler : SecurityTokenHandler { - internal async Task> ValidateTokenAsync( + internal override async Task> ValidateTokenAsync( string token, ValidationParameters validationParameters, CallContext callContext, @@ -34,7 +34,7 @@ internal async Task> ValidateTokenAsync( return await ValidateTokenAsync(tokenReadingResult.UnwrapResult(), validationParameters, callContext, cancellationToken).ConfigureAwait(false); } - internal async Task> ValidateTokenAsync( + internal override async Task> ValidateTokenAsync( SecurityToken securityToken, ValidationParameters validationParameters, CallContext callContext, @@ -148,7 +148,6 @@ internal async Task> ValidateTokenAsync( samlToken.SigningKey, samlToken, validationParameters, - null, callContext); if (!issuerSigningKeyValidationResult.IsValid) diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs index 4b9f18d590..141506e115 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs @@ -16,7 +16,7 @@ namespace Microsoft.IdentityModel.Tokens.Saml2 /// public partial class Saml2SecurityTokenHandler : SecurityTokenHandler { - internal async Task> ValidateTokenAsync( + internal override async Task> ValidateTokenAsync( string token, ValidationParameters validationParameters, CallContext callContext, @@ -35,7 +35,7 @@ internal async Task> ValidateTokenAsync( return await ValidateTokenAsync(tokenReadingResult.UnwrapResult(), validationParameters, callContext, cancellationToken).ConfigureAwait(false); } - internal async Task> ValidateTokenAsync( + internal override async Task> ValidateTokenAsync( SecurityToken securityToken, ValidationParameters validationParameters, CallContext callContext, @@ -151,7 +151,6 @@ internal async Task> ValidateTokenAsync( samlToken.SigningKey, samlToken, validationParameters, - null, callContext); if (!issuerSigningKeyValidationResult.IsValid) diff --git a/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt index 14d2d3493a..fd67eb60ac 100644 --- a/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt @@ -68,6 +68,7 @@ static Microsoft.IdentityModel.Tokens.TokenTypeValidationError.NullParameter(str static Microsoft.IdentityModel.Tokens.Base64UrlEncoder.Decode(System.ReadOnlySpan strSpan, System.Span output) -> int static Microsoft.IdentityModel.Tokens.Utility.SerializeAsSingleCommaDelimitedString(System.Collections.Generic.IList strings) -> string static Microsoft.IdentityModel.Tokens.ValidationError.GetCurrentStackFrame(string filePath = "", int lineNumber = 0, int skipFrames = 1) -> System.Diagnostics.StackFrame +static Microsoft.IdentityModel.Tokens.Validators.ValidateIssuerSigningKey(Microsoft.IdentityModel.Tokens.SecurityKey securityKey, Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.ValidationResult static readonly Microsoft.IdentityModel.Tokens.LoggingEventId.TokenValidationFailed -> Microsoft.Extensions.Logging.EventId static readonly Microsoft.IdentityModel.Tokens.LoggingEventId.TokenValidationSucceeded -> Microsoft.Extensions.Logging.EventId static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.AlgorithmValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType @@ -84,3 +85,5 @@ static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.TokenIsNotS static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.TokenReplayValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.TokenTypeValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.XmlValidationFailed -> Microsoft.IdentityModel.Tokens.ValidationFailureType +virtual Microsoft.IdentityModel.Tokens.TokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.SecurityToken token, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> +virtual Microsoft.IdentityModel.Tokens.TokenHandler.ValidateTokenAsync(string token, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> diff --git a/src/Microsoft.IdentityModel.Tokens/TokenHandler.Internal.cs b/src/Microsoft.IdentityModel.Tokens/TokenHandler.Internal.cs new file mode 100644 index 0000000000..9bbade6008 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/TokenHandler.Internal.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Defines properties shared across all security token handlers. + /// + public abstract partial class TokenHandler + { + internal virtual Task> ValidateTokenAsync( + string token, + ValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + internal virtual Task> ValidateTokenAsync( + SecurityToken token, + ValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs b/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs index ffb8a97232..754a87eea3 100644 --- a/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs +++ b/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs @@ -12,7 +12,7 @@ namespace Microsoft.IdentityModel.Tokens /// /// Defines properties shared across all security token handlers. /// - public abstract class TokenHandler + public abstract partial class TokenHandler { private int _defaultTokenLifetimeInMinutes = DefaultTokenLifetimeInMinutes; private int _maximumTokenSizeInBytes = TokenValidationParameters.DefaultMaximumTokenSizeInBytes; diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs index 24fdaed4e5..00496f5ec5 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs @@ -20,6 +20,7 @@ internal class ValidationParameters private string _nameClaimType = ClaimsIdentity.DefaultNameClaimType; private string _roleClaimType = ClaimsIdentity.DefaultRoleClaimType; private Dictionary _instancePropertyBag; + private Dictionary _propertyBag; private IList _issuerSigningKeys; private IList _validIssuers; private IList _validTokenTypes; @@ -77,7 +78,9 @@ protected ValidationParameters(ValidationParameters other) LogTokenId = other.LogTokenId; NameClaimType = other.NameClaimType; NameClaimTypeRetriever = other.NameClaimTypeRetriever; - PropertyBag = other.PropertyBag; + foreach (var keyValue in other.PropertyBag) + PropertyBag[keyValue.Key] = keyValue.Value; + RefreshBeforeValidation = other.RefreshBeforeValidation; RoleClaimType = other.RoleClaimType; RoleClaimTypeRetriever = other.RoleClaimTypeRetriever; @@ -282,7 +285,10 @@ public IssuerSigningKeyValidationDelegate IssuerSigningKeyValidator /// Gets a that is unique to this instance. /// Calling will result in a new instance of this IDictionary. /// - public IDictionary InstancePropertyBag => _instancePropertyBag ??= new Dictionary(); + public IDictionary InstancePropertyBag => + _instancePropertyBag ?? + Interlocked.CompareExchange(ref _instancePropertyBag, [], null) ?? + _instancePropertyBag; /// /// Gets a value indicating if was called to obtain this instance. @@ -374,7 +380,9 @@ public string NameClaimType /// Gets or sets the that contains a collection of custom key/value pairs. /// This allows addition of parameters that could be used in custom token validation scenarios. /// - public IDictionary PropertyBag { get; } + public IDictionary PropertyBag => _propertyBag ?? + Interlocked.CompareExchange(ref _propertyBag, [], null) ?? + _propertyBag; /// /// A boolean to control whether configuration should be refreshed before validating a token. diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs index 6e4ef95132..8e2d22fbb9 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs @@ -16,7 +16,6 @@ internal record struct ValidatedSigningKeyLifetime(DateTime? ValidFrom, DateTime /// The security key to validate. /// The that is being validated. /// The to be used for validating the token. - /// The to be used for validation. /// The to be used for logging. /// A that contains the results of validating the issuer. /// This delegate is not expected to throw. @@ -24,7 +23,6 @@ internal delegate ValidationResult IssuerSigningKey SecurityKey signingKey, SecurityToken securityToken, ValidationParameters validationParameters, - BaseConfiguration? configuration, CallContext callContext); /// @@ -39,7 +37,6 @@ public static partial class Validators /// The that signed the . /// The being validated. /// The to be used for validating the token. - /// The to be used for validation. /// The to be used for logging. /// if 'securityKey' is null and ValidateIssuerSigningKey is true. /// if 'securityToken' is null and ValidateIssuerSigningKey is true. @@ -48,9 +45,6 @@ internal static ValidationResult ValidateIssuerSign SecurityKey securityKey, SecurityToken securityToken, ValidationParameters validationParameters, -#pragma warning disable CA1801 // Review unused parameters - BaseConfiguration? configuration, -#pragma warning restore CA1801 // Review unused parameters CallContext? callContext) { if (validationParameters == null) diff --git a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.Internal.cs b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.Internal.cs new file mode 100644 index 0000000000..983a2e8ddd --- /dev/null +++ b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.Internal.cs @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using System; +using Microsoft.IdentityModel.Tokens; +using System.Threading; +using Microsoft.IdentityModel.Logging; +using System.Diagnostics; + +namespace Microsoft.IdentityModel.Validators +{ + /// + /// Generic class that validates the issuer for either JsonWebTokens or JwtSecurityTokens issued from the Microsoft identity platform (AAD). + /// + public partial class AadIssuerValidator + { + /// + /// Validate the issuer for single and multi-tenant applications of various audiences (Work and School accounts, or Work and School accounts + + /// Personal accounts) and the various clouds. + /// + /// Issuer to validate (will be tenanted). + /// Received security token. + /// The to be used for validating the token. + /// + /// + /// An that contains either the issuer that was validated or an error. + /// An EXACT match is required. + internal async Task> ValidateIssuerAsync( + string issuer, + SecurityToken securityToken, + ValidationParameters validationParameters, +#pragma warning disable CA1801 // Review unused parameters + CallContext callContext, + CancellationToken cancellationToken) +#pragma warning restore CA1801 // Review unused parameters + { + _ = issuer ?? throw LogHelper.LogArgumentNullException(nameof(issuer)); + _ = securityToken ?? throw LogHelper.LogArgumentNullException(nameof(securityToken)); + _ = validationParameters ?? throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + string tenantId; + + try + { + tenantId = GetTenantIdFromToken(securityToken); + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types + { + return new IssuerValidationError( + new MessageDetail( + ex.Message, + LogHelper.MarkAsNonPII(issuer)), + ValidationFailureType.IssuerValidationFailed, + typeof(SecurityTokenInvalidIssuerException), + new StackFrame(true), + issuer, + ex); + } + + if (string.IsNullOrWhiteSpace(tenantId)) + return new IssuerValidationError( + new MessageDetail( + LogMessages.IDX40003, + LogHelper.MarkAsNonPII(issuer)), + ValidationFailureType.IssuerValidationFailed, + typeof(SecurityTokenInvalidIssuerException), + new StackFrame(true), + issuer); + + for (int i = 0; i < validationParameters.ValidIssuers.Count; i++) + { + if (IsValidIssuer(validationParameters.ValidIssuers[i], tenantId, issuer)) + return new ValidatedIssuer(issuer!, IssuerValidationSource.IssuerMatchedConfiguration); + } + + try + { + var issuerVersion = GetTokenIssuerVersion(securityToken); + var effectiveConfigurationManager = GetEffectiveConfigurationManager(issuerVersion); + + string aadIssuer = null; + if (validationParameters.ValidateWithLKG) + { + // returns null if LKG issuer expired + aadIssuer = GetEffectiveLKGIssuer(issuerVersion); + } + else + { + var baseConfiguration = await GetBaseConfigurationAsync(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, issuerVersion, effectiveConfigurationManager.LastKnownGoodLifetime); + + if (isIssuerValid) + return new ValidatedIssuer(issuer!, IssuerValidationSource.IssuerMatchedConfiguration); + } + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types + { + return new IssuerValidationError( + new MessageDetail( + LogMessages.IDX40001, + LogHelper.MarkAsNonPII(issuer)), + ValidationFailureType.IssuerValidationFailed, + typeof(SecurityTokenInvalidIssuerException), + new StackFrame(true), + issuer, + ex); + } + + // If a valid issuer is not found, return an error. + return new IssuerValidationError( + new MessageDetail( + LogMessages.IDX40001, + LogHelper.MarkAsNonPII(issuer)), + ValidationFailureType.IssuerValidationFailed, + typeof(SecurityTokenInvalidIssuerException), + new StackFrame(true), + issuer); + } + + private static async Task GetBaseConfigurationAsync(BaseConfigurationManager configurationManager, ValidationParameters validationParameters) + { + if (validationParameters.RefreshBeforeValidation) + configurationManager.RequestRefresh(); + + return await configurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false); + } + } +} diff --git a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs index 72771086b5..46ba3de09d 100644 --- a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs +++ b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs @@ -19,7 +19,7 @@ namespace Microsoft.IdentityModel.Validators /// /// Generic class that validates the issuer for either JsonWebTokens or JwtSecurityTokens issued from the Microsoft identity platform (AAD). /// - public class AadIssuerValidator + public partial class AadIssuerValidator { private static readonly TimeSpan LastKnownGoodConfigurationLifetime = new TimeSpan(0, 24, 0, 0); @@ -492,7 +492,7 @@ private BaseConfigurationManager GetEffectiveConfigurationManager(ProtocolVersio 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/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidatorConstants.cs b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidatorConstants.cs index f32fbcc342..e7aac386c6 100644 --- a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidatorConstants.cs +++ b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidatorConstants.cs @@ -12,6 +12,7 @@ internal class AadIssuerValidatorConstants public const string Common = "common"; public const string OidcEndpoint = "/.well-known/openid-configuration"; public const string FallbackAuthority = "https://login.microsoftonline.com/"; + public const string CloudInstanceNameKey = "cloud_instance_name"; /// /// Old TenantId claim: "http://schemas.microsoft.com/identity/claims/tenantid". diff --git a/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs b/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs index 56491d141f..58f07e8f80 100644 --- a/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs +++ b/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs @@ -16,8 +16,6 @@ namespace Microsoft.IdentityModel.Validators /// public static class AadTokenValidationParametersExtension { - private const string CloudInstanceNameKey = "cloud_instance_name"; - /// /// Enables validation of the cloud instance of the Microsoft Entra ID token signing keys. /// @@ -150,13 +148,13 @@ internal static void ValidateSigningKeyCloudInstance(SecurityKey securityKey, Ba return; JsonWebKey matchedKeyFromConfig = GetJsonWebKeyBySecurityKey(openIdConnectConfiguration, securityKey); - if (matchedKeyFromConfig != null && matchedKeyFromConfig.AdditionalData.TryGetValue(CloudInstanceNameKey, out object value)) + if (matchedKeyFromConfig != null && matchedKeyFromConfig.AdditionalData.TryGetValue(AadIssuerValidatorConstants.CloudInstanceNameKey, out object value)) { string signingKeyCloudInstanceName = value as string; if (string.IsNullOrWhiteSpace(signingKeyCloudInstanceName)) return; - if (openIdConnectConfiguration.AdditionalData.TryGetValue(CloudInstanceNameKey, out object configurationCloudInstanceNameObjectValue)) + if (openIdConnectConfiguration.AdditionalData.TryGetValue(AadIssuerValidatorConstants.CloudInstanceNameKey, out object configurationCloudInstanceNameObjectValue)) { string configurationCloudInstanceName = configurationCloudInstanceNameObjectValue as string; if (string.IsNullOrWhiteSpace(configurationCloudInstanceName)) @@ -174,7 +172,7 @@ internal static void ValidateSigningKeyCloudInstance(SecurityKey securityKey, Ba } } - private static JsonWebKey GetJsonWebKeyBySecurityKey(OpenIdConnectConfiguration configuration, SecurityKey securityKey) + internal static JsonWebKey GetJsonWebKeyBySecurityKey(OpenIdConnectConfiguration configuration, SecurityKey securityKey) { if (configuration.JsonWebKeySet == null) return null; diff --git a/src/Microsoft.IdentityModel.Validators/AadValidationParametersExtension.Internal.cs b/src/Microsoft.IdentityModel.Validators/AadValidationParametersExtension.Internal.cs new file mode 100644 index 0000000000..a0900e9c33 --- /dev/null +++ b/src/Microsoft.IdentityModel.Validators/AadValidationParametersExtension.Internal.cs @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using Microsoft.IdentityModel.Tokens; + +namespace Microsoft.IdentityModel.Validators +{ + /// + /// A generic class for additional validation checks on issued by the Microsoft identity platform (AAD). + /// + internal static class AadValidationParametersExtension + { + /// + /// Enables validation of the cloud instance of the Microsoft Entra ID token signing keys. + /// + /// The that are used to validate the token. + internal static void EnableEntraIdSigningKeyCloudInstanceValidation(this ValidationParameters validationParameters) + { + if (validationParameters == null) + throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + + IssuerSigningKeyValidationDelegate issuerSigningKeyValidationDelegate = validationParameters.IssuerSigningKeyValidator; + + IssuerSigningKeyValidationDelegate issuerSigningKeyValidationDelegate1 = (securityKey, securityToken, validationParameters, callContext) => + { + // TODO remove GetAwaiter + BaseConfiguration configuration = null; + if (validationParameters.ConfigurationManager != null) + configuration = validationParameters.ConfigurationManager.GetBaseConfigurationAsync(System.Threading.CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult(); + + ValidateSigningKeyCloudInstance(securityKey, configuration); + + // preserve and run provided logic + if (issuerSigningKeyValidationDelegate != null) + return issuerSigningKeyValidationDelegate(securityKey, securityToken, validationParameters, callContext); + + return new ValidatedSigningKeyLifetime(DateTime.UtcNow, DateTime.UtcNow, DateTime.UtcNow); + }; + + validationParameters.IssuerSigningKeyValidator = issuerSigningKeyValidationDelegate1; + } + + /// + /// Enables the validation of the issuer of the signing keys used by the Microsoft identity platform (AAD) against the issuer of the token. + /// + /// The that are used to validate the token. + internal static void EnableAadSigningKeyIssuerValidation(this ValidationParameters validationParameters) + { + if (validationParameters == null) + throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + + IssuerSigningKeyValidationDelegate issuerSigningKeyValidationDelegate = validationParameters.IssuerSigningKeyValidator; + + validationParameters.IssuerSigningKeyValidator = (securityKey, securityToken, vp, callContext) => + { + BaseConfiguration baseConfiguration = null; + if (vp.ConfigurationManager != null) + baseConfiguration = vp.ConfigurationManager.GetBaseConfigurationAsync(System.Threading.CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult(); + + AadTokenValidationParametersExtension.ValidateIssuerSigningKey(securityKey, securityToken, baseConfiguration); + + // preserve and run provided logic + if (issuerSigningKeyValidationDelegate != null) + return issuerSigningKeyValidationDelegate(securityKey, securityToken, vp, callContext); + + return ValidateIssuerSigningKeyCertificate(securityKey, validationParameters); + }; + } + + /// + /// Validates the cloud instance of the signing key. + /// + /// The that signed the . + /// The provided. + internal static void ValidateSigningKeyCloudInstance(SecurityKey securityKey, BaseConfiguration configuration) + { + if (securityKey == null) + return; + + if (configuration is not OpenIdConnectConfiguration openIdConnectConfiguration) + return; + + JsonWebKey matchedKeyFromConfig = AadTokenValidationParametersExtension.GetJsonWebKeyBySecurityKey(openIdConnectConfiguration, securityKey); + if (matchedKeyFromConfig != null && matchedKeyFromConfig.AdditionalData.TryGetValue(AadIssuerValidatorConstants.CloudInstanceNameKey, out object value)) + { + string signingKeyCloudInstanceName = value as string; + if (string.IsNullOrWhiteSpace(signingKeyCloudInstanceName)) + return; + + if (openIdConnectConfiguration.AdditionalData.TryGetValue(AadIssuerValidatorConstants.CloudInstanceNameKey, out object configurationCloudInstanceNameObjectValue)) + { + string configurationCloudInstanceName = configurationCloudInstanceNameObjectValue as string; + if (string.IsNullOrWhiteSpace(configurationCloudInstanceName)) + return; + + if (!string.Equals(signingKeyCloudInstanceName, configurationCloudInstanceName, StringComparison.Ordinal)) + throw LogHelper.LogExceptionMessage( + new SecurityTokenInvalidCloudInstanceException( + LogHelper.FormatInvariant( + LogMessages.IDX40012, + LogHelper.MarkAsNonPII(signingKeyCloudInstanceName), + LogHelper.MarkAsNonPII(configurationCloudInstanceName))) + { + ConfigurationCloudInstanceName = configurationCloudInstanceName, + SigningKeyCloudInstanceName = signingKeyCloudInstanceName, + SigningKey = securityKey, + }); + } + } + } + + /// + /// Validates the issuer signing key certificate. + /// + /// The that signed the . + /// The that are used to validate the token. + /// true if the issuer signing key certificate is valid; otherwise, false. + internal static ValidationResult ValidateIssuerSigningKeyCertificate(SecurityKey securityKey, ValidationParameters validationParameters) + { + if (securityKey == null) + { + throw LogHelper.LogExceptionMessage(new ArgumentNullException(nameof(securityKey), LogMessages.IDX40007)); + } + + return Tokens.Validators.ValidateIssuerSigningKeyLifeTime(securityKey, validationParameters, new CallContext()); + } + } +} diff --git a/src/Microsoft.IdentityModel.Validators/InternalAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Validators/InternalAPI.Unshipped.txt index e69de29bb2..7b57e22604 100644 --- a/src/Microsoft.IdentityModel.Validators/InternalAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.Validators/InternalAPI.Unshipped.txt @@ -0,0 +1,9 @@ +const Microsoft.IdentityModel.Validators.AadIssuerValidatorConstants.CloudInstanceNameKey = "cloud_instance_name" -> string +Microsoft.IdentityModel.Validators.AadIssuerValidator.ValidateIssuerAsync(string issuer, Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> +Microsoft.IdentityModel.Validators.AadValidationParametersExtension +static Microsoft.IdentityModel.Validators.AadTokenValidationParametersExtension.GetJsonWebKeyBySecurityKey(Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfiguration configuration, Microsoft.IdentityModel.Tokens.SecurityKey securityKey) -> Microsoft.IdentityModel.Tokens.JsonWebKey +static Microsoft.IdentityModel.Validators.AadValidationParametersExtension.EnableAadSigningKeyIssuerValidation(this Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters) -> void +static Microsoft.IdentityModel.Validators.AadValidationParametersExtension.EnableEntraIdSigningKeyCloudInstanceValidation(this Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters) -> void +static Microsoft.IdentityModel.Validators.AadValidationParametersExtension.ValidateIssuerSigningKey(Microsoft.IdentityModel.Tokens.SecurityKey securityKey, Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.BaseConfiguration configuration) -> bool +static Microsoft.IdentityModel.Validators.AadValidationParametersExtension.ValidateIssuerSigningKeyCertificate(Microsoft.IdentityModel.Tokens.SecurityKey securityKey, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters) -> Microsoft.IdentityModel.Tokens.ValidationResult +static Microsoft.IdentityModel.Validators.AadValidationParametersExtension.ValidateSigningKeyCloudInstance(Microsoft.IdentityModel.Tokens.SecurityKey securityKey, Microsoft.IdentityModel.Tokens.BaseConfiguration configuration) -> void \ No newline at end of file diff --git a/test/Microsoft.IdentityModel.TestUtils/Properties/AssemblyInfo.cs b/test/Microsoft.IdentityModel.TestUtils/Properties/AssemblyInfo.cs index 9822268a7b..9f18beb123 100644 --- a/test/Microsoft.IdentityModel.TestUtils/Properties/AssemblyInfo.cs +++ b/test/Microsoft.IdentityModel.TestUtils/Properties/AssemblyInfo.cs @@ -2,7 +2,9 @@ // Licensed under the MIT License. using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; [assembly: CLSCompliant(false)] [assembly: ComVisible(false)] +[assembly: InternalsVisibleTo("Microsoft.IdentityModel.Validators.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/test/Microsoft.IdentityModel.TestUtils/SamlClaimsIdentityComparisonTestBase.cs b/test/Microsoft.IdentityModel.TestUtils/SamlClaimsIdentityComparisonTestBase.cs index 00f8656c52..ebbfb2587a 100644 --- a/test/Microsoft.IdentityModel.TestUtils/SamlClaimsIdentityComparisonTestBase.cs +++ b/test/Microsoft.IdentityModel.TestUtils/SamlClaimsIdentityComparisonTestBase.cs @@ -3,7 +3,6 @@ using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; using Microsoft.IdentityModel.Tokens; -using Microsoft.IdentityModel.Tokens.Saml; using System; using System.Collections.Generic; using System.Threading; diff --git a/test/Microsoft.IdentityModel.TestUtils/SkipValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/SkipValidationDelegates.cs index 90b49be8aa..3e94c810c1 100644 --- a/test/Microsoft.IdentityModel.TestUtils/SkipValidationDelegates.cs +++ b/test/Microsoft.IdentityModel.TestUtils/SkipValidationDelegates.cs @@ -47,7 +47,6 @@ public static class SkipValidationDelegates SecurityKey signingKey, SecurityToken securityToken, ValidationParameters validationParameters, - BaseConfiguration? configuration, CallContext callContext) { return new ValidatedSigningKeyLifetime( diff --git a/test/Microsoft.IdentityModel.TestUtils/TestUtilities.cs b/test/Microsoft.IdentityModel.TestUtils/TestUtilities.cs index 55d5b56cfa..bcde2bba01 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TestUtilities.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TestUtilities.cs @@ -35,6 +35,78 @@ public class GetSetContext public static class TestUtilities { + internal static ValidationParameters CreateFromTokenValidationParameters(TokenValidationParameters tokenValidationParameters) + { + ValidationParameters validationParameters = new(); + + if (tokenValidationParameters.AuthenticationType != null) + validationParameters.AuthenticationType = tokenValidationParameters.AuthenticationType; + + validationParameters.ClockSkew = tokenValidationParameters.ClockSkew; + validationParameters.ConfigurationManager = tokenValidationParameters.ConfigurationManager; + validationParameters.CryptoProviderFactory = tokenValidationParameters.CryptoProviderFactory; + validationParameters.DebugId = tokenValidationParameters.DebugId; + validationParameters.IncludeTokenOnFailedValidation = tokenValidationParameters.IncludeTokenOnFailedValidation; + validationParameters.IgnoreTrailingSlashWhenValidatingAudience = tokenValidationParameters.IgnoreTrailingSlashWhenValidatingAudience; + + //validationParameters.IssuerSigningKeyResolver = tokenValidationParameters.IssuerSigningKeyResolver; + if (tokenValidationParameters.IssuerSigningKeys != null) + foreach (SecurityKey key in tokenValidationParameters.IssuerSigningKeys) + validationParameters.IssuerSigningKeys.Add(key); + + if (tokenValidationParameters.IssuerSigningKey != null) + validationParameters.IssuerSigningKeys.Add(tokenValidationParameters.IssuerSigningKey); + + validationParameters.LogTokenId = tokenValidationParameters.LogTokenId; + validationParameters.NameClaimType = tokenValidationParameters.NameClaimType; + validationParameters.NameClaimTypeRetriever = tokenValidationParameters.NameClaimTypeRetriever; + if (tokenValidationParameters.PropertyBag != null) + foreach (var item in tokenValidationParameters.PropertyBag) + validationParameters.PropertyBag.Add(item.Key, item.Value); + + validationParameters.RefreshBeforeValidation = tokenValidationParameters.RefreshBeforeValidation; + validationParameters.RoleClaimType = tokenValidationParameters.RoleClaimType; + validationParameters.RoleClaimTypeRetriever = tokenValidationParameters.RoleClaimTypeRetriever; + validationParameters.SaveSigninToken = tokenValidationParameters.SaveSigninToken; + + if (tokenValidationParameters.TokenDecryptionKey != null) + validationParameters.TokenDecryptionKeys.Add(tokenValidationParameters.TokenDecryptionKey); + + if (tokenValidationParameters.TokenDecryptionKeys != null) + foreach (SecurityKey key in tokenValidationParameters.TokenDecryptionKeys) + validationParameters.TokenDecryptionKeys.Add(key); + + validationParameters.TokenReplayCache = tokenValidationParameters.TokenReplayCache; + validationParameters.TryAllIssuerSigningKeys = tokenValidationParameters.TryAllIssuerSigningKeys; + validationParameters.ValidateActor = tokenValidationParameters.ValidateActor; + validationParameters.ValidateSignatureLast = tokenValidationParameters.ValidateSignatureLast; + validationParameters.ValidateWithLKG = tokenValidationParameters.ValidateWithLKG; + + if (tokenValidationParameters.ValidAlgorithms != null) + foreach (string algorithms in tokenValidationParameters.ValidAlgorithms) + validationParameters.ValidAlgorithms.Add(algorithms); + + if (tokenValidationParameters.ValidAudiences != null) + foreach (string audience in tokenValidationParameters.ValidAudiences) + validationParameters.ValidAudiences.Add(audience); + + if (tokenValidationParameters.ValidAudience != null) + validationParameters.ValidAudiences.Add(tokenValidationParameters.ValidAudience); + + if (tokenValidationParameters.ValidIssuers != null) + foreach (string issuer in tokenValidationParameters.ValidIssuers) + validationParameters.ValidIssuers.Add(issuer); + + if (tokenValidationParameters.ValidIssuer != null) + validationParameters.ValidIssuers.Add(tokenValidationParameters.ValidIssuer); + + if (tokenValidationParameters.ValidTypes != null) + foreach (string type in tokenValidationParameters.ValidTypes) + validationParameters.ValidTypes.Add(type); + + return validationParameters; + } + /// /// Calls all public instance and static properties on an object /// diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerSigningKeyValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerSigningKeyValidationDelegates.cs index 2c94cdaf92..1e596e687f 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerSigningKeyValidationDelegates.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerSigningKeyValidationDelegates.cs @@ -13,7 +13,6 @@ internal static ValidationResult CustomIssuerSignin SecurityKey signingKey, SecurityToken securityToken, ValidationParameters validationParameters, - BaseConfiguration? configuration, CallContext callContext) { // Returns a CustomIssuerSigningKeyValidationError : IssuerSigningKeyValidationError @@ -30,7 +29,6 @@ internal static ValidationResult CustomIssuerSignin SecurityKey signingKey, SecurityToken securityToken, ValidationParameters validationParameters, - BaseConfiguration? configuration, CallContext callContext) { return new CustomIssuerSigningKeyValidationError( @@ -46,7 +44,6 @@ internal static ValidationResult CustomIssuerSignin SecurityKey signingKey, SecurityToken securityToken, ValidationParameters validationParameters, - BaseConfiguration? configuration, CallContext callContext) { return new CustomIssuerSigningKeyValidationError( @@ -61,7 +58,6 @@ internal static ValidationResult CustomIssuerSignin SecurityKey signingKey, SecurityToken securityToken, ValidationParameters validationParameters, - BaseConfiguration? configuration, CallContext callContext) { return new CustomIssuerSigningKeyValidationError( @@ -77,7 +73,6 @@ internal static ValidationResult CustomIssuerSignin SecurityKey signingKey, SecurityToken securityToken, ValidationParameters validationParameters, - BaseConfiguration? configuration, CallContext callContext) { return new CustomIssuerSigningKeyWithoutGetExceptionValidationOverrideError( @@ -92,7 +87,6 @@ internal static ValidationResult IssuerSigningKeyVa SecurityKey signingKey, SecurityToken securityToken, ValidationParameters validationParameters, - BaseConfiguration? configuration, CallContext callContext) { return new IssuerSigningKeyValidationError( @@ -108,7 +102,6 @@ internal static ValidationResult IssuerSigningKeyVa SecurityKey signingKey, SecurityToken securityToken, ValidationParameters validationParameters, - BaseConfiguration? configuration, CallContext callContext) { throw new CustomSecurityTokenInvalidSigningKeyException(nameof(IssuerSigningKeyValidatorThrows), null); @@ -118,7 +111,6 @@ internal static ValidationResult IssuerSigningKeyVa SecurityKey signingKey, SecurityToken securityToken, ValidationParameters validationParameters, - BaseConfiguration? configuration, CallContext callContext) { return new IssuerSigningKeyValidationError( @@ -134,7 +126,6 @@ internal static ValidationResult IssuerSigningKeyVa SecurityKey signingKey, SecurityToken securityToken, ValidationParameters validationParameters, - BaseConfiguration? configuration, CallContext callContext) { return new IssuerSigningKeyValidationError( diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs index 932d521595..4edfe8af9a 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/SigningKeyValidationResultTests.cs @@ -20,7 +20,6 @@ public void SecurityKey(SigningKeyValidationTheoryData theoryData) theoryData.SecurityKey, theoryData.SecurityToken, theoryData.ValidationParameters, - theoryData.BaseConfiguration, new CallContext()); if (result.IsValid) diff --git a/test/Microsoft.IdentityModel.Validators.Tests/AadTokenValidationParametersExtensionTests.cs b/test/Microsoft.IdentityModel.Validators.Tests/AadTokenValidationParametersExtensionTests.cs index 867dc51ae8..c493a0b6ca 100644 --- a/test/Microsoft.IdentityModel.Validators.Tests/AadTokenValidationParametersExtensionTests.cs +++ b/test/Microsoft.IdentityModel.Validators.Tests/AadTokenValidationParametersExtensionTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; +using System.Threading; using System.Threading.Tasks; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Protocols; @@ -14,8 +15,6 @@ using Microsoft.IdentityModel.Tokens.Saml2; using Xunit; -#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant - namespace Microsoft.IdentityModel.Validators.Tests { // Serialize as one of the tests depends on static state (app context) @@ -28,16 +27,33 @@ public async Task EnableEntraIdSigningKeyCloudInstanceValidationTests(EnableEntr var context = TestUtilities.WriteHeader($"{this}.EnableAadSigningKeyValidationTests", theoryData); try { + + ValidationParameters validationParameters = TestUtilities.CreateFromTokenValidationParameters(theoryData.TokenValidationParameters); + validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; + validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation; + // set delegates + bool validationParametersDelegateSet = false; bool delegateSet = false; + if (theoryData.SetDelegateUsingConfig) { theoryData.TokenValidationParameters.IssuerSigningKeyValidatorUsingConfiguration = (securityKey, securityToken, tvp, config) => { delegateSet = true; return true; }; + validationParameters.IssuerSigningKeyValidator = (securityKey, securityToken, validationParameters, callContext) => + { + validationParametersDelegateSet = true; + return new ValidatedSigningKeyLifetime(DateTime.UtcNow, DateTime.UtcNow, DateTime.UtcNow); + }; } else if (theoryData.SetDelegateWithoutConfig) { theoryData.TokenValidationParameters.IssuerSigningKeyValidatorUsingConfiguration = null; theoryData.TokenValidationParameters.IssuerSigningKeyValidator = (securityKey, securityToken, tvp) => { delegateSet = true; return true; }; + validationParameters.IssuerSigningKeyValidator = (securityKey, securityToken, validationParameters, callContext) => + { + validationParametersDelegateSet = true; + return new ValidatedSigningKeyLifetime(DateTime.UtcNow, DateTime.UtcNow, DateTime.UtcNow); + }; } var handler = new JsonWebTokenHandler(); @@ -45,13 +61,18 @@ public async Task EnableEntraIdSigningKeyCloudInstanceValidationTests(EnableEntr theoryData.TokenValidationParameters.EnableEntraIdSigningKeyCloudInstanceValidation(); var validationResult = await handler.ValidateTokenAsync(theoryData.Token, theoryData.TokenValidationParameters); + ValidationResult validatedToken = await handler.ValidateTokenAsync(theoryData.Token, validationParameters, new CallContext(), CancellationToken.None); + theoryData.ExpectedException.ProcessNoException(context); Assert.NotNull(theoryData.TokenValidationParameters.IssuerSigningKeyValidatorUsingConfiguration); Assert.Equal(theoryData.ExpectedValidationResult, validationResult.IsValid); // verify delegates were executed if (theoryData.ExpectedValidationResult && (theoryData.SetDelegateUsingConfig || theoryData.SetDelegateWithoutConfig)) + { Assert.True(delegateSet); + Assert.True(validationParametersDelegateSet); + } } catch (Exception ex) { @@ -165,16 +186,32 @@ public async Task EnableAadSigningKeyIssuerValidationTests(EnableEntraIdSigningK var context = TestUtilities.WriteHeader($"{this}.EnableAadSigningKeyIssuerValidationTests", theoryData); try { + ValidationParameters validationParameters = TestUtilities.CreateFromTokenValidationParameters(theoryData.TokenValidationParameters); + validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; + validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation; + // set delegates bool delegateSet = false; + bool validationParametersDelegateSet = false; if (theoryData.SetDelegateUsingConfig) { theoryData.TokenValidationParameters.IssuerSigningKeyValidatorUsingConfiguration = (securityKey, securityToken, tvp, config) => { delegateSet = true; return true; }; + validationParameters.IssuerSigningKeyValidator = (securityKey, securityToken, validationParameters, callContext) => + { + validationParametersDelegateSet = true; + return new ValidatedSigningKeyLifetime(DateTime.UtcNow, DateTime.UtcNow, DateTime.UtcNow); + }; } else if (theoryData.SetDelegateWithoutConfig) { theoryData.TokenValidationParameters.IssuerSigningKeyValidatorUsingConfiguration = null; theoryData.TokenValidationParameters.IssuerSigningKeyValidator = (securityKey, securityToken, tvp) => { delegateSet = true; return true; }; + validationParameters.IssuerSigningKeyValidator = (securityKey, securityToken, validationParameters, callContext) => + { + validationParametersDelegateSet = true; + return new ValidatedSigningKeyLifetime(DateTime.UtcNow, DateTime.UtcNow, DateTime.UtcNow); + }; + } var handler = new JsonWebTokenHandler(); @@ -183,13 +220,18 @@ public async Task EnableAadSigningKeyIssuerValidationTests(EnableEntraIdSigningK theoryData.TokenValidationParameters.EnableAadSigningKeyIssuerValidation(); var validationResult = await handler.ValidateTokenAsync(jwt, theoryData.TokenValidationParameters); + ValidationResult validatedToken = await handler.ValidateTokenAsync(jwt, validationParameters, new CallContext(), CancellationToken.None); theoryData.ExpectedException.ProcessNoException(context); + Assert.NotNull(theoryData.TokenValidationParameters.IssuerSigningKeyValidatorUsingConfiguration); Assert.True(validationResult.IsValid); // verify delegates were executed if (theoryData.SetDelegateUsingConfig || theoryData.SetDelegateWithoutConfig) + { Assert.True(delegateSet); + Assert.True(validationParametersDelegateSet); + } } catch (Exception ex) { @@ -701,5 +743,3 @@ public class AadSigningKeyTheoryData : TheoryDataBase } } } - -#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant diff --git a/test/Microsoft.IdentityModel.Validators.Tests/MicrosoftIdentityIssuerValidatorTest.cs b/test/Microsoft.IdentityModel.Validators.Tests/MicrosoftIdentityIssuerValidatorTest.cs index 908ae78870..fb9311cea9 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,7 +142,7 @@ 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); @@ -149,14 +150,22 @@ public void Validate_NullOrEmptyParameters_ThrowsException() var validationParams = new TokenValidationParameters(); Assert.Throws(ValidatorConstants.Issuer, () => validator.Validate(null, jwtSecurityToken, validationParams)); + await Assert.ThrowsAsync(async () => await ValidateIssuerAsync(null, jwtSecurityToken, validator)); var exception = Assert.Throws(() => validator.Validate(string.Empty, jwtSecurityToken, validationParams)); + ValidationResult validatedIssuer = await ValidateIssuerAsync(string.Empty, jwtSecurityToken, validator); + Assert.False(validatedIssuer.IsValid); IdentityComparer.AreEqual(LogMessages.IDX40003, exception.Message); Assert.Throws(ValidatorConstants.SecurityToken, () => validator.Validate(ValidatorConstants.AadIssuer, null, validationParams)); + await Assert.ThrowsAsync(async () => await ValidateIssuerAsync(ValidatorConstants.AadIssuer, null, validator)); Assert.Throws(ValidatorConstants.ValidationParameters, () => validator.Validate(ValidatorConstants.AadIssuer, jwtSecurityToken, null)); + + await Assert.ThrowsAsync(async () => + await validator.ValidateIssuerAsync(ValidatorConstants.AadIssuer, jwtSecurityToken, null, new Tokens.CallContext(), CancellationToken.None)); + TestUtilities.AssertFailIfErrors(context); } @@ -190,7 +199,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,14 +209,13 @@ 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 }); + ValidationResult validatedIssuer = await ValidateIssuerAsync(issuer, issuer, jwtSecurityToken, validator); + IdentityComparer.AreEqual(validatedIssuer.Result.Issuer, actualIssuer, context); IdentityComparer.AreEqual(issuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); } @@ -217,7 +225,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); @@ -226,8 +234,13 @@ public void Validate_NoHttpclientFactory_ReturnsIssuer(string tidClaimType, stri var issClaim = new Claim(ValidatorConstants.ClaimNameIss, issuer); var jwtSecurityToken = new JwtSecurityToken(issuer: issuer, claims: new[] { issClaim, tidClaim }); - var tokenValidationParams = new TokenValidationParameters() { ConfigurationManager = new MockConfigurationManager(new OpenIdConnectConfiguration() { Issuer = issuer }) }; + MockConfigurationManager configurationManager = + new MockConfigurationManager(new OpenIdConnectConfiguration() { Issuer = issuer }); + + var tokenValidationParams = new TokenValidationParameters() { ConfigurationManager = configurationManager }; + ValidationResult validatedIssuer = await ValidateIssuerAsync(issuer, configurationManager, jwtSecurityToken, validator); + IdentityComparer.AreEqual(issuer, validatedIssuer.Result.Issuer, context); IdentityComparer.AreEqual(issuer, validator.Validate(issuer, jwtSecurityToken, tokenValidationParams), context); TestUtilities.AssertFailIfErrors(context); } @@ -237,7 +250,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(); @@ -252,10 +265,10 @@ public void Validate_IssuerMatchedInValidV1Issuer_ReturnsIssuer(string tidClaimT var issClaim = new Claim(ValidatorConstants.ClaimNameIss, issuer); var jwtSecurityToken = new JwtSecurityToken(issuer: issuer, claims: new[] { issClaim, tidClaim }); - validator.AadIssuerV1 = issuer; - var actualIssuer = validator.Validate(issuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuer = issuer }); + ValidationResult validatedIssuer = await ValidateIssuerAsync(issuer, issuer, jwtSecurityToken, validator); + IdentityComparer.AreEqual(issuer, validatedIssuer.Result.Issuer, context); IdentityComparer.AreEqual(issuer, actualIssuer, context); var actualIssuers = validator.Validate(issuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuers = new[] { issuer } }); @@ -269,7 +282,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,11 +298,18 @@ 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); + ValidationResult validatedIssuerResult = await ValidateIssuerAsync( + ValidatorConstants.AadIssuer, + ValidatorConstants.AadIssuer, + jwtSecurityToken, + validator); + + Assert.True(validatedIssuerResult.IsValid); var actualIssuer = validator.Validate(ValidatorConstants.AadIssuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuer = ValidatorConstants.AadIssuer }); + IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, validatedIssuerResult.Result.Issuer, context); IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); } @@ -328,7 +348,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) @@ -346,7 +366,13 @@ public void ValidateJsonWebToken_ReturnsIssuer(string tidClaimType, string issue var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor(Default.SymmetricSigningCredentials, claims))); var actualIssuer = validator.Validate(issuer, jsonWebToken, new TokenValidationParameters()); + ValidationResult validatedIssuerResult = await ValidateIssuerAsync( + issuer, + jsonWebToken, + validator); + Assert.True(validatedIssuerResult.IsValid); + IdentityComparer.AreEqual(issuer, validatedIssuerResult.Result.Issuer, context); IdentityComparer.AreEqual(issuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); } @@ -356,7 +382,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 +397,19 @@ 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()); + ValidationResult validatedIssuer = await ValidateIssuerAsync( + ValidatorConstants.V1Issuer, + jwtSecurityToken, + validator); + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(ValidatorConstants.V1Issuer, validatedIssuer.Result.Issuer, context); 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 +417,20 @@ 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 }); - + ValidationResult validatedIssuer = await ValidateIssuerAsync( + ValidatorConstants.AadIssuer, + ValidatorConstants.AadIssuer, + jwtSecurityToken, + validator); + + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, validatedIssuer.Result.Issuer, context); 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,11 +440,27 @@ 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 }); + ValidationResult validatedIssuer = await ValidateIssuerAsync( + ValidatorConstants.AadIssuer, + ValidatorConstants.AadIssuer, + jwtSecurityToken, + validator); + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, validatedIssuer.Result.Issuer, context); IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); - actualIssuer = validator.Validate(ValidatorConstants.AadIssuer, jsonWebToken, new TokenValidationParameters() { ValidIssuer = ValidatorConstants.AadIssuer }); + IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); + actualIssuer = validator.Validate(ValidatorConstants.AadIssuer, jsonWebToken, new TokenValidationParameters() { ValidIssuer = ValidatorConstants.AadIssuer }); + validatedIssuer = await ValidateIssuerAsync( + ValidatorConstants.AadIssuer, + ValidatorConstants.AadIssuer, + jsonWebToken, + validator); + + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, validatedIssuer.Result.Issuer, context); IdentityComparer.AreEqual(ValidatorConstants.AadIssuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); } @@ -413,7 +468,7 @@ public void Validate_TidClaimInToken_ReturnsIssuer() // 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"; @@ -428,12 +483,23 @@ public void Validate_InvalidIssuerToValidate_ThrowsException() var exception = Assert.Throws(() => validator.Validate(invalidIssuerToValidate, jwtSecurityToken, new TokenValidationParameters() { ValidIssuers = new[] { ValidatorConstants.AadIssuer } })); + + ValidationResult validatedIssuer = await ValidateIssuerAsync( + invalidIssuerToValidate, + ValidatorConstants.AadIssuer, + jwtSecurityToken, + validator); + + Assert.False(validatedIssuer.IsValid); + IdentityComparer.AreEqual(typeof(SecurityTokenInvalidIssuerException).ToString(), validatedIssuer.Error.ExceptionType.ToString(), context); + IdentityComparer.AreEqual(expectedErrorMessage, validatedIssuer.Error.MessageDetail.Message, context); + 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); @@ -442,13 +508,24 @@ public void Validate_FromB2CAuthority_WithNoTidClaim_ValidateSuccessfully() AadIssuerValidator validator = CreateIssuerValidator(ValidatorConstants.B2CAuthorityWithV2); - validator.Validate( + string issuer = validator.Validate( ValidatorConstants.B2CIssuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuers = new[] { ValidatorConstants.B2CIssuer }, }); + + ValidationResult validatedIssuer = await ValidateIssuerAsync( + ValidatorConstants.B2CIssuer, + ValidatorConstants.B2CIssuer, + jwtSecurityToken, + validator); + + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(ValidatorConstants.B2CIssuer, validatedIssuer.Result.Issuer, context); + IdentityComparer.AreEqual(ValidatorConstants.B2CIssuer, issuer, context); + IdentityComparer.AreEqual(ValidatorConstants.B2CAuthority, validator.AadAuthorityV1, context); IdentityComparer.AreEqual(ValidatorConstants.B2CAuthorityWithV2, validator.AadAuthorityV2, context); IdentityComparer.AreEqual(ProtocolVersion.V2, validator.AadAuthorityVersion, context); @@ -456,29 +533,44 @@ public void Validate_FromB2CAuthority_WithNoTidClaim_ValidateSuccessfully() } [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); var tfpClaim = new Claim(ValidatorConstants.ClaimNameTfp, ValidatorConstants.B2CSignUpSignInUserFlow); var jwtSecurityToken = new JwtSecurityToken(issuer: ValidatorConstants.B2CIssuer, claims: new[] { issClaim, tfpClaim }); + BaseConfigurationManager configurationManager = new MockConfigurationManager(new OpenIdConnectConfiguration() + { + Issuer = ValidatorConstants.B2CIssuer + }); var validator = new AadIssuerValidator(null, ValidatorConstants.B2CAuthority); var tokenValidationParams = new TokenValidationParameters() { - ConfigurationManager = new MockConfigurationManager(new OpenIdConnectConfiguration() - { - Issuer = ValidatorConstants.B2CIssuer - }) + ConfigurationManager = configurationManager }; - IdentityComparer.AreEqual(ValidatorConstants.B2CIssuer, validator.Validate(ValidatorConstants.B2CIssuer, jwtSecurityToken, tokenValidationParams), context); + string issuer = validator.Validate( + ValidatorConstants.B2CIssuer, + jwtSecurityToken, + tokenValidationParams); + + ValidationResult validatedIssuer = await ValidateIssuerAsync( + ValidatorConstants.B2CIssuer, + configurationManager, + jwtSecurityToken, + validator); + + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(ValidatorConstants.B2CIssuer, validatedIssuer.Result.Issuer, context); + IdentityComparer.AreEqual(ValidatorConstants.B2CIssuer, issuer, 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); @@ -488,13 +580,24 @@ public void Validate_FromB2CAuthority_WithTidClaim_ValidateSuccessfully() AadIssuerValidator validator = CreateIssuerValidator(ValidatorConstants.B2CAuthorityWithV2); - validator.Validate( + string issuer = validator.Validate( ValidatorConstants.B2CIssuer, jwtSecurityToken, new TokenValidationParameters() { ValidIssuers = new[] { ValidatorConstants.B2CIssuer }, }); + + ValidationResult validatedIssuer = await ValidateIssuerAsync( + ValidatorConstants.B2CIssuer, + ValidatorConstants.B2CIssuer, + jwtSecurityToken, + validator); + + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(ValidatorConstants.B2CIssuer, validatedIssuer.Result.Issuer, context); + IdentityComparer.AreEqual(ValidatorConstants.B2CIssuer, issuer, context); + IdentityComparer.AreEqual(ValidatorConstants.B2CAuthority, validator.AadAuthorityV1, context); IdentityComparer.AreEqual(ValidatorConstants.B2CAuthorityWithV2, validator.AadAuthorityV2, context); IdentityComparer.AreEqual(ProtocolVersion.V2, validator.AadAuthorityVersion, context); @@ -502,7 +605,7 @@ public void Validate_FromB2CAuthority_WithTidClaim_ValidateSuccessfully() } [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); @@ -519,12 +622,22 @@ public void Validate_FromB2CAuthority_InvalidIssuer_Fails() { ValidIssuers = new[] { ValidatorConstants.B2CIssuer }, })); - IdentityComparer.AreEqual(string.Format(LogMessages.IDX40001, ValidatorConstants.B2CIssuer2), exception.Message, context); + + ValidationResult validatedIssuer = await ValidateIssuerAsync( + ValidatorConstants.B2CIssuer2, + ValidatorConstants.B2CIssuer, + jwtSecurityToken, + validator); + + string expectedMessage = string.Format(LogMessages.IDX40001, ValidatorConstants.B2CIssuer2); + IdentityComparer.AreEqual(typeof(SecurityTokenInvalidIssuerException).ToString(), validatedIssuer.Error.ExceptionType.ToString(), context); + IdentityComparer.AreEqual(expectedMessage, validatedIssuer.Error.MessageDetail.Message, context); + IdentityComparer.AreEqual(expectedMessage, 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"; @@ -543,12 +656,21 @@ public void Validate_FromB2CAuthority_InvalidIssuerTid_Fails() ValidIssuers = new[] { ValidatorConstants.B2CIssuer }, })); - IdentityComparer.AreEqual(string.Format(LogMessages.IDX40001, issuerWithInvalidTid), exception.Message, context); + ValidationResult validatedIssuer = await ValidateIssuerAsync( + issuerWithInvalidTid, + ValidatorConstants.B2CIssuer, + jwtSecurityToken, + validator); + + string expectedMessage = string.Format(LogMessages.IDX40001, issuerWithInvalidTid); + IdentityComparer.AreEqual(typeof(SecurityTokenInvalidIssuerException).ToString(), validatedIssuer.Error.ExceptionType.ToString(), context); + IdentityComparer.AreEqual(expectedMessage, validatedIssuer.Error.MessageDetail.Message, context); + IdentityComparer.AreEqual(expectedMessage, 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); @@ -557,7 +679,7 @@ public void Validate_FromCustomB2CAuthority_ValidateSuccessfully() AadIssuerValidator validator = CreateIssuerValidator(ValidatorConstants.B2CCustomDomainAuthorityWithV2); - validator.Validate( + string issuer = validator.Validate( ValidatorConstants.B2CCustomDomainIssuer, jwtSecurityToken, new TokenValidationParameters() @@ -565,6 +687,16 @@ public void Validate_FromCustomB2CAuthority_ValidateSuccessfully() ValidIssuers = new[] { ValidatorConstants.B2CCustomDomainIssuer }, }); + ValidationResult validatedIssuer = await ValidateIssuerAsync( + ValidatorConstants.B2CCustomDomainIssuer, + ValidatorConstants.B2CCustomDomainIssuer, + jwtSecurityToken, + validator); + + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(ValidatorConstants.B2CCustomDomainIssuer, validatedIssuer.Result.Issuer, context); + IdentityComparer.AreEqual(ValidatorConstants.B2CCustomDomainIssuer, issuer, context); + IdentityComparer.AreEqual(ValidatorConstants.B2CCustomDomainAuthority, validator.AadAuthorityV1, context); IdentityComparer.AreEqual(ValidatorConstants.B2CCustomDomainAuthorityWithV2, validator.AadAuthorityV2, context); IdentityComparer.AreEqual(ProtocolVersion.V2, validator.AadAuthorityVersion, context); @@ -572,7 +704,7 @@ public void Validate_FromCustomB2CAuthority_ValidateSuccessfully() } [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); @@ -589,6 +721,15 @@ public void Validate_FromB2CAuthority_WithTfpIssuer_ThrowsException() ValidIssuers = new[] { ValidatorConstants.B2CIssuerTfp }, })); + ValidationResult validatedIssuer = await ValidateIssuerAsync( + ValidatorConstants.B2CIssuerTfp, + ValidatorConstants.B2CIssuerTfp, + jwtSecurityToken, + validator); + + Assert.False(validatedIssuer.IsValid); + IdentityComparer.AreEqual(typeof(SecurityTokenInvalidIssuerException).ToString(), validatedIssuer.Error.ExceptionType.ToString(), context); + IdentityComparer.AreEqual(LogMessages.IDX40002, validatedIssuer.Error.MessageDetail.Message, context); IdentityComparer.AreEqual(LogMessages.IDX40002, exception.Message, context); TestUtilities.AssertFailIfErrors(context); } @@ -603,7 +744,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,7 +812,13 @@ public void Validate_WithAuthorityUsingConfigurationProvider(ProtocolVersion aut var aadIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(authority, _httpClient, configurationManagerProvider); var actualIssuer = aadIssuerValidator.Validate(tokenIssuer, jwtSecurityToken, new TokenValidationParameters()); + ValidationResult validatedIssuer = await ValidateIssuerAsync( + tokenIssuer, + jwtSecurityToken, + aadIssuerValidator); + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(tokenIssuer, validatedIssuer.Result.Issuer, context); IdentityComparer.AreEqual(tokenIssuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); } @@ -686,7 +833,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) => { @@ -772,6 +919,13 @@ public void Validate_UsesLKGWithoutConfigurationProvider(ProtocolVersion authori // set LKG var actualIssuer = aadIssuerValidator.Validate(issuer, jwtSecurityToken, new TokenValidationParameters()); + ValidationResult validatedIssuer = await ValidateIssuerAsync( + issuer, + jwtSecurityToken, + aadIssuerValidator); + + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(issuer, validatedIssuer.Result.Issuer, context); IdentityComparer.AreEqual(issuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); @@ -779,6 +933,14 @@ public void Validate_UsesLKGWithoutConfigurationProvider(ProtocolVersion authori configurationManagerSetter(aadIssuerValidator, true); actualIssuer = aadIssuerValidator.Validate(issuer, jwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); + validatedIssuer = await ValidateIssuerAsync( + issuer, + jwtSecurityToken, + aadIssuerValidator, + true); + + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(issuer, validatedIssuer.Result.Issuer, context); IdentityComparer.AreEqual(issuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); } @@ -793,7 +955,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) => { @@ -825,16 +987,19 @@ public void Validate_CanFetchMetadataWithoutConfigurationProvider(ProtocolVersio var jwtSecurityToken = new JwtSecurityToken(issuer: issuer, claims: new[] { issClaim, tidClaim }); var authority = authorityUrlProvider(authorityVersion); - var aadIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(authority, _httpClient); + var validator = AadIssuerValidator.GetAadIssuerValidator(authority, _httpClient); - // set LKG - var actualIssuer = aadIssuerValidator.Validate(issuer, jwtSecurityToken, new TokenValidationParameters()); + ValidationResult validatedIssuer = await ValidateIssuerAsync(issuer, jwtSecurityToken, validator); + var actualIssuer = validator.Validate(issuer, jwtSecurityToken, new TokenValidationParameters()); + + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(issuer, validatedIssuer.Result.Issuer, context); IdentityComparer.AreEqual(issuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); } [Fact] - public void Validate_UsesLKGWithConfigurationProvider() + public async Task Validate_UsesLKGWithConfigurationProvider() { var v1Configuration = new OpenIdConnectConfiguration { @@ -890,7 +1055,13 @@ public void Validate_UsesLKGWithConfigurationProvider() // set LKG var actualIssuer = aadIssuerValidator.Validate(v2TokenIssuer, jwtSecurityToken, new TokenValidationParameters()); + ValidationResult validatedIssuer = await ValidateIssuerAsync( + v2TokenIssuer, + jwtSecurityToken, + aadIssuerValidator); + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(v2TokenIssuer, validatedIssuer.Result.Issuer, context); IdentityComparer.AreEqual(v2TokenIssuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); @@ -899,6 +1070,14 @@ public void Validate_UsesLKGWithConfigurationProvider() v2ConfigurationManager.RequestRefresh(); actualIssuer = aadIssuerValidator.Validate(v2TokenIssuer, jwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); + validatedIssuer = await ValidateIssuerAsync( + v2TokenIssuer, + jwtSecurityToken, + aadIssuerValidator, + true); + + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(v2TokenIssuer, validatedIssuer.Result.Issuer, context); IdentityComparer.AreEqual(v2TokenIssuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); @@ -908,9 +1087,19 @@ public void Validate_UsesLKGWithConfigurationProvider() // before testing v1 LKG setup v1 LKG for v2 manager for cross version validation _ = aadIssuerValidator.Validate(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters()); + _ = await ValidateIssuerAsync( + v1TokenIssuer, + v1JwtSecurityToken, + aadIssuerValidator); // V1 token and authority behaves like v2 token and authority actualIssuer = v1AadIssuerValidator.Validate(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters()); + validatedIssuer = await ValidateIssuerAsync( + v1TokenIssuer, + v1JwtSecurityToken, + aadIssuerValidator); + + IdentityComparer.AreEqual(validatedIssuer.Result.Issuer, v1TokenIssuer, context); IdentityComparer.AreEqual(v1TokenIssuer, actualIssuer, context); IdentityComparer.AreEqual(null, v1ConfigurationManager.LastKnownGoodConfiguration, context); TestUtilities.AssertFailIfErrors(context); @@ -920,17 +1109,32 @@ public void Validate_UsesLKGWithConfigurationProvider() v1ConfigurationManager.RequestRefresh(); actualIssuer = v1AadIssuerValidator.Validate(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); + validatedIssuer = await ValidateIssuerAsync( + v1TokenIssuer, + v1JwtSecurityToken, + aadIssuerValidator, + true); + + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(v1TokenIssuer, validatedIssuer.Result.Issuer, context); IdentityComparer.AreEqual(v1TokenIssuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); // validating cross versions also validates with LKG actualIssuer = aadIssuerValidator.Validate(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); - + validatedIssuer = await ValidateIssuerAsync( + v1TokenIssuer, + v1JwtSecurityToken, + aadIssuerValidator, + true); + + Assert.True(validatedIssuer.IsValid); + IdentityComparer.AreEqual(v1TokenIssuer, validatedIssuer.Result.Issuer, context); IdentityComparer.AreEqual(v1TokenIssuer, actualIssuer, context); TestUtilities.AssertFailIfErrors(context); // if LKG not valid validation fails - // set confgimanager lkg lifetime to 1ms + // set ConfigurationManager lkg lifetime to 1ms // validate successfully to set LKG // wait 1ms, validate with expired LKG v1ConfigurationManager.RefreshedConfiguration = v1Configuration; @@ -938,12 +1142,23 @@ public void Validate_UsesLKGWithConfigurationProvider() v1ConfigurationManager.LastKnownGoodLifetime = TimeSpan.FromMilliseconds(1); actualIssuer = aadIssuerValidator.Validate(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters()); + validatedIssuer = await ValidateIssuerAsync( + v1TokenIssuer, + v1JwtSecurityToken, + aadIssuerValidator); + Thread.Sleep(TimeSpan.FromMilliseconds(1)); var securityExceptionThrown = false; var exceptionMessage = string.Empty; try { + validatedIssuer = await ValidateIssuerAsync( + v1TokenIssuer, + v1JwtSecurityToken, + aadIssuerValidator, + true); + _ = aadIssuerValidator.Validate(v1TokenIssuer, v1JwtSecurityToken, new TokenValidationParameters { ValidateWithLKG = true }); } catch (SecurityTokenInvalidIssuerException securityException) @@ -952,11 +1167,65 @@ public void Validate_UsesLKGWithConfigurationProvider() exceptionMessage = securityException.Message; } + Assert.False(validatedIssuer.IsValid); + IdentityComparer.AreEqual(string.Format(LogMessages.IDX40001, "https://sts.windows.net/f645ad92-e38d-4d1a-b510-d1b09a74a8ca/"), validatedIssuer.Error.MessageDetail.Message, context); + 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); + IdentityComparer.AreEqual(string.Format(LogMessages.IDX40001, "https://sts.windows.net/f645ad92-e38d-4d1a-b510-d1b09a74a8ca/"), exceptionMessage, context); TestUtilities.AssertFailIfErrors(context); } + + private static async Task> ValidateIssuerAsync( + string issuerFromToken, + SecurityToken securityToken, + AadIssuerValidator validator, + bool validateWithLKG = false) + { + ValidationParameters validationParameters = new ValidationParameters() { ValidateWithLKG = validateWithLKG }; + CallContext callContext = new CallContext(); + + return await validator.ValidateIssuerAsync( + issuerFromToken, + securityToken, + validationParameters, + callContext, + CancellationToken.None); + } + + private static async Task> ValidateIssuerAsync( + string issuerFromToken, + string addIssuerToValidationParameters, + SecurityToken securityToken, + AadIssuerValidator validator) + { + ValidationParameters validationParameters = new ValidationParameters(); + validationParameters.ValidIssuers.Add(addIssuerToValidationParameters); + CallContext callContext = new CallContext(); + + return await validator.ValidateIssuerAsync( + issuerFromToken, + securityToken, + validationParameters, + callContext, + CancellationToken.None); + } + + private static async Task> ValidateIssuerAsync( + string issuerFromToken, + BaseConfigurationManager configurationManager, + SecurityToken securityToken, + AadIssuerValidator validator) + { + ValidationParameters validationParameters = new ValidationParameters(); + validationParameters.ConfigurationManager = configurationManager; + + return await validator.ValidateIssuerAsync( + issuerFromToken, + securityToken, + validationParameters, + new CallContext(), + CancellationToken.None); + } } } - #pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant