diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs index 1db69bf6f0..bf1c3fbadf 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs @@ -1298,7 +1298,7 @@ private async Task ValidateTokenAsync(JsonWebToken jsonWe } } - TokenValidationResult tokenValidationResult = ValidateToken(jsonWebToken, validationParameters, currentConfiguration); + TokenValidationResult tokenValidationResult = await ValidateTokenAsync(jsonWebToken, validationParameters, currentConfiguration).ConfigureAwait(false); if (validationParameters.ConfigurationManager != null) { if (tokenValidationResult.IsValid) @@ -1320,12 +1320,12 @@ private async Task ValidateTokenAsync(JsonWebToken jsonWe validationParameters.ConfigurationManager.RequestRefresh(); validationParameters.RefreshBeforeValidation = true; var lastConfig = currentConfiguration; - currentConfiguration = validationParameters.ConfigurationManager.GetBaseConfigurationAsync(CancellationToken.None).GetAwaiter().GetResult(); + currentConfiguration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false); // Only try to re-validate using the newly obtained config if it doesn't reference equal the previously used configuration. if (lastConfig != currentConfiguration) { - tokenValidationResult = ValidateToken(jsonWebToken, validationParameters, currentConfiguration); + tokenValidationResult = await ValidateTokenAsync(jsonWebToken, validationParameters, currentConfiguration).ConfigureAwait(false); if (tokenValidationResult.IsValid) { @@ -1345,7 +1345,7 @@ private async Task ValidateTokenAsync(JsonWebToken jsonWe { if (!lkgConfiguration.Equals(currentConfiguration) && TokenUtilities.IsRecoverableConfiguration(jsonWebToken.Kid, currentConfiguration, lkgConfiguration, recoverableException)) { - tokenValidationResult = ValidateToken(jsonWebToken, validationParameters, lkgConfiguration); + tokenValidationResult = await ValidateTokenAsync(jsonWebToken, validationParameters, lkgConfiguration).ConfigureAwait(false); if (tokenValidationResult.IsValid) return tokenValidationResult; @@ -1358,15 +1358,15 @@ private async Task ValidateTokenAsync(JsonWebToken jsonWe return tokenValidationResult; } - private TokenValidationResult ValidateToken(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + private async Task ValidateTokenAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) { if (jsonWebToken.IsEncrypted) - return ValidateJWE(jsonWebToken, validationParameters, configuration); + return await ValidateJWEAsync(jsonWebToken, validationParameters, configuration).ConfigureAwait(false); - return ValidateJWS(jsonWebToken, validationParameters, configuration); + return await ValidateJWSAsync(jsonWebToken, validationParameters, configuration).ConfigureAwait(false); } - private TokenValidationResult ValidateJWS(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + private async Task ValidateJWSAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) { try { @@ -1377,21 +1377,21 @@ private TokenValidationResult ValidateJWS(JsonWebToken jsonWebToken, TokenValida if (validationParameters.SignatureValidator != null || validationParameters.SignatureValidatorUsingConfiguration != null) { var validatedToken = ValidateSignatureUsingDelegates(jsonWebToken.EncodedToken, validationParameters, configuration); - tokenValidationResult = ValidateTokenPayload(validatedToken, validationParameters, configuration); + tokenValidationResult = await ValidateTokenPayloadAsync(validatedToken, validationParameters, configuration).ConfigureAwait(false); Validators.ValidateIssuerSecurityKey(validatedToken.SigningKey, validatedToken, validationParameters, configuration); } else { if (validationParameters.ValidateSignatureLast) { - tokenValidationResult = ValidateTokenPayload(jsonWebToken, validationParameters, configuration); + tokenValidationResult = await ValidateTokenPayloadAsync(jsonWebToken, validationParameters, configuration).ConfigureAwait(false); if (tokenValidationResult.IsValid) tokenValidationResult.SecurityToken = ValidateSignatureAndIssuerSecurityKey(jsonWebToken, validationParameters, configuration); } else { var validatedToken = ValidateSignatureAndIssuerSecurityKey(jsonWebToken, validationParameters, configuration); - tokenValidationResult = ValidateTokenPayload(validatedToken, validationParameters, configuration); + tokenValidationResult = await ValidateTokenPayloadAsync(validatedToken, validationParameters, configuration).ConfigureAwait(false); } } @@ -1410,16 +1410,15 @@ private TokenValidationResult ValidateJWS(JsonWebToken jsonWebToken, TokenValida } } - private TokenValidationResult ValidateJWE(JsonWebToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + private async Task ValidateJWEAsync(JsonWebToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) { try { - string jws = DecryptToken(jwtToken, validationParameters, configuration); - TokenValidationResult readTokenResult = ReadToken(jws, validationParameters); - if (!readTokenResult.IsValid) - return readTokenResult; + TokenValidationResult tokenValidationResult = ReadToken(DecryptToken(jwtToken, validationParameters, configuration), validationParameters); + if (!tokenValidationResult.IsValid) + return tokenValidationResult; - TokenValidationResult tokenValidationResult = ValidateJWS(readTokenResult.SecurityToken as JsonWebToken, validationParameters, configuration); + tokenValidationResult = await ValidateJWSAsync(tokenValidationResult.SecurityToken as JsonWebToken, validationParameters, configuration).ConfigureAwait(false); if (!tokenValidationResult.IsValid) return tokenValidationResult; @@ -1482,14 +1481,14 @@ private static JsonWebToken ValidateSignatureAndIssuerSecurityKey(JsonWebToken j return validatedToken; } - private TokenValidationResult ValidateTokenPayload(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + private async Task ValidateTokenPayloadAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) { var expires = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Exp) ? (DateTime?)jsonWebToken.ValidTo : null; var notBefore = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Nbf) ? (DateTime?)jsonWebToken.ValidFrom : null; Validators.ValidateLifetime(notBefore, expires, jsonWebToken, validationParameters); Validators.ValidateAudience(jsonWebToken.Audiences, jsonWebToken, validationParameters); - string issuer = Validators.ValidateIssuer(jsonWebToken.Issuer, jsonWebToken, validationParameters, configuration); + string issuer = await Validators.ValidateIssuerAsync(jsonWebToken.Issuer, jsonWebToken, validationParameters, configuration).ConfigureAwait(false); Validators.ValidateTokenReplay(expires, jsonWebToken.EncodedToken, validationParameters); if (validationParameters.ValidateActor && !string.IsNullOrWhiteSpace(jsonWebToken.Actor)) @@ -1500,7 +1499,9 @@ private TokenValidationResult ValidateTokenPayload(JsonWebToken jsonWebToken, To // and (since issuer validation occurs first) came from a trusted authority. // NOTE: More than one nested actor token should not be considered a valid token, but if we somehow encounter one, // this code will still work properly. - TokenValidationResult tokenValidationResult = ValidateToken(jsonWebToken.Actor, validationParameters.ActorValidationParameters ?? validationParameters); + TokenValidationResult tokenValidationResult = + await ValidateTokenAsync(jsonWebToken.Actor, validationParameters.ActorValidationParameters ?? validationParameters).ConfigureAwait(false); + if (!tokenValidationResult.IsValid) return tokenValidationResult; } diff --git a/src/Microsoft.IdentityModel.Tokens/Properties/AssemblyInfo.cs b/src/Microsoft.IdentityModel.Tokens/Properties/AssemblyInfo.cs index eea8e88572..65c4dee54d 100644 --- a/src/Microsoft.IdentityModel.Tokens/Properties/AssemblyInfo.cs +++ b/src/Microsoft.IdentityModel.Tokens/Properties/AssemblyInfo.cs @@ -26,4 +26,10 @@ [assembly: InternalsVisibleTo("Microsoft.IdentityModel.Validators, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("Microsoft.IdentityModel.Validators.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("Microsoft.IdentityModel.TestExtensions, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S.Configuration, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S.Configuration.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S.Extensions.AspNetCore.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S.Tokens, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S.Tokens.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs index 93d77fa3c3..cf40db0b45 100644 --- a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs +++ b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Security.Claims; +using System.Threading.Tasks; using Microsoft.IdentityModel.Logging; namespace Microsoft.IdentityModel.Tokens @@ -100,6 +101,18 @@ namespace Microsoft.IdentityModel.Tokens /// public delegate string IssuerValidatorUsingConfiguration(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration); + /// + /// Definition for IssuerValidatorAsync. Left internal for now while we work out the details of async validation for all delegates. + /// + /// The issuer to validate. + /// The that is being validated. + /// required for validation. + /// The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity". + /// The delegate should return a non null string that represents the 'issuer'. If null a default value will be used. + /// if set, will be called before or + /// + internal delegate Task IssuerValidatorAsync(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters); + /// /// Definition for LifetimeValidator. /// @@ -178,8 +191,9 @@ public class TokenValidationParameters private string _roleClaimType = ClaimsIdentity.DefaultRoleClaimType; /// - /// This is the fallback authenticationtype that a will use if nothing is set. + /// This is the default value of when creating a . /// The value is "AuthenticationTypes.Federation". + /// To change the value, set to a different value. /// public static readonly string DefaultAuthenticationType = "AuthenticationTypes.Federation"; // Note: The change was because 5.x removed the dependency on System.IdentityModel and we used a different string which was a mistake. @@ -190,7 +204,7 @@ public class TokenValidationParameters public static readonly TimeSpan DefaultClockSkew = TimeSpan.FromSeconds(300); // 5 min. /// - /// Default for the maximm token size. + /// Default for the maximum token size. /// /// 250 KB (kilobytes). public const Int32 DefaultMaximumTokenSizeInBytes = 1024 * 250; @@ -218,7 +232,10 @@ protected TokenValidationParameters(TokenValidationParameters other) IssuerSigningKeyResolverUsingConfiguration = other.IssuerSigningKeyResolverUsingConfiguration; IssuerSigningKeys = other.IssuerSigningKeys; IssuerSigningKeyValidator = other.IssuerSigningKeyValidator; + IssuerSigningKeyValidatorUsingConfiguration = other.IssuerSigningKeyValidatorUsingConfiguration; IssuerValidator = other.IssuerValidator; + IssuerValidatorAsync = other.IssuerValidatorAsync; + IssuerValidatorUsingConfiguration = other.IssuerValidatorUsingConfiguration; LifetimeValidator = other.LifetimeValidator; LogTokenId = other.LogTokenId; LogValidationExceptions = other.LogValidationExceptions; @@ -233,6 +250,7 @@ protected TokenValidationParameters(TokenValidationParameters other) RoleClaimTypeRetriever = other.RoleClaimTypeRetriever; SaveSigninToken = other.SaveSigninToken; SignatureValidator = other.SignatureValidator; + SignatureValidatorUsingConfiguration = other.SignatureValidatorUsingConfiguration; TokenDecryptionKey = other.TokenDecryptionKey; TokenDecryptionKeyResolver = other.TokenDecryptionKeyResolver; TokenDecryptionKeys = other.TokenDecryptionKeys; @@ -260,7 +278,7 @@ protected TokenValidationParameters(TokenValidationParameters other) /// /// Initializes a new instance of the class. - /// + /// public TokenValidationParameters() { LogTokenId = true; @@ -518,6 +536,17 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken, public IssuerValidator IssuerValidator { get; set; } + /// + /// Gets or sets a delegate that will be used to validate the issuer of the token. + /// + /// + /// If set, this delegate will be called to validate the 'issuer' of the token, instead of default processing. + /// This means that no default 'issuer' validation will occur. + /// Even if is false, this delegate will still be called. + /// IssuerValidatorAsync takes precedence over and . + /// + internal IssuerValidatorAsync IssuerValidatorAsync { get; set; } + /// /// Gets or sets a delegate that will be used to validate the issuer of the token. /// diff --git a/src/Microsoft.IdentityModel.Tokens/Validators.cs b/src/Microsoft.IdentityModel.Tokens/Validators.cs index 4eb5ade68b..38364489ad 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validators.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validators.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; using Microsoft.IdentityModel.Logging; namespace Microsoft.IdentityModel.Tokens @@ -208,10 +209,36 @@ public static string ValidateIssuer(string issuer, SecurityToken securityToken, /// If 'issuer' failed to matched either or one of or . /// An EXACT match is required. internal static string ValidateIssuer(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + { + return ValidateIssuerAsync(issuer, securityToken, validationParameters, configuration).GetAwaiter().GetResult(); + } + + /// + /// Determines if an issuer found in a is valid. + /// + /// The issuer to validate + /// The that is being validated. + /// required for validation. + /// The required for issuer and signing key validation. + /// The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity". + /// If 'validationParameters' is null. + /// If 'issuer' is null or whitespace and is true. + /// If ' configuration' is null. + /// If is null or whitespace and is null and is null. + /// If 'issuer' failed to matched either or one of or . + /// An EXACT match is required. + internal static async Task ValidateIssuerAsync( + string issuer, + SecurityToken securityToken, + TokenValidationParameters validationParameters, + BaseConfiguration configuration) { if (validationParameters == null) throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + if (validationParameters.IssuerValidatorAsync != null) + return await validationParameters.IssuerValidatorAsync(issuer, securityToken, validationParameters).ConfigureAwait(false); + if (validationParameters.IssuerValidatorUsingConfiguration != null) return validationParameters.IssuerValidatorUsingConfiguration(issuer, securityToken, validationParameters, configuration); @@ -226,18 +253,20 @@ internal static string ValidateIssuer(string issuer, SecurityToken securityToken if (string.IsNullOrWhiteSpace(issuer)) throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX10211) - { InvalidIssuer = issuer }); + { InvalidIssuer = issuer }); // Throw if all possible places to validate against are null or empty - if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer) && validationParameters.ValidIssuers.IsNullOrEmpty() && string.IsNullOrWhiteSpace(configuration?.Issuer)) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX10204) - { InvalidIssuer = issuer }); + if ( string.IsNullOrWhiteSpace(validationParameters.ValidIssuer) + && validationParameters.ValidIssuers.IsNullOrEmpty() + && string.IsNullOrWhiteSpace(configuration?.Issuer)) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX10204) + { InvalidIssuer = issuer }); if (configuration != null) { if (string.Equals(configuration.Issuer, issuer)) { - LogHelper.LogInformation(LogMessages.IDX10236, issuer); + LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer)); return issuer; } } @@ -272,7 +301,7 @@ internal static string ValidateIssuer(string issuer, SecurityToken securityToken LogHelper.MarkAsNonPII(validationParameters.ValidIssuer ?? "null"), LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidIssuers)), LogHelper.MarkAsNonPII(configuration?.Issuer))) - { InvalidIssuer = issuer }; + { InvalidIssuer = issuer }; if (!validationParameters.LogValidationExceptions) throw ex; diff --git a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs index 6308d9f5fd..1ce12b0c1f 100644 --- a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs +++ b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs @@ -7,6 +7,7 @@ using System.IdentityModel.Tokens.Jwt; using System.Net.Http; using System.Threading; +using System.Threading.Tasks; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Protocols; @@ -122,6 +123,32 @@ public string Validate( string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters) + { + return ValidateAsync(issuer, securityToken, validationParameters).GetAwaiter().GetResult(); + } + + /// + /// 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. + /// Token validation parameters. + /// + /// AadIssuerValidator aadIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(authority, httpClient); + /// TokenValidationParameters.IssuerValidator = aadIssuerValidator.Validate; + /// + /// The issuer is considered as valid if it has the same HTTP scheme and authority as the + /// authority from the configuration file, has a tenant ID, and optionally v2.0 (if this web API + /// accepts both V1 and V2 tokens). + /// The issuer if it's valid, or otherwise SecurityTokenInvalidIssuerException is thrown. + /// if is null. + /// if is null. + /// if the issuer is invalid or if there is a network issue. + internal async Task ValidateAsync( + string issuer, + SecurityToken securityToken, + TokenValidationParameters validationParameters) { _ = issuer ?? throw LogHelper.LogArgumentNullException(nameof(issuer)); _ = securityToken ?? throw LogHelper.LogArgumentNullException(nameof(securityToken)); @@ -149,11 +176,12 @@ public string Validate( try { - var effectiveConfigurationManager = GetEffectiveConfigurationManager(securityToken); + BaseConfigurationManager effectiveConfigurationManager = GetEffectiveConfigurationManager(securityToken); if (validationParameters.RefreshBeforeValidation) effectiveConfigurationManager.RequestRefresh(); - string aadIssuer = effectiveConfigurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult().Issuer; + BaseConfiguration configuration = await effectiveConfigurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false); + string aadIssuer = configuration.Issuer; if (!validationParameters.ValidateWithLKG) { @@ -172,11 +200,20 @@ public string Validate( } catch (Exception ex) { - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogHelper.FormatInvariant(LogMessages.IDX40001, LogHelper.MarkAsNonPII(issuer)), ex)); + throw LogHelper.LogExceptionMessage( + new SecurityTokenInvalidIssuerException( + LogHelper.FormatInvariant( + LogMessages.IDX40001, + LogHelper.MarkAsNonPII(issuer)), + ex)); } // If a valid issuer is not found, throw - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogHelper.FormatInvariant(LogMessages.IDX40001, LogHelper.MarkAsNonPII(issuer)))); + throw LogHelper.LogExceptionMessage( + new SecurityTokenInvalidIssuerException( + LogHelper.FormatInvariant( + LogMessages.IDX40001, + LogHelper.MarkAsNonPII(issuer)))); } /// diff --git a/src/Microsoft.IdentityModel.Validators/Properties/AssemblyInfo.cs b/src/Microsoft.IdentityModel.Validators/Properties/AssemblyInfo.cs index 68231d450c..a7c437bab0 100644 --- a/src/Microsoft.IdentityModel.Validators/Properties/AssemblyInfo.cs +++ b/src/Microsoft.IdentityModel.Validators/Properties/AssemblyInfo.cs @@ -17,4 +17,10 @@ [assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S.Extensions.AspNetCore.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("Microsoft.IdentityModel.JsonWebTokens.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("System.IdentityModel.Tokens.Jwt.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S.Configuration, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S.Configuration.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S.Extensions.AspNetCore.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S.Tokens, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.IdentityModel.S2S.Tokens.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/test/Microsoft.IdentityModel.TestUtils/ExpectedException.cs b/test/Microsoft.IdentityModel.TestUtils/ExpectedException.cs index 6660e91231..f23518890d 100644 --- a/test/Microsoft.IdentityModel.TestUtils/ExpectedException.cs +++ b/test/Microsoft.IdentityModel.TestUtils/ExpectedException.cs @@ -208,7 +208,9 @@ public static ExpectedException SecurityTokenEncryptionKeyNotFoundException(stri public static ExpectedException SecurityTokenUnableToValidateException(string substringExpected = null, Type innerTypeExpected = null) { +#pragma warning disable CS0618 // Type or member is obsolete return new ExpectedException(typeof(SecurityTokenUnableToValidateException), substringExpected, innerTypeExpected); +#pragma warning restore CS0618 // Type or member is obsolete } public static ExpectedException SecurityTokenEncryptionFailedException(string substringExpected = null, Type innerTypeExpected = null) diff --git a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs index 65ac68b359..6533b83b55 100644 --- a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs +++ b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs @@ -1,6 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +//#define CheckIfCompared + +// Uncomment 'CheckIfCompared' to find out if any of your types are not being compared. +// The default behavior is to compare all public properties, if there is a type that is not being compared you will get an exception. +// _equalityDict contains all the types that are being compared and how they are compared. +// Add the string representing the type "typeof(YourType)" and matching delegate for comparing to the dictionary. + using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -138,6 +145,7 @@ public class IdentityComparer { typeof(SigningCredentials).ToString(), CompareAllPublicProperties }, { typeof(string).ToString(), AreStringsEqual }, { typeof(SymmetricSecurityKey).ToString(), CompareAllPublicProperties }, + { typeof(TimeSpan).ToString(), AreTimeSpansEqual }, { typeof(TokenValidationParameters).ToString(), CompareAllPublicProperties }, { typeof(Transform).ToString(), CompareAllPublicProperties }, { typeof(WsFederationConfiguration).ToString(), CompareAllPublicProperties }, @@ -493,19 +501,32 @@ public static bool AreEqual(object object1, object object2, CompareContext conte // Check if either t1 or t2 are null or references of each other to see if we can terminate early. if (!ContinueCheckingEquality(object1, object2, localContext)) return context.Merge(localContext); - +#if CheckIfCompared + bool wasCompared = false; +#endif string inter; // Use a special function for comparison if required by the specific class of the object. if (_equalityDict.TryGetValue(object1.GetType().ToString(), out Func areEqual)) { +#if CheckIfCompared + wasCompared = true; +#endif areEqual(object1, object2, localContext); - } + } // Check if any of the interfaces that the class uses require a special function. else if ((inter = object1.GetType().GetInterfaces().Select(t => t.ToString()).Intersect(_equalityDict.Keys).FirstOrDefault()) != null) { +#if CheckIfCompared + wasCompared = true; +#endif _equalityDict[inter](object1, object2, localContext); } +#if CheckIfCompared + if (!wasCompared) + localContext.Diffs.Add($"Objects were not handled: '{object1.GetType().ToString()}'."); +#endif + return context.Merge(localContext); } @@ -914,6 +935,21 @@ public static bool AreSignedInfosEqual(SignedInfo signedInfo1, SignedInfo signed return context.Merge(localContext); } + public static bool AreTimeSpansEqual(object object1, object object2, CompareContext context) + { + var localContext = new CompareContext(context); + if (!ContinueCheckingEquality(object1, object2, localContext)) + return context.Merge(localContext); + + TimeSpan timeSpan1 = (TimeSpan)object1; + TimeSpan timeSpan2 = (TimeSpan)object2; + + if (timeSpan1 != timeSpan2) + localContext.Diffs.Add($"timeSpan1 != timeSpan2. '{timeSpan1}' != '{timeSpan2}'."); + + return context.Merge(localContext); + } + public static bool AreWsFederationConfigurationsEqual(WsFederationConfiguration configuration1, WsFederationConfiguration configuration2, CompareContext context) { var localContext = new CompareContext(context); diff --git a/test/Microsoft.IdentityModel.TestUtils/TestUtilities.cs b/test/Microsoft.IdentityModel.TestUtils/TestUtilities.cs index c80ca84010..c918b5c01c 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TestUtilities.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TestUtilities.cs @@ -134,7 +134,7 @@ public static void GetSet(GetSetContext context) { context.Errors.Add(propertyKV.Key + ": initial value != null && expected == null, initial value: " + initialValue.ToString()); } - else if (initialValue != null && !initialValue.Equals(propertyKV.Value[0])) + else if (initialValue != null && !IdentityComparer.AreEqual(initialValue, propertyKV.Value[0])) { context.Errors.Add(propertyKV.Key + ", initial value != expected. expected: " + propertyKV.Value[0].ToString() + ", was: " + initialValue.ToString()); } diff --git a/test/Microsoft.IdentityModel.TestUtils/ValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/ValidationDelegates.cs index 4ed419e722..753f4f0fe4 100644 --- a/test/Microsoft.IdentityModel.TestUtils/ValidationDelegates.cs +++ b/test/Microsoft.IdentityModel.TestUtils/ValidationDelegates.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; +using System.Threading.Tasks; +using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens.Saml; using Microsoft.IdentityModel.Tokens.Saml2; @@ -20,6 +22,11 @@ public static AlgorithmValidator AlgorithmValidatorBuilder(bool result) return (string algorithm, SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters) => result; } + public static bool AlgorithmValidator(string algorithm, SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters) + { + return true; + } + public static bool AudienceValidatorReturnsFalse(IEnumerable audiences, SecurityToken token, TokenValidationParameters validationParameters) { return false; @@ -50,11 +57,41 @@ public static bool IssuerSecurityKeyValidatorThrows(SecurityKey securityKey, Sec throw new SecurityTokenInvalidSigningKeyException("IssuerSecurityKeyValidatorThrows"); } + public static IEnumerable IssuerSigningKeyResolver(string token, SecurityToken securityToken, string kid, TokenValidationParameters validationParameters) + { + return new List(); + } + + public static IEnumerable IssuerSigningKeyResolverUsingConfiguration(string token, SecurityToken securityToken, string kid, TokenValidationParameters validationParameters, BaseConfiguration configuration) + { + return new List(); + } + + public static bool IssuerSigningKeyValidator(SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters) + { + return true; + } + + public static bool IssuerSigningKeyValidatorUsingConfiguration(SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + { + return true; + } + public static string IssuerValidatorEcho(string issuer, SecurityToken token, TokenValidationParameters validationParameters) { return issuer; } + public static string IssuerValidatorUsingConfigEcho(string issuer, SecurityToken token, TokenValidationParameters validationParameters, BaseConfiguration baseConfiguration) + { + return issuer; + } + + public static Task IssuerValidatorAsync(string issuer, SecurityToken token, TokenValidationParameters validationParameters) + { + return Task.FromResult(issuer); + } + public static string IssuerValidatorReturnsDifferentIssuer(string issuer, SecurityToken token, TokenValidationParameters validationParameters) { return "DifferentIssuer"; @@ -85,11 +122,26 @@ public static bool LifetimeValidatorThrows(DateTime? expires, DateTime? notBefor throw new SecurityTokenInvalidLifetimeException("LifetimeValidatorThrows"); } + public static string NameClaimTypeRetriever(SecurityToken securityToken, string issuer) + { + return "NameClaimType"; + } + + public static string RoleClaimTypeRetriever(SecurityToken securityToken, string issuer) + { + return "RoleClaimType"; + } + public static SecurityToken SignatureValidatorReturnsJwtTokenAsIs(string token, TokenValidationParameters validationParameters) { return new JwtSecurityToken(token); } + public static SecurityToken SignatureValidatorReturnsJsonWebToken(string token, TokenValidationParameters validationParameters) + { + return new JsonWebToken(token); + } + public static SecurityToken SignatureValidatorReturnsNull(string token, TokenValidationParameters validationParameters) { return null; @@ -100,11 +152,31 @@ public static SecurityToken SignatureValidatorThrows(string token, TokenValidati throw new SecurityTokenInvalidSignatureException("SignatureValidatorThrows"); } + public static SecurityToken SignatureValidatorUsingConfigReturnsJwtTokenAsIs(string token, TokenValidationParameters validationParameters, BaseConfiguration baseConfiguration) + { + return new JwtSecurityToken(token); + } + + public static SecurityToken SignatureValidatorUsingConfigReturnsJsonWebToken(string token, TokenValidationParameters validationParameters, BaseConfiguration baseConfiguration) + { + return new JsonWebToken(token); + } + + public static IEnumerable TokenDecryptionKeyResolver(string token, SecurityToken securityToken, string kid, TokenValidationParameters validationParameters) + { + return new List(); + } + public static SecurityToken TokenReaderReturnsJwtSecurityToken(string token, TokenValidationParameters validationParameters) { return new JwtSecurityToken(token); } + public static SecurityToken TokenReaderReturnsJsonWebToken(string token, TokenValidationParameters validationParameters) + { + return new JsonWebToken(token); + } + public static SecurityToken TokenReaderReturnsIncorrectSecurityTokenType(string token, TokenValidationParameters validationParameters) { return new DerivedSecurityToken(); @@ -161,5 +233,15 @@ public static bool TokenReplayValidatorThrows(DateTime? expires, string token, T { throw new SecurityTokenReplayDetectedException("TokenReplayValidatorThrows"); } + + public static SecurityToken TransformBeforeSignatureValidation(SecurityToken token, TokenValidationParameters validationParameters) + { + return token; + } + + public static string TypeValidator(string type, SecurityToken securityToken, TokenValidationParameters validationParameters) + { + return type; + } } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/SecurityTokenExceptionTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/SecurityTokenExceptionTests.cs index 9c01ee5083..39e3302532 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/SecurityTokenExceptionTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/SecurityTokenExceptionTests.cs @@ -65,7 +65,7 @@ public static TheoryData ExceptionTestData ExceptionSetter = (ex) => { if (!(ex is SecurityTokenInvalidAudienceException securityTokenInvalidAudienceException)) - throw new ArgumentException($"expected argument of type {nameof(SecurityTokenInvalidAudienceException)} recieved type {ex.GetType()}"); + throw new ArgumentException($"expected argument of type {nameof(SecurityTokenInvalidAudienceException)} received type {ex.GetType()}"); securityTokenInvalidAudienceException.InvalidAudience = Guid.NewGuid().ToString(); } @@ -82,7 +82,7 @@ public static TheoryData ExceptionTestData ExceptionSetter = (ex) => { if (!(ex is SecurityTokenInvalidIssuerException securityTokenInvalidIssuerException)) - throw new ArgumentException($"expected argument of type {nameof(SecurityTokenInvalidIssuerException)} recieved type {ex.GetType()}"); + throw new ArgumentException($"expected argument of type {nameof(SecurityTokenInvalidIssuerException)} received type {ex.GetType()}"); securityTokenInvalidIssuerException.InvalidIssuer = Guid.NewGuid().ToString(); } @@ -99,7 +99,7 @@ public static TheoryData ExceptionTestData ExceptionSetter = (ex) => { if (!(ex is SecurityTokenExpiredException securityTokenExpiredException)) - throw new ArgumentException($"expected argument of type {nameof(SecurityTokenExpiredException)} recieved type {ex.GetType()}"); + throw new ArgumentException($"expected argument of type {nameof(SecurityTokenExpiredException)} received type {ex.GetType()}"); securityTokenExpiredException.Expires = DateTime.Now; } @@ -116,7 +116,7 @@ public static TheoryData ExceptionTestData ExceptionSetter = (ex) => { if (!(ex is SecurityTokenInvalidLifetimeException securityTokenInvalidLifetimeException)) - throw new ArgumentException($"expected argument of type {nameof(SecurityTokenInvalidLifetimeException)} recieved type {ex.GetType()}"); + throw new ArgumentException($"expected argument of type {nameof(SecurityTokenInvalidLifetimeException)} received type {ex.GetType()}"); securityTokenInvalidLifetimeException.Expires = DateTime.Now; securityTokenInvalidLifetimeException.NotBefore = DateTime.Now; @@ -134,7 +134,7 @@ public static TheoryData ExceptionTestData ExceptionSetter = (ex) => { if (!(ex is SecurityTokenInvalidTypeException securityTokenInvalidTypeException)) - throw new ArgumentException($"expected argument of type {nameof(SecurityTokenInvalidTypeException)} recieved type {ex.GetType()}"); + throw new ArgumentException($"expected argument of type {nameof(SecurityTokenInvalidTypeException)} received type {ex.GetType()}"); securityTokenInvalidTypeException.InvalidType = Guid.NewGuid().ToString(); } @@ -151,7 +151,7 @@ public static TheoryData ExceptionTestData ExceptionSetter = (ex) => { if (!(ex is SecurityTokenNotYetValidException securityTokenNotYetValidException)) - throw new ArgumentException($"expected argument of type {nameof(SecurityTokenNotYetValidException)} recieved type {ex.GetType()}"); + throw new ArgumentException($"expected argument of type {nameof(SecurityTokenNotYetValidException)} received type {ex.GetType()}"); securityTokenNotYetValidException.NotBefore = DateTime.Now; } @@ -168,7 +168,7 @@ public static TheoryData ExceptionTestData ExceptionSetter = (ex) => { if (!(ex is SecurityTokenInvalidSigningKeyException securityTokenInvalidSigningKeyException)) - throw new ArgumentException($"expected argument of type {nameof(SecurityTokenInvalidSigningKeyException)} recieved type {ex.GetType()}"); + throw new ArgumentException($"expected argument of type {nameof(SecurityTokenInvalidSigningKeyException)} received type {ex.GetType()}"); securityTokenInvalidSigningKeyException.SigningKey = new CustomSecurityKey(); }, @@ -189,7 +189,7 @@ public static TheoryData ExceptionTestData ExceptionSetter = (ex) => { if (!(ex is SecurityTokenInvalidAlgorithmException securityTokenInvalidAlgorithm)) - throw new ArgumentException($"expected argument of type {nameof(SecurityTokenInvalidAlgorithmException)} recieved type {ex.GetType()}"); + throw new ArgumentException($"expected argument of type {nameof(SecurityTokenInvalidAlgorithmException)} received type {ex.GetType()}"); securityTokenInvalidAlgorithm.InvalidAlgorithm = Guid.NewGuid().ToString(); }, @@ -199,6 +199,7 @@ public static TheoryData ExceptionTestData TestId = "SecurityTokenInvalidAlgorithmSerializesPropertiesDefaultValue", ExceptionType = typeof(SecurityTokenInvalidAlgorithmException), }, +#pragma warning disable CS0618 // Type or member is obsolete new SecurityTokenExceptionTheoryData { TestId = "SecurityTokenUnableToValidateExceptionDefaultValue", @@ -211,12 +212,13 @@ public static TheoryData ExceptionTestData ExceptionSetter = (ex) => { if (!(ex is SecurityTokenUnableToValidateException securityTokenUnableToValidateException)) - throw new ArgumentException($"expected argument of type {nameof(SecurityTokenUnableToValidateException)} recieved type {ex.GetType()}"); + throw new ArgumentException($"expected argument of type {nameof(SecurityTokenUnableToValidateException)} received type {ex.GetType()}"); securityTokenUnableToValidateException.ValidationFailure = ValidationFailure.InvalidIssuer; securityTokenUnableToValidateException.ValidationFailure |= ValidationFailure.InvalidLifetime; }, }, +#pragma warning restore CS0618 // Type or member is obsolete }; } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidationParametersTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidationParametersTests.cs index f28f91ab29..041de6d40d 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidationParametersTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidationParametersTests.cs @@ -10,20 +10,19 @@ using Microsoft.IdentityModel.Protocols.WsFederation; using Microsoft.IdentityModel.TestUtils; using Xunit; -using Xunit.Sdk; namespace Microsoft.IdentityModel.Tokens.Tests { public class TokenValidationParametersTests { - int ExpectedPropertyCount = 58; + int ExpectedPropertyCount = 59; [Fact] public void Publics() { TokenValidationParameters validationParameters = new TokenValidationParameters(); Type type = typeof(TokenValidationParameters); - PropertyInfo[] properties = type.GetProperties(); + PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (properties.Length != ExpectedPropertyCount) Assert.True(false, $"Number of properties has changed from {ExpectedPropertyCount} to: " + properties.Length + ", adjust tests"); @@ -158,50 +157,104 @@ public void Publics() [Fact] public void GetSets() { + CompareContext compareContext = new CompareContext("GetSets"); + TokenValidationParameters validationParameters = new TokenValidationParameters(); Type type = typeof(TokenValidationParameters); - PropertyInfo[] properties = type.GetProperties(); + PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (properties.Length != ExpectedPropertyCount) - Assert.True(false, $"Number of public fields has changed from {ExpectedPropertyCount} to: " + properties.Length + ", adjust tests"); + compareContext.AddDiff($"Number of fields has changed from {ExpectedPropertyCount} to: " + properties.Length + ", adjust tests"); + + List delegates = new List(); - GetSetContext context = - new GetSetContext + // ensure all delegates are null + foreach (PropertyInfo property in properties) + if (IsPropertyADelegate(property)) { - PropertyNamesAndSetGetValue = new List>> - { - new KeyValuePair>("ActorValidationParameters", new List{(TokenValidationParameters)null, new TokenValidationParameters(), new TokenValidationParameters()}), - new KeyValuePair>("AuthenticationType", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("ClockSkew", new List{TokenValidationParameters.DefaultClockSkew, TimeSpan.FromHours(2), TimeSpan.FromMinutes(1)}), - new KeyValuePair>("IgnoreTrailingSlashWhenValidatingAudience", new List{true, false, true}), - new KeyValuePair>("IssuerSigningKey", new List{(SecurityKey)null, KeyingMaterial.DefaultX509Key_2048, KeyingMaterial.RsaSecurityKey_2048}), - new KeyValuePair>("IssuerSigningKeys", new List{(IEnumerable)null, new List{KeyingMaterial.DefaultX509Key_2048, KeyingMaterial.RsaSecurityKey_1024}, new List()}), - new KeyValuePair>("NameClaimType", new List{ClaimsIdentity.DefaultNameClaimType, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("PropertyBag", new List{(IDictionary)null, new Dictionary {{"CustomKey", "CustomValue"}}, new Dictionary()}), - new KeyValuePair>("RoleClaimType", new List{ClaimsIdentity.DefaultRoleClaimType, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("RequireExpirationTime", new List{true, false, true}), - new KeyValuePair>("RequireSignedTokens", new List{true, false, true}), - new KeyValuePair>("SaveSigninToken", new List{false, true, false}), - new KeyValuePair>("ValidateActor", new List{false, true, false}), - new KeyValuePair>("ValidateAudience", new List{true, false, true}), - new KeyValuePair>("ValidateIssuer", new List{true, false, true}), - new KeyValuePair>("ValidateLifetime", new List{true, false, true}), - new KeyValuePair>("ValidateTokenReplay", new List{false, true, false}), - new KeyValuePair>("ValidIssuer", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("ConfigurationManager", new List{(BaseConfigurationManager)null, new ConfigurationManager("http://someaddress.com", new OpenIdConnectConfigurationRetriever()), new ConfigurationManager("http://someaddress.com", new WsFederationConfigurationRetriever()) }), - }, - Object = validationParameters, - }; + delegates.Add(property.Name); + if (!IsDelegateIsNull(property, validationParameters)) + compareContext.AddDiff($"delegate: '{property.Name}' was not null."); + } + + // check that CreateTokenValidationParameters set all delegates + validationParameters = CreateTokenValidationParameters(); + foreach (PropertyInfo property in properties) + if (IsPropertyADelegate(property)) + { + if (IsDelegateIsNull(property, validationParameters)) + compareContext.AddDiff($"delegate: '{property.Name}' was null."); + } + + // The first value in the List checks the default, the following values will be set and then checked. + validationParameters = new TokenValidationParameters(); + GetSetContext context = new GetSetContext + { + PropertyNamesAndSetGetValue = new List>> + { + new KeyValuePair>("ActorValidationParameters", new List{(TokenValidationParameters)null, new TokenValidationParameters(), new TokenValidationParameters()}), + new KeyValuePair>("AuthenticationType", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), + new KeyValuePair>("ClockSkew", new List{TokenValidationParameters.DefaultClockSkew, TimeSpan.FromHours(2), TimeSpan.FromMinutes(1)}), + new KeyValuePair>("ConfigurationManager", new List{(BaseConfigurationManager)null, new ConfigurationManager("http://someaddress.com", new OpenIdConnectConfigurationRetriever()), new ConfigurationManager("http://someaddress.com", new WsFederationConfigurationRetriever()) }), + new KeyValuePair>("CryptoProviderFactory", new List{(CryptoProviderFactory)null, new CryptoProviderFactory(), new CryptoProviderFactory() }), + new KeyValuePair>("DebugId", new List{(string)null, "DebugId", "DebugId" }), + new KeyValuePair>("IgnoreTrailingSlashWhenValidatingAudience", new List{true, false, true}), + new KeyValuePair>("IncludeTokenOnFailedValidation", new List{false, true, true}), + new KeyValuePair>("IsClone", new List{ false, true, true }), + new KeyValuePair>("InstancePropertyBag", new List{ new Dictionary(), new Dictionary(), new Dictionary()}), + new KeyValuePair>("IssuerSigningKey", new List{(SecurityKey)null, KeyingMaterial.DefaultX509Key_2048, KeyingMaterial.RsaSecurityKey_2048}), + new KeyValuePair>("IssuerSigningKeys", new List{(IEnumerable)null, new List{KeyingMaterial.DefaultX509Key_2048, KeyingMaterial.RsaSecurityKey_1024}, new List()}), + new KeyValuePair>("LogTokenId", new List{true, false, true}), + new KeyValuePair>("LogValidationExceptions", new List{true, false, true}), + new KeyValuePair>("NameClaimType", new List{ClaimsIdentity.DefaultNameClaimType, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), + new KeyValuePair>("PropertyBag", new List{(IDictionary)null, new Dictionary {{"CustomKey", "CustomValue"}}, new Dictionary()}), + new KeyValuePair>("RefreshBeforeValidation", new List{false, true, false}), + new KeyValuePair>("RequireAudience", new List{true, false, true}), + new KeyValuePair>("RequireExpirationTime", new List{true, false, true}), + new KeyValuePair>("RequireSignedTokens", new List{true, false, true}), + new KeyValuePair>("RoleClaimType", new List{ClaimsIdentity.DefaultRoleClaimType, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), + new KeyValuePair>("SaveSigninToken", new List{false, true, false}), + new KeyValuePair>("TokenDecryptionKey", new List{(SecurityKey)null, KeyingMaterial.DefaultX509Key_2048, KeyingMaterial.RsaSecurityKey_2048}), + new KeyValuePair>("TokenDecryptionKeys", new List{(IEnumerable)null, new List{KeyingMaterial.DefaultX509Key_2048, KeyingMaterial.RsaSecurityKey_1024}, new List()}), + new KeyValuePair>("TokenReplayCache", new List{(ITokenReplayCache)null, new TokenReplayCache(), new TokenReplayCache()}), + new KeyValuePair>("TryAllIssuerSigningKeys", new List{true, false, true}), + new KeyValuePair>("ValidateActor", new List{false, true, false}), + new KeyValuePair>("ValidAlgorithms", new List{(IEnumerable)null, new List{Guid.NewGuid().ToString()}, new List{Guid.NewGuid().ToString()}}), + new KeyValuePair>("ValidateAudience", new List{true, false, true}), + new KeyValuePair>("ValidateLifetime", new List{true, false, true}), + new KeyValuePair>("ValidateIssuer", new List{true, false, true}), + new KeyValuePair>("ValidateIssuerSigningKey", new List{false, true, false}), + new KeyValuePair>("ValidateSignatureLast", new List{false, true, false }), + new KeyValuePair>("ValidateTokenReplay", new List{false, true, false}), + new KeyValuePair>("ValidateWithLKG", new List{false, true, false}), + new KeyValuePair>("ValidAudience", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), + new KeyValuePair>("ValidAudiences", new List{(IEnumerable)null, new List{Guid.NewGuid().ToString()}, new List{Guid.NewGuid().ToString()}}), + new KeyValuePair>("ValidIssuer", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), + new KeyValuePair>("ValidIssuers", new List{(IEnumerable)null, new List{Guid.NewGuid().ToString()}, new List{Guid.NewGuid().ToString()}}), + new KeyValuePair>("ValidTypes", new List{(IEnumerable)null, new List{Guid.NewGuid().ToString()}, new List{Guid.NewGuid().ToString()}}), + }, + Object = validationParameters, + }; + + // check that we have checked all properties, subract the number of delegates. + if (context.PropertyNamesAndSetGetValue.Count != ExpectedPropertyCount - delegates.Count) + compareContext.AddDiff($"Number of properties being set is: {context.PropertyNamesAndSetGetValue.Count}, number of properties is: {properties.Length - delegates.Count} (#Properties - #Delegates), adjust tests"); TestUtilities.GetSet(context); - TestUtilities.AssertFailIfErrors("TokenValidationParametersTests: GetSets", context.Errors); - Assert.Null(validationParameters.AudienceValidator); - Assert.Null(validationParameters.LifetimeValidator); - Assert.Null(validationParameters.IssuerSigningKeyResolver); - Assert.Null(validationParameters.IssuerValidator); - Assert.Null(validationParameters.TypeValidator); - Assert.Null(validationParameters.ValidAudiences); - Assert.Null(validationParameters.ValidIssuers); - Assert.Null(validationParameters.SignatureValidator); + foreach (string error in context.Errors) + compareContext.AddDiff(error); + + TestUtilities.AssertFailIfErrors(compareContext); + } + + public static bool IsPropertyADelegate(PropertyInfo property) + { + return typeof(Delegate).IsAssignableFrom(property.PropertyType); + } + + public static bool IsDelegateIsNull(PropertyInfo propertyInfo, object obj) + { + var propertyValue = propertyInfo.GetValue(obj, null); + return propertyValue == null; } [Fact] @@ -210,10 +263,8 @@ public void Clone() object obj = new object(); var compareContext = new CompareContext(); - TokenValidationParameters validationParameters = new TokenValidationParameters(); - validationParameters.PropertyBag = new Dictionary { { "object", obj } }; - validationParameters.InstancePropertyBag["object"] = obj; - + TokenValidationParameters validationParameters = CreateTokenValidationParameters(); + compareContext.PropertiesToIgnoreWhenComparing.Add(typeof(TokenValidationParameters), new List { "InstancePropertyBag", "IsClone" }); TokenValidationParameters validationParametersClone = validationParameters.Clone(); IdentityComparer.AreEqual(validationParametersClone, validationParameters, compareContext); @@ -229,6 +280,73 @@ public void Clone() TestUtilities.AssertFailIfErrors(compareContext); } + private TokenValidationParameters CreateTokenValidationParameters() + { + TokenValidationParameters validationParameters = new TokenValidationParameters(); + + validationParameters.AlgorithmValidator = ValidationDelegates.AlgorithmValidatorBuilder(true); + validationParameters.AudienceValidator = ValidationDelegates.AudienceValidatorReturnsTrue; + validationParameters.IssuerSigningKeyResolver = ValidationDelegates.IssuerSigningKeyResolver; + validationParameters.IssuerSigningKeyResolverUsingConfiguration = ValidationDelegates.IssuerSigningKeyResolverUsingConfiguration; + validationParameters.IssuerSigningKeyValidator = ValidationDelegates.IssuerSigningKeyValidator; + validationParameters.IssuerSigningKeyValidatorUsingConfiguration = ValidationDelegates.IssuerSigningKeyValidatorUsingConfiguration; + validationParameters.IssuerValidator = ValidationDelegates.IssuerValidatorEcho; + validationParameters.IssuerValidatorAsync = ValidationDelegates.IssuerValidatorAsync; + validationParameters.IssuerValidatorUsingConfiguration = ValidationDelegates.IssuerValidatorUsingConfigEcho; + validationParameters.LifetimeValidator = ValidationDelegates.LifetimeValidatorReturnsTrue; + validationParameters.NameClaimTypeRetriever = ValidationDelegates.NameClaimTypeRetriever; + validationParameters.RoleClaimTypeRetriever = ValidationDelegates.RoleClaimTypeRetriever; + validationParameters.SignatureValidator = ValidationDelegates.SignatureValidatorReturnsJsonWebToken; + validationParameters.SignatureValidatorUsingConfiguration = ValidationDelegates.SignatureValidatorUsingConfigReturnsJsonWebToken; + validationParameters.TokenDecryptionKeyResolver = ValidationDelegates.TokenDecryptionKeyResolver; + validationParameters.TokenReader = ValidationDelegates.TokenReaderReturnsJsonWebToken; + validationParameters.TokenReplayValidator = ValidationDelegates.TokenReplayValidatorReturnsTrue; + validationParameters.TransformBeforeSignatureValidation = ValidationDelegates.TransformBeforeSignatureValidation; + validationParameters.TypeValidator = ValidationDelegates.TypeValidator; + + validationParameters.ActorValidationParameters = new TokenValidationParameters(); + validationParameters.ClockSkew = TimeSpan.FromSeconds(42); + validationParameters.DebugId = Guid.NewGuid().ToString(); + validationParameters.InstancePropertyBag["object"] = new object(); + validationParameters.IssuerSigningKey = KeyingMaterial.DefaultX509Key_2048_Public; + validationParameters.IssuerSigningKeys = + new List + { + KeyingMaterial.DefaultX509Key_2048_Public, + KeyingMaterial.RsaSecurityKey_2048 + }; + + validationParameters.PropertyBag = + new Dictionary + { + { "CustomKey", "CustomValue" } + }; + + validationParameters.ValidAlgorithms = new List { "RSA2048", "RSA1024" }; + validationParameters.ValidAudience = "ValidAudience"; + validationParameters.ValidAudiences = new List { "ValidAudience" }; ; + validationParameters.ValidIssuer = "ValidIssuer"; ; + validationParameters.ValidIssuers = new List { "ValidIssuer" }; + validationParameters.ValidTypes = new List { "ValidType1", "ValidType2", "ValidType3" }; + + // properties - set bools the opposite of the default. + TokenValidationParameters validationParametersDefault = new TokenValidationParameters(); + validationParameters.LogTokenId = !validationParametersDefault.LogTokenId; + validationParameters.LogValidationExceptions = !validationParametersDefault.LogValidationExceptions; + validationParameters.IncludeTokenOnFailedValidation = !validationParametersDefault.IncludeTokenOnFailedValidation; + validationParameters.RefreshBeforeValidation = !validationParametersDefault.RefreshBeforeValidation; + validationParameters.SaveSigninToken = !validationParametersDefault.SaveSigninToken; + validationParameters.ValidateAudience = !validationParametersDefault.ValidateAudience; + validationParameters.ValidateIssuer = !validationParametersDefault.ValidateIssuer; + validationParameters.ValidateIssuerSigningKey = !validationParametersDefault.ValidateIssuerSigningKey; + validationParameters.ValidateLifetime = !validationParametersDefault.ValidateLifetime; + validationParameters.ValidateSignatureLast = !validationParametersDefault.ValidateSignatureLast; + validationParameters.ValidateWithLKG = !validationParametersDefault.ValidateWithLKG; + validationParameters.ValidateTokenReplay = !validationParametersDefault.ValidateTokenReplay; + + return validationParameters; + } + class DerivedTokenValidationParameters : TokenValidationParameters { string _internalString;