diff --git a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs
index 1258652d6d..969dc5fb6e 100644
--- a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs
+++ b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs
@@ -214,8 +214,8 @@ public TokenValidationParameters()
/// Gets or sets a delegate that will be used to validate the audience.
///
///
- /// If set, this delegate will be called to validate the 'audience' instead of normal processing.
- /// If is false, this delegate will not be called.
+ /// If set, this delegate will be called to validate the 'audience', instead of normal processing. This means that no default 'audience' validation will occur.
+ /// Even if is false, this delegate will still be called.
///
public AudienceValidator AudienceValidator { get; set; }
@@ -329,7 +329,8 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken,
/// Gets or sets a delegate for validating the that signed the token.
///
///
- /// If set, this delegate will be called to validate the that signed the token, instead of normal processing.
+ /// If set, this delegate will be called to validate the that signed the token, instead of normal processing. This means that no default validation will occur.
+ /// Even if is false, this delegate will still be called.
///
public IssuerSigningKeyValidator IssuerSigningKeyValidator { get; set; }
@@ -355,8 +356,8 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken,
/// 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 normal processing.
- /// If is false, this delegate will not be called.
+ /// If set, this delegate will be called to validate the 'issuer' of the token, instead of normal processing. This means that no default 'issuer' validation will occur.
+ /// Even if is false, this delegate will still be called.
///
public IssuerValidator IssuerValidator { get; set; }
@@ -364,8 +365,8 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken,
/// Gets or sets a delegate that will be used to validate the lifetime of the token
///
///
- /// If set, this delegate will be called to validate the lifetime of the token, instead of normal processing.
- /// If is false, this delegate will not be called.
+ /// If set, this delegate will be called to validate the lifetime of the token, instead of normal processing. This means that no default lifetime validation will occur.
+ /// Even if is false, this delegate will still be called.
///
public LifetimeValidator LifetimeValidator { get; set; }
@@ -500,8 +501,8 @@ public string RoleClaimType
/// Gets or sets a delegate that will be used to validate the token replay of the token
///
///
- /// If set, this delegate will be called to validate the token replay of the token, instead of normal processing.
- /// If is false, this delegate will not be called.
+ /// If set, this delegate will be called to validate the token replay of the token, instead of normal processing. This means no default token replay validation will occur.
+ /// Even if is false, this delegate will still be called.
///
public TokenReplayValidator TokenReplayValidator { get; set; }
@@ -515,7 +516,9 @@ public string RoleClaimType
/// Gets or sets a boolean to control if the audience will be validated during token validation.
///
/// Validation of the audience, mitigates forwarding attacks. For example, a site that receives a token, could not replay it to another side.
- /// A forwarded token would contain the audience of the original site.
+ /// A forwarded token would contain the audience of the original site.
+ /// This boolean only applies to default audience validation. If is set, it will be called regardless of whether this
+ /// property is true or false.
[DefaultValue(true)]
public bool ValidateAudience { get; set; }
@@ -528,13 +531,19 @@ public string RoleClaimType
/// It is possible that a token issued for the same audience could be from a different tenant. For example an application could accept users from
/// contoso.onmicrosoft.com but not fabrikam.onmicrosoft.com, both valid tenants. A application that accepts tokens from fabrikam could forward them
/// to the application that accepts tokens for contoso.
+ /// This boolean only applies to default issuer validation. If is set, it will be called regardless of whether this
+ /// property is true or false.
///
[DefaultValue(true)]
public bool ValidateIssuer { get; set; }
///
/// Gets or sets a boolean to control if the lifetime will be validated during token validation.
- ///
+ ///
+ ///
+ /// This boolean only applies to default lifetime validation. If is set, it will be called regardless of whether this
+ /// property is true or false.
+ ///
[DefaultValue(true)]
public bool ValidateLifetime { get; set; }
@@ -542,13 +551,20 @@ public string RoleClaimType
/// Gets or sets a boolean that controls if validation of the that signed the securityToken is called.
///
/// It is possible for tokens to contain the public key needed to check the signature. For example, X509Data can be hydrated into an X509Certificate,
- /// which can be used to validate the signature. In these cases it is important to validate the SigningKey that was used to validate the signature.
+ /// which can be used to validate the signature. In these cases it is important to validate the SigningKey that was used to validate the signature.
+ /// This boolean only applies to default signing key validation. If is set, it will be called regardless of whether this
+ /// property is true or false.
+ ///
[DefaultValue(false)]
public bool ValidateIssuerSigningKey { get; set; }
///
/// Gets or sets a boolean to control if the token replay will be validated during token validation.
- ///
+ ///
+ ///
+ /// This boolean only applies to default token replay validation. If is set, it will be called regardless of whether this
+ /// property is true or false.
+ ///
[DefaultValue(false)]
public bool ValidateTokenReplay { get; set; }
diff --git a/src/Microsoft.IdentityModel.Tokens/Validators.cs b/src/Microsoft.IdentityModel.Tokens/Validators.cs
index ffddf5b9d6..59e44c2953 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validators.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validators.cs
@@ -53,12 +53,6 @@ public static void ValidateAudience(IEnumerable audiences, SecurityToken
if (validationParameters == null)
throw LogHelper.LogArgumentNullException(nameof(validationParameters));
- if (!validationParameters.ValidateAudience)
- {
- LogHelper.LogWarning(LogMessages.IDX10233);
- return;
- }
-
if (validationParameters.AudienceValidator != null)
{
if (!validationParameters.AudienceValidator(audiences, securityToken, validationParameters))
@@ -70,6 +64,12 @@ public static void ValidateAudience(IEnumerable audiences, SecurityToken
return;
}
+ if (!validationParameters.ValidateAudience)
+ {
+ LogHelper.LogWarning(LogMessages.IDX10233);
+ return;
+ }
+
if (audiences == null)
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAudienceException(LogMessages.IDX10207) { InvalidAudience = null });
@@ -127,15 +127,15 @@ public static string ValidateIssuer(string issuer, SecurityToken securityToken,
if (validationParameters == null)
throw LogHelper.LogArgumentNullException(nameof(validationParameters));
+ if (validationParameters.IssuerValidator != null)
+ return validationParameters.IssuerValidator(issuer, securityToken, validationParameters);
+
if (!validationParameters.ValidateIssuer)
{
LogHelper.LogInformation(LogMessages.IDX10235);
return issuer;
}
- if (validationParameters.IssuerValidator != null)
- return validationParameters.IssuerValidator(issuer, securityToken, validationParameters);
-
if (string.IsNullOrWhiteSpace(issuer))
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX10211)
{ InvalidIssuer = issuer });
@@ -188,16 +188,16 @@ public static void ValidateIssuerSecurityKey(SecurityKey securityKey, SecurityTo
if (validationParameters == null)
throw LogHelper.LogArgumentNullException(nameof(validationParameters));
- if (!validationParameters.ValidateIssuerSigningKey)
+ if (validationParameters.IssuerSigningKeyValidator != null)
{
- LogHelper.LogInformation(LogMessages.IDX10237);
- return;
+ if (!validationParameters.IssuerSigningKeyValidator(securityKey, securityToken, validationParameters))
+ throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10232, securityKey)) { SigningKey = securityKey });
}
- if (validationParameters.IssuerSigningKeyValidator != null)
+ if (!validationParameters.ValidateIssuerSigningKey)
{
- if (!validationParameters.IssuerSigningKeyValidator(securityKey, securityToken, validationParameters))
- throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10232, securityKey)){ SigningKey = securityKey });
+ LogHelper.LogInformation(LogMessages.IDX10237);
+ return;
}
if (!validationParameters.RequireSignedTokens && securityKey == null)
@@ -250,12 +250,6 @@ public static void ValidateLifetime(DateTime? notBefore, DateTime? expires, Secu
if (validationParameters == null)
throw LogHelper.LogArgumentNullException(nameof(validationParameters));
- if (!validationParameters.ValidateLifetime)
- {
- LogHelper.LogInformation(LogMessages.IDX10238);
- return;
- }
-
if (validationParameters.LifetimeValidator != null)
{
if (!validationParameters.LifetimeValidator(notBefore, expires, securityToken, validationParameters))
@@ -265,6 +259,12 @@ public static void ValidateLifetime(DateTime? notBefore, DateTime? expires, Secu
return;
}
+ if (!validationParameters.ValidateLifetime)
+ {
+ LogHelper.LogInformation(LogMessages.IDX10238);
+ return;
+ }
+
if (!expires.HasValue && validationParameters.RequireExpirationTime)
throw LogHelper.LogExceptionMessage(new SecurityTokenNoExpirationException(LogHelper.FormatInvariant(LogMessages.IDX10225, securityToken == null ? "null" : securityToken.GetType().ToString())));
@@ -304,16 +304,16 @@ public static void ValidateTokenReplay(DateTime? expirationTime, string security
if (validationParameters == null)
throw LogHelper.LogArgumentNullException(nameof(validationParameters));
- if (!validationParameters.ValidateTokenReplay)
+ if (validationParameters.TokenReplayValidator != null)
{
- LogHelper.LogInformation(LogMessages.IDX10246);
+ if (!validationParameters.TokenReplayValidator(expirationTime, securityToken, validationParameters))
+ throw LogHelper.LogExceptionMessage(new SecurityTokenReplayDetectedException(LogHelper.FormatInvariant(LogMessages.IDX10228, securityToken)));
return;
}
- if (validationParameters.TokenReplayValidator != null)
+ if (!validationParameters.ValidateTokenReplay)
{
- if (!validationParameters.TokenReplayValidator(expirationTime, securityToken, validationParameters))
- throw LogHelper.LogExceptionMessage(new SecurityTokenReplayDetectedException(LogHelper.FormatInvariant(LogMessages.IDX10228, securityToken)));
+ LogHelper.LogInformation(LogMessages.IDX10246);
return;
}
diff --git a/test/Microsoft.IdentityModel.TestUtils/ReferenceTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/ReferenceTheoryData.cs
index f2c0fa09a6..8b007dbb1b 100644
--- a/test/Microsoft.IdentityModel.TestUtils/ReferenceTheoryData.cs
+++ b/test/Microsoft.IdentityModel.TestUtils/ReferenceTheoryData.cs
@@ -26,12 +26,14 @@ public static TheoryData TokenReplayValidationTheoryData
new TokenReplayTheoryData
{
TestId = $"ValidateTokenReplay: false, {nameof(ValidationDelegates.TokenReplayValidatorReturnsFalse)}",
- TokenReplayValidator = ValidationDelegates.TokenReplayValidatorReturnsFalse
+ TokenReplayValidator = ValidationDelegates.TokenReplayValidatorReturnsFalse,
+ ExpectedException = ExpectedException.SecurityTokenReplayDetected("IDX10228:")
},
new TokenReplayTheoryData
{
TestId = $"ValidateTokenReplay: false, {nameof(ValidationDelegates.TokenReplayValidatorThrows)}",
- TokenReplayValidator = ValidationDelegates.TokenReplayValidatorThrows
+ TokenReplayValidator = ValidationDelegates.TokenReplayValidatorThrows,
+ ExpectedException = ExpectedException.SecurityTokenReplayDetected("TokenReplayValidatorThrows")
},
new TokenReplayTheoryData
{
diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.cs
index 2e8d80f772..ea6da1c64a 100644
--- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.cs
+++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.cs
@@ -431,6 +431,7 @@ public static TheoryData WriteTokenTheoryData
theoryData.Add(new Saml2TheoryData
{
+ ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidSigningKeyException)),
SecurityToken = tokenHandler.CreateToken(tokenDescriptor),
TestId = nameof(ValidationDelegates.IssuerSecurityKeyValidatorThrows) + "-false",
ValidationParameters = validationParameters
@@ -468,6 +469,7 @@ public static TheoryData WriteTokenTheoryData
theoryData.Add(new Saml2TheoryData
{
+ ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidAudienceException)),
SecurityToken = tokenHandler.CreateToken(tokenDescriptor),
TestId = nameof(ValidationDelegates.AudienceValidatorThrows) + "-false",
ValidationParameters = validationParameters
diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs
index df8a76a56e..fbf00288bf 100644
--- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs
+++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs
@@ -873,6 +873,7 @@ public static TheoryData WriteTokenTheoryData
theoryData.Add(new SamlTheoryData
{
+ ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidSigningKeyException)),
SecurityToken = tokenHandler.CreateToken(tokenDescriptor),
TestId = nameof(ValidationDelegates.IssuerSecurityKeyValidatorThrows) + "-false",
ValidationParameters = validationParameters
@@ -910,6 +911,7 @@ public static TheoryData WriteTokenTheoryData
theoryData.Add(new SamlTheoryData
{
+ ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidAudienceException)),
SecurityToken = tokenHandler.CreateToken(tokenDescriptor),
TestId = nameof(ValidationDelegates.AudienceValidatorThrows) + "-false",
ValidationParameters = validationParameters
diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/ValidateTheoryData.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/ValidateTheoryData.cs
index 0282e52e80..a413b10fce 100644
--- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/ValidateTheoryData.cs
+++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/ValidateTheoryData.cs
@@ -106,6 +106,7 @@ public static void AddValidateAudienceTheoryData(List theoryDat
theoryData.Add(new TokenTheoryData
{
Audiences = new List { "John" },
+ ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException(),
TestId = "AudienceValidator throws, validateAudience false",
ValidationParameters = new TokenValidationParameters
{
diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs
index 853c2f4dff..44e794319e 100644
--- a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs
+++ b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs
@@ -982,12 +982,14 @@ public static TheoryData ValidateAudienceTheoryData
},
new JwtTheoryData
{
+ ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidAudienceException), substringExpected: $"{typeof(ValidationDelegates)}.AudienceValidatorThrows"),
TestId = "'validateAudience == false, AudienceValidator throws'",
SecurityToken = tokenHandler.CreateJwtSecurityToken(issuer: Default.Issuer, audience: Default.Audience),
ValidationParameters = ValidateAudienceValidationParameters(null, null, ValidationDelegates.AudienceValidatorThrows, false),
},
new JwtTheoryData
{
+ ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidAudienceException), substringExpected: "IDX10231", propertiesExpected: new Dictionary{ { "InvalidAudience", Default.Audience } }),
TestId = "'validateAudience == false, AudienceValidator return false'",
SecurityToken = tokenHandler.CreateJwtSecurityToken(issuer: Default.Issuer, audience: Default.Audience),
ValidationParameters = ValidateAudienceValidationParameters(null, null, ValidationDelegates.AudienceValidatorReturnsFalse, false),
@@ -1077,7 +1079,8 @@ public static TheoryData ValidateIssuerTheoryData
ValidationParameters = ValidateIssuerValidationParameters(null, Default.Issuers, null, true)
},
new JwtTheoryData
- {
+ {
+ ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidIssuerException), "IssuerValidatorThrows"),
TestId = "ValidationDelegates.IssuerValidatorThrows, ValidateIssuer: false",
Token = jwt,
ValidationParameters = ValidateIssuerValidationParameters(
@@ -1181,19 +1184,21 @@ public static TheoryData ValidateLifetimeTheoryData
},
new JwtTheoryData
{
+ ExpectedException = ExpectedException.SecurityTokenInvalidLifetimeException("IDX10230:"),
TestId = $"{nameof(ValidationDelegates.LifetimeValidatorReturnsFalse)}, ValidateLifetime: false",
Token = Default.UnsignedJwt,
ValidationParameters = ValidateLifetimeValidationParameters(ValidationDelegates.LifetimeValidatorReturnsFalse, false)
},
new JwtTheoryData
{
- ExpectedException = ExpectedException.SecurityTokenInvalidLifetimeException("IDX10230:"),
+ ExpectedException = ExpectedException.SecurityTokenInvalidLifetimeException("LifetimeValidatorThrows"),
TestId = nameof(ValidationDelegates.LifetimeValidatorThrows),
Token = Default.UnsignedJwt,
- ValidationParameters = ValidateLifetimeValidationParameters(ValidationDelegates.LifetimeValidatorReturnsFalse, true)
+ ValidationParameters = ValidateLifetimeValidationParameters(ValidationDelegates.LifetimeValidatorThrows, true)
},
new JwtTheoryData
- {
+ {
+ ExpectedException = ExpectedException.SecurityTokenInvalidLifetimeException("LifetimeValidatorThrows"),
TestId = $"'{nameof(ValidationDelegates.LifetimeValidatorThrows)}, ValidateLifetime: false'",
Token = Default.UnsignedJwt,
ValidationParameters = ValidateLifetimeValidationParameters(ValidationDelegates.LifetimeValidatorThrows, false)