diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateSignature.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateSignature.cs
index ff0a9637a3..1c9e490f7a 100644
--- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateSignature.cs
+++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateSignature.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.IdentityModel.Logging;
@@ -23,7 +22,7 @@ public partial class JsonWebTokenHandler : TokenHandler
/// The parameters used for validation.
/// The optional configuration used for validation.
/// The context in which the method is called.
- /// Returned if or is null."
+ /// Returned if or is null."
/// Returned by the default implementation if the token is not signed, or if the validation fails.
/// Returned if the algorithm is not supported by the key.
/// Returned if the key cannot be resolved.
@@ -34,22 +33,47 @@ internal static ValidationResult ValidateSignature(
CallContext callContext)
{
if (jwtToken is null)
- return ValidationError.NullParameter(
+ return SignatureValidationError.NullParameter(
nameof(jwtToken),
- new StackFrame(true));
+ ValidationError.GetCurrentStackFrame());
if (validationParameters is null)
- return ValidationError.NullParameter(
+ return SignatureValidationError.NullParameter(
nameof(validationParameters),
- new StackFrame(true));
+ ValidationError.GetCurrentStackFrame());
// Delegate is set by the user, we call it and return the result.
if (validationParameters.SignatureValidator is not null)
- return validationParameters.SignatureValidator(jwtToken, validationParameters, configuration, callContext);
+ {
+ try
+ {
+ ValidationResult signatureValidationResult = validationParameters.SignatureValidator(
+ jwtToken,
+ validationParameters,
+ configuration,
+ callContext);
+
+ if (!signatureValidationResult.IsValid)
+ return signatureValidationResult.UnwrapError().AddCurrentStackFrame();
+
+ return signatureValidationResult;
+ }
+#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 SignatureValidationError(
+ new MessageDetail(TokenLogMessages.IDX10272),
+ ValidationFailureType.SignatureValidatorThrew,
+ typeof(SecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame(),
+ innerException: ex);
+ }
+ }
// If the user wants to accept unsigned tokens, they must implement the delegate.
if (!jwtToken.IsSigned)
- return new ValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10504,
LogHelper.MarkAsSecurityArtifact(
@@ -57,7 +81,7 @@ internal static ValidationResult ValidateSignature(
JwtTokenUtilities.SafeLogJwtToken)),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenInvalidSignatureException),
- new StackFrame(true));
+ ValidationError.GetCurrentStackFrame());
SecurityKey? key = null;
if (validationParameters.IssuerSigningKeyResolver is not null)
@@ -93,8 +117,7 @@ internal static ValidationResult ValidateSignature(
{
if (!string.IsNullOrEmpty(jwtToken.Kid))
{
- StackFrame kidNotMatchedNoTryAllStackFrame = StackFrames.KidNotMatchedNoTryAll ??= new StackFrame(true);
- return new ValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10502,
LogHelper.MarkAsNonPII(jwtToken.Kid),
@@ -103,15 +126,14 @@ internal static ValidationResult ValidateSignature(
LogHelper.MarkAsSecurityArtifact(jwtToken.EncodedToken, JwtTokenUtilities.SafeLogJwtToken)),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenSignatureKeyNotFoundException),
- kidNotMatchedNoTryAllStackFrame);
+ ValidationError.GetCurrentStackFrame());
}
- StackFrame noKeysProvidedStackFrame = StackFrames.NoKeysProvided ??= new StackFrame(true);
- return new ValidationError(
+ return new SignatureValidationError(
new MessageDetail(TokenLogMessages.IDX10500),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenSignatureKeyNotFoundException),
- noKeysProvidedStackFrame);
+ ValidationError.GetCurrentStackFrame());
}
}
@@ -144,11 +166,11 @@ private static ValidationResult ValidateSignatureUsingAllKeys(
return unwrappedVpResult;
if (vpFailedResult is null && configFailedResult is null) // No keys were attempted
- return new ValidationError(
+ return new SignatureValidationError(
new MessageDetail(TokenLogMessages.IDX10500),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenSignatureKeyNotFoundException),
- new StackFrame(true));
+ ValidationError.GetCurrentStackFrame());
StringBuilder exceptionStrings = new();
StringBuilder keysAttempted = new();
@@ -223,61 +245,63 @@ private static ValidationResult ValidateSignatureWithKey(
CryptoProviderFactory cryptoProviderFactory = validationParameters.CryptoProviderFactory ?? key.CryptoProviderFactory;
if (!cryptoProviderFactory.IsSupportedAlgorithm(jsonWebToken.Alg, key))
{
- return new ValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10400,
LogHelper.MarkAsNonPII(jsonWebToken.Alg),
key),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenInvalidAlgorithmException),
- new StackFrame(true));
+ ValidationError.GetCurrentStackFrame());
}
- ValidationResult result = validationParameters.AlgorithmValidator(
- jsonWebToken.Alg,
- key,
- jsonWebToken,
- validationParameters,
- callContext);
-
- if (!result.IsValid)
+ try
{
- if (result.UnwrapError() is AlgorithmValidationError algorithmValidationError)
- {
- return new AlgorithmValidationError(
- new MessageDetail(
- TokenLogMessages.IDX10518,
- algorithmValidationError.MessageDetail.Message),
- ValidationFailureType.AlgorithmValidationFailed,
- typeof(SecurityTokenInvalidAlgorithmException),
- new StackFrame(true),
- algorithmValidationError.InvalidAlgorithm);
- }
- else
+ ValidationResult algorithmValidationResult = validationParameters.AlgorithmValidator(
+ jsonWebToken.Alg,
+ key,
+ jsonWebToken,
+ validationParameters,
+ callContext);
+
+ if (!algorithmValidationResult.IsValid)
{
- // overridden delegate did not return an AlgorithmValidationError
- return new ValidationError(
+ var validationError = algorithmValidationResult.UnwrapError().AddCurrentStackFrame();
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10518,
- result.UnwrapError().MessageDetail.Message),
- ValidationFailureType.SignatureAlgorithmValidationFailed,
- typeof(SecurityTokenInvalidAlgorithmException),
- new StackFrame(true));
+ validationError.MessageDetail.Message),
+ validationError.FailureType, // Surface the algorithm validation error's failure type.
+ typeof(SecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame(),
+ validationError); // Pass the algorithm validation error as the inner validation error.
}
}
+#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 SignatureValidationError(
+ new MessageDetail(TokenLogMessages.IDX10273),
+ ValidationFailureType.AlgorithmValidatorThrew,
+ typeof(SecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame(),
+ null, // No need to create an AlgorithmValidationError for this case.
+ ex);
+ }
SignatureProvider signatureProvider = cryptoProviderFactory.CreateForVerifying(key, jsonWebToken.Alg);
try
{
if (signatureProvider == null)
- return new ValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10636,
key?.ToString() ?? "Null",
LogHelper.MarkAsNonPII(jsonWebToken.Alg)),
ValidationFailureType.SignatureValidationFailed,
- typeof(InvalidOperationException),
- new StackFrame(true));
+ typeof(SecurityTokenInvalidOperationException),
+ ValidationError.GetCurrentStackFrame());
bool valid = EncodingUtils.PerformEncodingDependentOperation(
jsonWebToken.EncodedToken,
@@ -292,7 +316,7 @@ private static ValidationResult ValidateSignatureWithKey(
if (valid)
return key;
else
- return new ValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10504,
LogHelper.MarkAsSecurityArtifact(
@@ -300,13 +324,13 @@ private static ValidationResult ValidateSignatureWithKey(
JwtTokenUtilities.SafeLogJwtToken)),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenInvalidSignatureException),
- new StackFrame(true));
+ ValidationError.GetCurrentStackFrame());
}
#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 ValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10504,
LogHelper.MarkAsSecurityArtifact(
@@ -314,8 +338,8 @@ private static ValidationResult ValidateSignatureWithKey(
JwtTokenUtilities.SafeLogJwtToken)),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenInvalidSignatureException),
- new StackFrame(true),
- ex);
+ ValidationError.GetCurrentStackFrame(),
+ innerException: ex);
}
finally
{
@@ -323,7 +347,7 @@ private static ValidationResult ValidateSignatureWithKey(
}
}
- private static ValidationError GetSignatureValidationError(
+ private static SignatureValidationError GetSignatureValidationError(
JsonWebToken jwtToken,
ValidationParameters validationParameters,
BaseConfiguration? configuration,
@@ -343,7 +367,7 @@ private static ValidationError GetSignatureValidationError(
JsonWebToken localJwtToken = jwtToken; // avoid closure on non-exceptional path
bool isKidInTVP = keysInTokenValidationParameters.Any(x => x.KeyId.Equals(localJwtToken.Kid));
string keyLocation = isKidInTVP ? "TokenValidationParameters" : "Configuration";
- return new ValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10511,
LogHelper.MarkAsNonPII(keysAttempted.ToString()),
@@ -355,11 +379,11 @@ private static ValidationError GetSignatureValidationError(
LogHelper.MarkAsSecurityArtifact(jwtToken.EncodedToken, JwtTokenUtilities.SafeLogJwtToken)),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenSignatureKeyNotFoundException),
- new StackFrame(true));
+ ValidationError.GetCurrentStackFrame());
}
if (kidExists)
- return new ValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10503,
LogHelper.MarkAsNonPII(jwtToken.Kid),
@@ -370,9 +394,9 @@ private static ValidationError GetSignatureValidationError(
LogHelper.MarkAsSecurityArtifact(jwtToken.EncodedToken, JwtTokenUtilities.SafeLogJwtToken)),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenSignatureKeyNotFoundException),
- new StackFrame(true));
+ ValidationError.GetCurrentStackFrame());
- return new ValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10517, // Kid is missing and no keys match.
LogHelper.MarkAsNonPII(keysAttempted.ToString()),
@@ -382,7 +406,7 @@ private static ValidationError GetSignatureValidationError(
LogHelper.MarkAsSecurityArtifact(jwtToken.EncodedToken, JwtTokenUtilities.SafeLogJwtToken)),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenSignatureKeyNotFoundException),
- new StackFrame(true));
+ ValidationError.GetCurrentStackFrame());
}
private static void PopulateFailedResults(
diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateSignature.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateSignature.cs
index 3691a62159..5681070c45 100644
--- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateSignature.cs
+++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateSignature.cs
@@ -1,9 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
+using System;
using System.Collections.Generic;
using System.Text;
-using Microsoft.IdentityModel.Xml;
using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages;
#nullable enable
@@ -20,25 +20,50 @@ internal static ValidationResult ValidateSignature(
{
if (samlToken is null)
{
- return ValidationError.NullParameter(
+ return SignatureValidationError.NullParameter(
nameof(samlToken),
ValidationError.GetCurrentStackFrame());
}
if (validationParameters is null)
{
- return ValidationError.NullParameter(
+ return SignatureValidationError.NullParameter(
nameof(validationParameters),
ValidationError.GetCurrentStackFrame());
}
// Delegate is set by the user, we call it and return the result.
if (validationParameters.SignatureValidator is not null)
- return validationParameters.SignatureValidator(samlToken, validationParameters, null, callContext);
+ {
+ try
+ {
+ ValidationResult signatureValidationResult = validationParameters.SignatureValidator(
+ samlToken,
+ validationParameters,
+ null, // configuration
+ callContext);
+
+ if (!signatureValidationResult.IsValid)
+ return signatureValidationResult.UnwrapError().AddCurrentStackFrame();
+
+ return signatureValidationResult;
+ }
+#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 SignatureValidationError(
+ new MessageDetail(TokenLogMessages.IDX10272),
+ ValidationFailureType.SignatureValidatorThrew,
+ typeof(SecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame(),
+ innerException: ex);
+ }
+ }
// If the user wants to accept unsigned tokens, they must implement the delegate
if (samlToken.Assertion.Signature is null)
- return new XmlValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10504,
samlToken.Assertion.CanonicalString),
@@ -64,16 +89,15 @@ internal static ValidationResult ValidateSignature(
resolvedKey = SamlTokenUtilities.ResolveTokenSigningKey(samlToken.Assertion.Signature.KeyInfo, validationParameters);
}
- ValidationError? error = null;
-
if (resolvedKey is not null)
{
keyMatched = true;
var result = ValidateSignatureUsingKey(resolvedKey, samlToken, validationParameters, callContext);
- if (result.IsValid)
- return result;
- error = result.UnwrapError();
+ if (!result.IsValid)
+ return result.UnwrapError().AddCurrentStackFrame();
+
+ return result;
}
bool canMatchKey = samlToken.Assertion.Signature.KeyInfo != null;
@@ -103,12 +127,12 @@ internal static ValidationResult ValidateSignature(
}
if (canMatchKey && keyMatched)
- return new XmlValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10514,
keysAttempted?.ToString(),
samlToken.Assertion.Signature.KeyInfo,
- GetErrorString(error, errors),
+ GetErrorString(errors),
samlToken),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenInvalidSignatureException),
@@ -121,17 +145,17 @@ internal static ValidationResult ValidateSignature(
keysAttemptedString = keysAttempted!.ToString();
if (keysAttemptedString is not null)
- return new XmlValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10512,
keysAttemptedString,
- GetErrorString(error, errors),
+ GetErrorString(errors),
samlToken),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenSignatureKeyNotFoundException),
ValidationError.GetCurrentStackFrame());
- return new XmlValidationError(
+ return new SignatureValidationError(
new MessageDetail(TokenLogMessages.IDX10500),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenSignatureKeyNotFoundException),
@@ -140,44 +164,61 @@ internal static ValidationResult ValidateSignature(
private static ValidationResult ValidateSignatureUsingKey(SecurityKey key, SamlSecurityToken samlToken, ValidationParameters validationParameters, CallContext callContext)
{
- ValidationResult algorithmValidationResult = validationParameters.AlgorithmValidator(
- samlToken.Assertion.Signature.SignedInfo.SignatureMethod,
- key,
- samlToken,
- validationParameters,
- callContext);
-
- if (!algorithmValidationResult.IsValid)
+ try
{
- return algorithmValidationResult.UnwrapError().AddCurrentStackFrame();
+ ValidationResult algorithmValidationResult = validationParameters.AlgorithmValidator(
+ samlToken.Assertion.Signature.SignedInfo.SignatureMethod,
+ key,
+ samlToken,
+ validationParameters,
+ callContext);
+
+ if (!algorithmValidationResult.IsValid)
+ {
+ var algorithmValidationError = algorithmValidationResult.UnwrapError().AddCurrentStackFrame();
+ return new SignatureValidationError(
+ new MessageDetail(
+ TokenLogMessages.IDX10518,
+ algorithmValidationError.MessageDetail.Message),
+ algorithmValidationError.FailureType, // Surface the algorithm validation error's failure type.
+ typeof(SecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame(),
+ algorithmValidationError); // Pass the algorithm validation error as the inner validation error.
+ }
}
- else
+#pragma warning disable CA1031 // Do not catch general exception types
+ catch (Exception ex)
+#pragma warning restore CA1031 // Do not catch general exception types
{
- var validationError = samlToken.Assertion.Signature.Verify(
+ return new SignatureValidationError(
+ new MessageDetail(TokenLogMessages.IDX10273),
+ ValidationFailureType.AlgorithmValidatorThrew,
+ typeof(SecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame(),
+ null, // No need to create an AlgorithmValidationError for this case.
+ ex);
+ }
+
+ var validationError = samlToken.Assertion.Signature.Verify(
key,
validationParameters.CryptoProviderFactory ?? key.CryptoProviderFactory,
callContext);
- if (validationError is null)
- {
- samlToken.SigningKey = key;
+ if (validationError is null)
+ {
+ samlToken.SigningKey = key;
- return key;
- }
- else
- {
- return validationError.AddCurrentStackFrame();
- }
+ return key;
+ }
+ else
+ {
+ return validationError.AddCurrentStackFrame();
}
}
- private static string GetErrorString(ValidationError? error, List? errorList)
+ private static string GetErrorString(List? errorList)
{
// This method is called if there are errors in the signature validation process.
- // This check is there to account for the optional parameter.
- if (error is not null)
- return error.MessageDetail.Message;
-
if (errorList is null)
return string.Empty;
diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateSignature.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateSignature.cs
index 74d2884b26..7aeb16093e 100644
--- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateSignature.cs
+++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateSignature.cs
@@ -1,10 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
+using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.IdentityModel.Tokens.Saml;
-using Microsoft.IdentityModel.Xml;
using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages;
#nullable enable
@@ -19,25 +19,50 @@ internal static ValidationResult ValidateSignature(
{
if (samlToken is null)
{
- return ValidationError.NullParameter(
+ return SignatureValidationError.NullParameter(
nameof(samlToken),
ValidationError.GetCurrentStackFrame());
}
if (validationParameters is null)
{
- return ValidationError.NullParameter(
+ return SignatureValidationError.NullParameter(
nameof(validationParameters),
ValidationError.GetCurrentStackFrame());
}
// Delegate is set by the user, we call it and return the result.
if (validationParameters.SignatureValidator is not null)
- return validationParameters.SignatureValidator(samlToken, validationParameters, null, callContext);
+ {
+ try
+ {
+ ValidationResult signatureValidationResult = validationParameters.SignatureValidator(
+ samlToken,
+ validationParameters,
+ null, // configuration
+ callContext);
+
+ if (!signatureValidationResult.IsValid)
+ return signatureValidationResult.UnwrapError().AddCurrentStackFrame();
+
+ return signatureValidationResult;
+ }
+#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 SignatureValidationError(
+ new MessageDetail(TokenLogMessages.IDX10272),
+ ValidationFailureType.SignatureValidatorThrew,
+ typeof(SecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame(),
+ innerException: ex);
+ }
+ }
// If the user wants to accept unsigned tokens, they must set validationParameters.SignatureValidator
if (samlToken.Assertion.Signature is null)
- return new XmlValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10504,
samlToken.Assertion.CanonicalString),
@@ -63,16 +88,14 @@ internal static ValidationResult ValidateSignature(
resolvedKey = SamlTokenUtilities.ResolveTokenSigningKey(samlToken.Assertion.Signature.KeyInfo, validationParameters);
}
- ValidationError? error = null;
-
if (resolvedKey is not null)
{
keyMatched = true;
var result = ValidateSignatureUsingKey(resolvedKey, samlToken, validationParameters, callContext);
- if (result.IsValid)
- return result;
+ if (!result.IsValid)
+ return result.UnwrapError().AddCurrentStackFrame();
- error = result.UnwrapError();
+ return result;
}
bool canMatchKey = samlToken.Assertion.Signature.KeyInfo != null;
@@ -102,12 +125,12 @@ internal static ValidationResult ValidateSignature(
}
if (canMatchKey && keyMatched)
- return new XmlValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10514,
keysAttempted?.ToString(),
samlToken.Assertion.Signature.KeyInfo,
- GetErrorStrings(error, errors),
+ GetErrorStrings(errors),
samlToken),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenInvalidSignatureException),
@@ -120,17 +143,17 @@ internal static ValidationResult ValidateSignature(
keysAttemptedString = keysAttempted!.ToString();
if (keysAttemptedString is not null)
- return new XmlValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10512,
keysAttemptedString,
- GetErrorStrings(error, errors),
+ GetErrorStrings(errors),
samlToken),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenSignatureKeyNotFoundException),
ValidationError.GetCurrentStackFrame());
- return new XmlValidationError(
+ return new SignatureValidationError(
new MessageDetail(TokenLogMessages.IDX10500),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenSignatureKeyNotFoundException),
@@ -139,43 +162,61 @@ internal static ValidationResult ValidateSignature(
private static ValidationResult ValidateSignatureUsingKey(SecurityKey key, Saml2SecurityToken samlToken, ValidationParameters validationParameters, CallContext callContext)
{
- ValidationResult algorithmValidationResult = validationParameters.AlgorithmValidator(
- samlToken.Assertion.Signature.SignedInfo.SignatureMethod,
- key,
- samlToken,
- validationParameters,
- callContext);
-
- if (!algorithmValidationResult.IsValid)
+ try
{
- return algorithmValidationResult.UnwrapError().AddCurrentStackFrame();
- }
- else
- {
- var validationError = samlToken.Assertion.Signature.Verify(
+ ValidationResult algorithmValidationResult = validationParameters.AlgorithmValidator(
+ samlToken.Assertion.Signature.SignedInfo.SignatureMethod,
key,
- validationParameters.CryptoProviderFactory ?? key.CryptoProviderFactory,
+ samlToken,
+ validationParameters,
callContext);
- if (validationError is null)
+ if (!algorithmValidationResult.IsValid)
{
- samlToken.SigningKey = key;
-
- return key;
- }
- else
- {
- return validationError.AddCurrentStackFrame();
+ var algorithmValidationError = algorithmValidationResult.UnwrapError().AddCurrentStackFrame();
+ return new SignatureValidationError(
+ new MessageDetail(
+ TokenLogMessages.IDX10518,
+ algorithmValidationError.MessageDetail.Message),
+ algorithmValidationError.FailureType, // Surface the algorithm validation error's failure type.
+ typeof(SecurityTokenInvalidSignatureException),
+ SignatureValidationError.GetCurrentStackFrame(),
+ algorithmValidationError); // Pass the algorithm validation error as the inner validation error.
}
}
+#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 SignatureValidationError(
+ new MessageDetail(TokenLogMessages.IDX10273),
+ ValidationFailureType.AlgorithmValidatorThrew,
+ typeof(SecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame(),
+ null, // No need to create an AlgorithmValidationError for this case.
+ ex);
+ }
+
+ var validationError = samlToken.Assertion.Signature.Verify(
+ key,
+ validationParameters.CryptoProviderFactory ?? key.CryptoProviderFactory,
+ callContext);
+
+ if (validationError is null)
+ {
+ samlToken.SigningKey = key;
+
+ return key;
+ }
+ else
+ {
+ return validationError.AddCurrentStackFrame();
+ }
}
- private static string GetErrorStrings(ValidationError? error, List? errors)
+ private static string GetErrorStrings(List? errors)
{
// This method is called if there are errors in the signature validation process.
- // This check is there to account for the optional parameter.
- if (error is not null)
- return error.MessageDetail.Message;
if (errors is null)
return string.Empty;
diff --git a/src/Microsoft.IdentityModel.Tokens/Delegates.cs b/src/Microsoft.IdentityModel.Tokens/Delegates.cs
index e116fc4662..876d267345 100644
--- a/src/Microsoft.IdentityModel.Tokens/Delegates.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Delegates.cs
@@ -204,6 +204,10 @@ namespace Microsoft.IdentityModel.Tokens
/// The to be used for logging.
/// This method is not expected to throw.
/// The validated .
- internal delegate ValidationResult SignatureValidationDelegate(SecurityToken token, ValidationParameters validationParameters, BaseConfiguration? configuration, CallContext? callContext);
+ internal delegate ValidationResult SignatureValidationDelegate(
+ SecurityToken token,
+ ValidationParameters validationParameters,
+ BaseConfiguration? configuration,
+ CallContext callContext);
#nullable restore
}
diff --git a/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenInvalidOperationException.cs b/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenInvalidOperationException.cs
new file mode 100644
index 0000000000..61cf94c224
--- /dev/null
+++ b/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenInvalidOperationException.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Runtime.Serialization;
+
+namespace Microsoft.IdentityModel.Tokens
+{
+ ///
+ /// Throw this exception when a method call is invalid for the object's current state.
+ ///
+ [Serializable]
+ internal class SecurityTokenInvalidOperationException : InvalidOperationException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SecurityTokenInvalidOperationException() : base() { }
+
+ ///
+ /// Initializes a new instance of the class with a specified error message.
+ ///
+ /// The error message that explains the reason for the exception.
+ public SecurityTokenInvalidOperationException(string message) : base(message) { }
+
+ ///
+ /// Initializes a new instance of the class with a specified error message
+ /// and a reference to the inner exception that is the cause of this exception.
+ ///
+ /// The error message that explains the reason for the exception.
+ /// The that is the cause of the current exception, or a null reference if no inner exception is specified.
+ public SecurityTokenInvalidOperationException(string message, Exception innerException) : base(message, innerException) { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// the that holds the serialized object data.
+ /// The contextual information about the source or destination.
+ protected SecurityTokenInvalidOperationException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt
index 15dcd06514..cbfa1e30c6 100644
--- a/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt
+++ b/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt
@@ -1,20 +1,18 @@
const Microsoft.IdentityModel.Tokens.LogMessages.IDX10002 = "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'." -> string
const Microsoft.IdentityModel.Tokens.LogMessages.IDX10268 = "IDX10268: Unable to validate audience, validationParameters.ValidAudiences.Count == 0." -> string
const Microsoft.IdentityModel.Tokens.LogMessages.IDX10269 = "IDX10269: IssuerValidationDelegate threw an exception, see inner exception." -> string
+const Microsoft.IdentityModel.Tokens.LogMessages.IDX10272 = "IDX10272: SignatureValidationDelegate threw an exception, see inner exception." -> string
+const Microsoft.IdentityModel.Tokens.LogMessages.IDX10273 = "IDX10273: AlgorithmValidationDelegate threw an exception, see inner exception." -> string
+const Microsoft.IdentityModel.Tokens.LogMessages.IDX10274 = "IDX10274: IssuerSigningKeyValidationDelegate threw an exception, see inner exception." -> string
const Microsoft.IdentityModel.Tokens.LogMessages.IDX10275 = "IDX10275: TokenTypeValidationDelegate threw an exception, see inner exception." -> string
Microsoft.IdentityModel.Tokens.AlgorithmValidationError
Microsoft.IdentityModel.Tokens.AlgorithmValidationError.AlgorithmValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidAlgorithm, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.AlgorithmValidationError.InvalidAlgorithm.get -> string
-Microsoft.IdentityModel.Tokens.AlgorithmValidationError._invalidAlgorithm -> string
Microsoft.IdentityModel.Tokens.AudienceValidationError.AudienceValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Collections.Generic.IList tokenAudiences, System.Collections.Generic.IList validAudiences, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.AudienceValidationError.TokenAudiences.get -> System.Collections.Generic.IList
Microsoft.IdentityModel.Tokens.AudienceValidationError.TokenAudiences.set -> void
Microsoft.IdentityModel.Tokens.AudienceValidationError.ValidAudiences.get -> System.Collections.Generic.IList
Microsoft.IdentityModel.Tokens.AudienceValidationError.ValidAudiences.set -> void
-Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError
-Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError.InvalidSigningKey.get -> Microsoft.IdentityModel.Tokens.SecurityKey
-Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError.InvalidSigningKey.set -> void
-Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError.IssuerSigningKeyValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, Microsoft.IdentityModel.Tokens.SecurityKey invalidSigningKey, Microsoft.IdentityModel.Tokens.ValidationFailureType failureType = null, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.IssuerValidationError.InvalidIssuer.get -> string
Microsoft.IdentityModel.Tokens.IssuerValidationError.IssuerValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidIssuer, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.IssuerValidationSource.IssuerMatchedConfiguration = 1 -> Microsoft.IdentityModel.Tokens.IssuerValidationSource
@@ -22,9 +20,17 @@ Microsoft.IdentityModel.Tokens.IssuerValidationSource.IssuerMatchedValidationPar
Microsoft.IdentityModel.Tokens.LifetimeValidationError.Expires.get -> System.DateTime?
Microsoft.IdentityModel.Tokens.LifetimeValidationError.LifetimeValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.DateTime? notBefore, System.DateTime? expires, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.LifetimeValidationError.NotBefore.get -> System.DateTime?
+Microsoft.IdentityModel.Tokens.SecurityTokenInvalidOperationException
+Microsoft.IdentityModel.Tokens.SecurityTokenInvalidOperationException.SecurityTokenInvalidOperationException() -> void
+Microsoft.IdentityModel.Tokens.SecurityTokenInvalidOperationException.SecurityTokenInvalidOperationException(string message) -> void
+Microsoft.IdentityModel.Tokens.SecurityTokenInvalidOperationException.SecurityTokenInvalidOperationException(string message, System.Exception innerException) -> void
+Microsoft.IdentityModel.Tokens.SecurityTokenInvalidOperationException.SecurityTokenInvalidOperationException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) -> void
+Microsoft.IdentityModel.Tokens.SignatureValidationError
+Microsoft.IdentityModel.Tokens.SignatureValidationError.InnerValidationError.get -> Microsoft.IdentityModel.Tokens.ValidationError
+Microsoft.IdentityModel.Tokens.SignatureValidationError.SignatureValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, Microsoft.IdentityModel.Tokens.ValidationError innerValidationError = null, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.TokenTypeValidationError
-Microsoft.IdentityModel.Tokens.TokenTypeValidationError.TokenTypeValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidTokenType, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.TokenTypeValidationError.InvalidTokenType.get -> string
+Microsoft.IdentityModel.Tokens.TokenTypeValidationError.TokenTypeValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidTokenType, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.TokenValidationParameters.TimeProvider.get -> System.TimeProvider
Microsoft.IdentityModel.Tokens.TokenValidationParameters.TimeProvider.set -> void
Microsoft.IdentityModel.Tokens.ValidationError.AddCurrentStackFrame(string filePath = "", int lineNumber = 0, int skipFrames = 1) -> Microsoft.IdentityModel.Tokens.ValidationError
@@ -36,16 +42,19 @@ Microsoft.IdentityModel.Tokens.ValidationResult.Error.get -> Microsoft.
Microsoft.IdentityModel.Tokens.ValidationResult.IsValid.get -> bool
Microsoft.IdentityModel.Tokens.ValidationResult.Result.get -> TResult
override Microsoft.IdentityModel.Tokens.AlgorithmValidationError.GetException() -> System.Exception
-override Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError.GetException() -> System.Exception
+override Microsoft.IdentityModel.Tokens.SignatureValidationError.GetException() -> System.Exception
override Microsoft.IdentityModel.Tokens.TokenTypeValidationError.GetException() -> System.Exception
+static Microsoft.IdentityModel.Tokens.SignatureValidationError.NullParameter(string parameterName, System.Diagnostics.StackFrame stackFrame) -> Microsoft.IdentityModel.Tokens.SignatureValidationError
static Microsoft.IdentityModel.Tokens.TokenTypeValidationError.NullParameter(string parameterName, System.Diagnostics.StackFrame stackFrame) -> Microsoft.IdentityModel.Tokens.TokenTypeValidationError
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 readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.AlgorithmValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType
+static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.IssuerSigningKeyValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.IssuerValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType
-static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.LifetimeValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.NoTokenAudiencesProvided -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.NoValidationParameterAudiencesProvided -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.SignatureAlgorithmValidationFailed -> Microsoft.IdentityModel.Tokens.ValidationFailureType
+static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.SignatureValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.TokenExceedsMaximumSize -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.TokenIsNotSigned -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.TokenTypeValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType
diff --git a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs
index 8e2cecae39..6a203b74e7 100644
--- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs
+++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs
@@ -88,6 +88,8 @@ internal static class LogMessages
public const string IDX10267 = "IDX10267: '{0}' has been called by a derived class '{1}' which has not implemented this method. For this call graph to succeed, '{1}' will need to implement '{0}'.";
public const string IDX10268 = "IDX10268: Unable to validate audience, validationParameters.ValidAudiences.Count == 0.";
public const string IDX10269 = "IDX10269: IssuerValidationDelegate threw an exception, see inner exception.";
+ public const string IDX10272 = "IDX10272: SignatureValidationDelegate threw an exception, see inner exception.";
+ public const string IDX10273 = "IDX10273: AlgorithmValidationDelegate threw an exception, see inner exception.";
public const string IDX10275 = "IDX10275: TokenTypeValidationDelegate threw an exception, see inner exception.";
// 10500 - SignatureValidation
diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AlgorithmValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AlgorithmValidationError.cs
index 940d099f32..de0dd174d5 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AlgorithmValidationError.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AlgorithmValidationError.cs
@@ -9,8 +9,6 @@ namespace Microsoft.IdentityModel.Tokens
{
internal class AlgorithmValidationError : ValidationError
{
- protected string? _invalidAlgorithm;
-
public AlgorithmValidationError(
MessageDetail messageDetail,
ValidationFailureType validationFailureType,
@@ -20,7 +18,7 @@ public AlgorithmValidationError(
Exception? innerException = null) :
base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException)
{
- _invalidAlgorithm = invalidAlgorithm;
+ InvalidAlgorithm = invalidAlgorithm;
}
internal override Exception GetException()
@@ -29,8 +27,9 @@ internal override Exception GetException()
{
SecurityTokenInvalidAlgorithmException exception = new(MessageDetail.Message, InnerException)
{
- InvalidAlgorithm = _invalidAlgorithm
+ InvalidAlgorithm = InvalidAlgorithm
};
+ exception.SetValidationError(this);
return exception;
}
@@ -38,7 +37,7 @@ internal override Exception GetException()
return base.GetException();
}
- internal string? InvalidAlgorithm => _invalidAlgorithm;
+ protected string? InvalidAlgorithm { get; }
}
}
#nullable restore
diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/SignatureValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/SignatureValidationError.cs
new file mode 100644
index 0000000000..78c51069d8
--- /dev/null
+++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/SignatureValidationError.cs
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Diagnostics;
+
+#nullable enable
+namespace Microsoft.IdentityModel.Tokens
+{
+ internal class SignatureValidationError : ValidationError
+ {
+ public SignatureValidationError(
+ MessageDetail messageDetail,
+ ValidationFailureType validationFailureType,
+ Type exceptionType,
+ StackFrame stackFrame,
+ ValidationError? innerValidationError = null,
+ Exception? innerException = null) :
+ base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException)
+ {
+ InnerValidationError = innerValidationError;
+ }
+
+ internal override Exception GetException()
+ {
+ var inner = InnerException ?? InnerValidationError?.GetException();
+
+ if (ExceptionType == typeof(SecurityTokenInvalidSignatureException))
+ {
+ SecurityTokenInvalidSignatureException exception = new(MessageDetail.Message, inner);
+ exception.SetValidationError(this);
+
+ return exception;
+ }
+ else if (ExceptionType == typeof(SecurityTokenSignatureKeyNotFoundException))
+ {
+ SecurityTokenSignatureKeyNotFoundException exception = new(MessageDetail.Message, inner);
+ exception.SetValidationError(this);
+
+ return exception;
+ }
+
+ return base.GetException();
+ }
+
+ internal static new SignatureValidationError NullParameter(
+ string parameterName, StackFrame stackFrame) => new(
+ MessageDetail.NullParameter(parameterName),
+ ValidationFailureType.NullArgument,
+ typeof(SecurityTokenArgumentNullException),
+ stackFrame,
+ null); // innerValidationError
+
+ protected internal ValidationError? InnerValidationError { get; }
+ }
+}
+#nullable restore
diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs
index 8f40ffdadb..5347e72d65 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs
@@ -66,6 +66,8 @@ internal Exception GetException(Type exceptionType, Exception? innerException)
exception = new SecurityTokenInvalidIssuerException(MessageDetail.Message);
else if (exceptionType == typeof(SecurityTokenInvalidLifetimeException))
exception = new SecurityTokenInvalidLifetimeException(MessageDetail.Message);
+ else if (exceptionType == typeof(SecurityTokenInvalidOperationException))
+ exception = new SecurityTokenInvalidOperationException(MessageDetail.Message);
else if (exceptionType == typeof(SecurityTokenReplayDetectedException))
exception = new SecurityTokenReplayDetectedException(MessageDetail.Message);
else if (exceptionType == typeof(SecurityTokenReplayAddFailedException))
@@ -123,6 +125,8 @@ internal Exception GetException(Type exceptionType, Exception? innerException)
exception = new SecurityTokenInvalidIssuerException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenInvalidLifetimeException))
exception = new SecurityTokenInvalidLifetimeException(MessageDetail.Message, innerException);
+ else if (exceptionType == typeof(SecurityTokenInvalidOperationException))
+ exception = new SecurityTokenInvalidOperationException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenReplayDetectedException))
exception = new SecurityTokenReplayDetectedException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenReplayAddFailedException))
diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs
index 1f1dcef7af..bcdab9cfbd 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs
@@ -129,12 +129,22 @@ private class InvalidSecurityTokenFailure : ValidationFailureType { internal Inv
public static readonly ValidationFailureType XmlValidationFailed = new XmlValidationFailure("XmlValidationFailed");
private class XmlValidationFailure : ValidationFailureType { internal XmlValidationFailure(string name) : base(name) { } }
+ ///
+ /// Defines a type that represents the fact that the algorithm validation delegate threw an exception.
+ ///
+ public static readonly ValidationFailureType AlgorithmValidatorThrew = new AlgorithmValidationFailure("AlgorithmValidatorThrew");
+
///
/// Defines a type that represents that a token is invalid.
///
public static readonly ValidationFailureType IssuerValidatorThrew = new IssuerValidatorFailure("IssuerValidatorThrew");
private class IssuerValidatorFailure : ValidationFailureType { internal IssuerValidatorFailure(string name) : base(name) { } }
+ ///
+ /// Defines a type that represents the fact that the signature validation delegate threw an exception.
+ ///
+ public static readonly ValidationFailureType SignatureValidatorThrew = new SignatureValidationFailure("SignatureValidatorThrew");
+
///
/// Defines a type that represents the fact that the token type validation delegate threw an exception.
///
diff --git a/src/Microsoft.IdentityModel.Xml/Exceptions/XmlValidationError.cs b/src/Microsoft.IdentityModel.Xml/Exceptions/XmlValidationError.cs
deleted file mode 100644
index a339d58da7..0000000000
--- a/src/Microsoft.IdentityModel.Xml/Exceptions/XmlValidationError.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-using System;
-using System.Diagnostics;
-using Microsoft.IdentityModel.Tokens;
-
-#nullable enable
-namespace Microsoft.IdentityModel.Xml
-{
- internal class XmlValidationError : ValidationError
- {
- public XmlValidationError(
- MessageDetail messageDetail,
- ValidationFailureType validationFailureType,
- Type exceptionType,
- StackFrame stackFrame,
- Exception? innerException = null) :
- base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException)
- {
-
- }
-
- internal override Exception GetException()
- {
- if (ExceptionType == typeof(XmlValidationException))
- {
- XmlValidationException exception = new(MessageDetail.Message, InnerException);
- exception.SetValidationError(this);
- return exception;
- }
-
- return base.GetException();
- }
- }
-}
-#nullable restore
diff --git a/src/Microsoft.IdentityModel.Xml/InternalAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Xml/InternalAPI.Unshipped.txt
index 59024a50b0..7541dd5624 100644
--- a/src/Microsoft.IdentityModel.Xml/InternalAPI.Unshipped.txt
+++ b/src/Microsoft.IdentityModel.Xml/InternalAPI.Unshipped.txt
@@ -1,6 +1,6 @@
-Microsoft.IdentityModel.Xml.Reference.Verify(Microsoft.IdentityModel.Tokens.CryptoProviderFactory cryptoProviderFactory, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.ValidationError
-Microsoft.IdentityModel.Xml.Signature.Verify(Microsoft.IdentityModel.Tokens.SecurityKey key, Microsoft.IdentityModel.Tokens.CryptoProviderFactory cryptoProviderFactory, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.ValidationError
-Microsoft.IdentityModel.Xml.SignedInfo.Verify(Microsoft.IdentityModel.Tokens.CryptoProviderFactory cryptoProviderFactory, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.ValidationError
+Microsoft.IdentityModel.Xml.Reference.Verify(Microsoft.IdentityModel.Tokens.CryptoProviderFactory cryptoProviderFactory, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.SignatureValidationError
+Microsoft.IdentityModel.Xml.Signature.Verify(Microsoft.IdentityModel.Tokens.SecurityKey key, Microsoft.IdentityModel.Tokens.CryptoProviderFactory cryptoProviderFactory, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.SignatureValidationError
+Microsoft.IdentityModel.Xml.SignedInfo.Verify(Microsoft.IdentityModel.Tokens.CryptoProviderFactory cryptoProviderFactory, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.SignatureValidationError
Microsoft.IdentityModel.Xml.XmlValidationError
Microsoft.IdentityModel.Xml.XmlValidationError.XmlValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Xml.XmlValidationException.SetValidationError(Microsoft.IdentityModel.Tokens.ValidationError validationError) -> void
diff --git a/src/Microsoft.IdentityModel.Xml/Reference.cs b/src/Microsoft.IdentityModel.Xml/Reference.cs
index 2a1f6870af..a2534ef1cf 100644
--- a/src/Microsoft.IdentityModel.Xml/Reference.cs
+++ b/src/Microsoft.IdentityModel.Xml/Reference.cs
@@ -134,23 +134,25 @@ public void Verify(CryptoProviderFactory cryptoProviderFactory)
/// supplies the .
/// contextual information for diagnostics.
/// if is null.
- internal ValidationError? Verify(
+ internal SignatureValidationError? Verify(
CryptoProviderFactory cryptoProviderFactory,
#pragma warning disable CA1801 // Review unused parameters
CallContext callContext)
#pragma warning restore CA1801
{
if (cryptoProviderFactory == null)
- return ValidationError.NullParameter(nameof(cryptoProviderFactory), new System.Diagnostics.StackFrame());
+ return SignatureValidationError.NullParameter(
+ nameof(cryptoProviderFactory),
+ ValidationError.GetCurrentStackFrame());
if (!Utility.AreEqual(ComputeDigest(cryptoProviderFactory), Convert.FromBase64String(DigestValue)))
- return new XmlValidationError(
+ return new SignatureValidationError(
new MessageDetail(
LogMessages.IDX30201,
Uri ?? Id),
ValidationFailureType.XmlValidationFailed,
- typeof(XmlValidationException),
- new System.Diagnostics.StackFrame());
+ typeof(SecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame());
return null;
}
diff --git a/src/Microsoft.IdentityModel.Xml/Signature.cs b/src/Microsoft.IdentityModel.Xml/Signature.cs
index b7bf6219fa..5a0d122beb 100644
--- a/src/Microsoft.IdentityModel.Xml/Signature.cs
+++ b/src/Microsoft.IdentityModel.Xml/Signature.cs
@@ -126,7 +126,7 @@ public void Verify(SecurityKey key, CryptoProviderFactory cryptoProviderFactory)
}
#nullable enable
- internal ValidationError? Verify(
+ internal SignatureValidationError? Verify(
SecurityKey key,
CryptoProviderFactory cryptoProviderFactory,
#pragma warning disable CA1801 // Review unused parameters
@@ -134,34 +134,38 @@ public void Verify(SecurityKey key, CryptoProviderFactory cryptoProviderFactory)
#pragma warning restore CA1801
{
if (key is null)
- return ValidationError.NullParameter(nameof(key), ValidationError.GetCurrentStackFrame());
+ return SignatureValidationError.NullParameter(
+ nameof(key),
+ ValidationError.GetCurrentStackFrame());
if (cryptoProviderFactory is null)
- return ValidationError.NullParameter(nameof(cryptoProviderFactory), ValidationError.GetCurrentStackFrame());
+ return SignatureValidationError.NullParameter(
+ nameof(cryptoProviderFactory),
+ ValidationError.GetCurrentStackFrame());
if (SignedInfo is null)
- return new XmlValidationError(
+ return new SignatureValidationError(
new MessageDetail(LogMessages.IDX30212),
- ValidationFailureType.XmlValidationFailed,
- typeof(XmlValidationException),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(SecurityTokenInvalidSignatureException),
ValidationError.GetCurrentStackFrame());
if (!cryptoProviderFactory.IsSupportedAlgorithm(SignedInfo.SignatureMethod, key))
- return new XmlValidationError(
+ return new SignatureValidationError(
new MessageDetail(LogMessages.IDX30207, SignedInfo.SignatureMethod, cryptoProviderFactory.GetType()),
ValidationFailureType.XmlValidationFailed,
- typeof(XmlValidationException),
+ typeof(SecurityTokenInvalidSignatureException),
ValidationError.GetCurrentStackFrame());
var signatureProvider = cryptoProviderFactory.CreateForVerifying(key, SignedInfo.SignatureMethod);
if (signatureProvider is null)
- return new XmlValidationError(
+ return new SignatureValidationError(
new MessageDetail(LogMessages.IDX30203, cryptoProviderFactory, key, SignedInfo.SignatureMethod),
ValidationFailureType.XmlValidationFailed,
- typeof(XmlValidationException),
+ typeof(SecurityTokenInvalidSignatureException),
ValidationError.GetCurrentStackFrame());
- ValidationError? validationError = null;
+ SignatureValidationError? validationError = null;
try
{
@@ -170,10 +174,10 @@ public void Verify(SecurityKey key, CryptoProviderFactory cryptoProviderFactory)
SignedInfo.GetCanonicalBytes(memoryStream);
if (!signatureProvider.Verify(memoryStream.ToArray(), Convert.FromBase64String(SignatureValue)))
{
- validationError = new XmlValidationError(
+ validationError = new SignatureValidationError(
new MessageDetail(LogMessages.IDX30200, cryptoProviderFactory, key),
ValidationFailureType.XmlValidationFailed,
- typeof(XmlValidationException),
+ typeof(SecurityTokenInvalidSignatureException),
ValidationError.GetCurrentStackFrame());
}
}
diff --git a/src/Microsoft.IdentityModel.Xml/SignedInfo.cs b/src/Microsoft.IdentityModel.Xml/SignedInfo.cs
index 9f16187e56..3ccf96df8e 100644
--- a/src/Microsoft.IdentityModel.Xml/SignedInfo.cs
+++ b/src/Microsoft.IdentityModel.Xml/SignedInfo.cs
@@ -118,16 +118,18 @@ public void Verify(CryptoProviderFactory cryptoProviderFactory)
///
/// supplies any required cryptographic operators.
/// contextual information for diagnostics.
- internal ValidationError? Verify(
+ internal SignatureValidationError? Verify(
CryptoProviderFactory cryptoProviderFactory,
#pragma warning disable CA1801
CallContext callContext)
#pragma warning restore CA1801
{
if (cryptoProviderFactory == null)
- return ValidationError.NullParameter(nameof(cryptoProviderFactory), ValidationError.GetCurrentStackFrame());
+ return SignatureValidationError.NullParameter(
+ nameof(cryptoProviderFactory),
+ ValidationError.GetCurrentStackFrame());
- ValidationError? validationError = null;
+ SignatureValidationError? validationError = null;
for (int i = 0; i < References.Count; i++)
{
diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Algorithm.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Algorithm.cs
new file mode 100644
index 0000000000..fdfc48ed4a
--- /dev/null
+++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Algorithm.cs
@@ -0,0 +1,334 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.IdentityModel.JsonWebTokens.Tests;
+using Microsoft.IdentityModel.Logging;
+using Microsoft.IdentityModel.TestUtils;
+using Microsoft.IdentityModel.Tokens;
+using Xunit;
+
+#nullable enable
+namespace Microsoft.IdentityModel.JsonWebTokens.Extensibility.Tests
+{
+ public partial class JsonWebTokenHandlerValidateTokenAsyncTests
+ {
+ [Theory, MemberData(nameof(Algorithm_ExtensibilityTestCases), DisableDiscoveryEnumeration = true)]
+ public async Task ValidateTokenAsync_AlgorithmValidator_Extensibility(AlgorithmExtensibilityTheoryData theoryData)
+ {
+ var context = TestUtilities.WriteHeader($"{this}.{nameof(ValidateTokenAsync_AlgorithmValidator_Extensibility)}", theoryData);
+ context.IgnoreType = false;
+ for (int i = 0; i < theoryData.ExtraStackFrames; i++)
+ theoryData.ValidationError!.AddStackFrame(new StackFrame(false));
+
+ theoryData.ValidationParameters!.IssuerSigningKeys.Add(theoryData.SigningKey);
+
+ try
+ {
+ ValidationResult validationResult = await theoryData.JsonWebTokenHandler.ValidateTokenAsync(
+ theoryData.JsonWebToken!,
+ theoryData.ValidationParameters!,
+ theoryData.CallContext,
+ CancellationToken.None);
+
+ if (validationResult.IsValid)
+ {
+ // We expect the validation to fail, but it passed
+ context.AddDiff("ValidationResult is Valid.");
+ }
+ else
+ {
+ ValidationError validationError = validationResult.UnwrapError();
+
+ if (validationError is SignatureValidationError signatureValidationError &&
+ signatureValidationError.InnerValidationError is not null)
+ {
+ IdentityComparer.AreValidationErrorsEqual(
+ signatureValidationError.InnerValidationError,
+ theoryData.ValidationError,
+ context);
+ }
+ else
+ {
+ IdentityComparer.AreValidationErrorsEqual(
+ validationError,
+ theoryData.ValidationError,
+ context);
+ }
+
+ var exception = validationError.GetException();
+ theoryData.ExpectedException.ProcessException(exception, context);
+ // Process the inner exception since invalid algorithm exceptions are wrapped inside
+ // invalid signature exceptions
+ if (theoryData.ExpectedInnerException is not null)
+ theoryData.ExpectedInnerException.ProcessException(exception.InnerException, context);
+ }
+ }
+ catch (Exception ex)
+ {
+ // We expect the validation to fail, but it threw an exception
+ context.AddDiff($"ValidateTokenAsync threw exception: {ex}");
+ }
+
+ TestUtilities.AssertFailIfErrors(context);
+ }
+
+ public static TheoryData Algorithm_ExtensibilityTestCases
+ {
+ get
+ {
+ var theoryData = new TheoryData();
+ CallContext callContext = new CallContext();
+ var utcNow = DateTime.UtcNow;
+ var utcPlusOneHour = utcNow + TimeSpan.FromHours(1);
+
+ #region return CustomAlgorithmValidationError
+ // Test cases where delegate is overridden and return a CustomAlgorithmValidationError
+ // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: SecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "CustomAlgorithmValidatorDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorDelegate,
+ extraStackFrames: 1)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(SecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorDelegate)),
+ ValidationError = new CustomAlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(SecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 160),
+ "algorithm")
+ });
+
+ // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: CustomSecurityTokenInvalidAlgorithmException : SecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "CustomAlgorithmValidatorCustomExceptionDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionDelegate,
+ extraStackFrames: 1)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(CustomSecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionDelegate)),
+ ValidationError = new CustomAlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 175),
+ "algorithm"),
+ });
+
+ // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: NotSupportedException : SystemException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "CustomAlgorithmValidatorUnknownExceptionDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorUnknownExceptionDelegate,
+ extraStackFrames: 1)
+ {
+ // CustomAlgorithmValidationError does not handle the exception type 'NotSupportedException'
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenException)),
+ ExpectedInnerException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(NotSupportedException),
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorUnknownExceptionDelegate))),
+ ValidationError = new CustomAlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorUnknownExceptionDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(NotSupportedException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 205),
+ "algorithm"),
+ });
+
+ // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomAudienceValidationFailureType
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate,
+ extraStackFrames: 1)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(CustomSecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate)),
+ ValidationError = new CustomAlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate), null),
+ CustomAlgorithmValidationError.CustomAlgorithmValidationFailureType,
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 190),
+ "algorithm"),
+ });
+ #endregion
+
+ #region return AlgorithmValidationError
+ // Test cases where delegate is overridden and return an AlgorithmValidationError
+ // AlgorithmValidationError : ValidationError, ExceptionType: SecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "AlgorithmValidatorDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.AlgorithmValidatorDelegate,
+ extraStackFrames: 1)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(SecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorDelegate)),
+ ValidationError = new AlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(SecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 235),
+ "algorithm")
+ });
+
+ // AlgorithmValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidAlgorithmException : SecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate,
+ extraStackFrames: 1)
+ {
+ // AlgorithmValidationError does not handle the exception type 'CustomSecurityTokenInvalidAlgorithmException'
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenException)),
+ ExpectedInnerException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate))),
+ ValidationError = new AlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 259),
+ "algorithm")
+ });
+
+ // AlgorithmValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "AlgorithmValidatorCustomExceptionTypeDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomExceptionTypeDelegate,
+ extraStackFrames: 1)
+ {
+ // AlgorithmValidationError does not handle the exception type 'CustomSecurityTokenException'
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenException)),
+ ExpectedInnerException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(CustomSecurityTokenException),
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomExceptionTypeDelegate))),
+ ValidationError = new AlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomExceptionTypeDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(CustomSecurityTokenException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 274),
+ "algorithm")
+ });
+
+ // SignatureValidationError : ValidationError, ExceptionType: SecurityTokenInvalidSignatureException, inner: CustomSecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "AlgorithmValidatorThrows",
+ utcNow,
+ CustomAlgorithmValidationDelegates.AlgorithmValidatorThrows,
+ extraStackFrames: 2)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10273:",
+ typeof(CustomSecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorThrows)),
+ ValidationError = new SignatureValidationError(
+ new MessageDetail(
+ string.Format(Tokens.LogMessages.IDX10273), null),
+ ValidationFailureType.AlgorithmValidatorThrew,
+ typeof(SecurityTokenInvalidSignatureException),
+ new StackFrame("JsonWebTokenHandler.ValidateSignature.cs", 250),
+ null, // no inner validation error
+ new CustomSecurityTokenInvalidAlgorithmException(nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorThrows), null)
+ )
+ });
+ #endregion
+
+ return theoryData;
+ }
+ }
+
+ public class AlgorithmExtensibilityTheoryData : ValidateTokenAsyncBaseTheoryData
+ {
+ internal AlgorithmExtensibilityTheoryData(string testId, DateTime utcNow, AlgorithmValidationDelegate algorithmValidator, int extraStackFrames) : base(testId)
+ {
+ // The token is never read by the custom delegtes, so we create a dummy token
+ JsonWebToken = JsonWebTokenHandler.ReadJsonWebToken(JsonWebTokenHandler.CreateToken(new SecurityTokenDescriptor()
+ {
+ SigningCredentials = Default.SymmetricSigningCredentials,
+ }));
+
+ ValidationParameters = new ValidationParameters
+ {
+ // We leave the default signature validator to call the custom algorithm validator
+ AlgorithmValidator = algorithmValidator,
+ AudienceValidator = SkipValidationDelegates.SkipAudienceValidation,
+ IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation,
+ IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation,
+ LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation,
+ TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation,
+ TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation,
+ };
+
+ ExtraStackFrames = extraStackFrames;
+ }
+
+ public JsonWebToken JsonWebToken { get; }
+
+ public JsonWebTokenHandler JsonWebTokenHandler { get; } = new JsonWebTokenHandler();
+
+ public bool IsValid { get; set; }
+
+ internal ValidationError? ValidationError { get; set; }
+
+ public ExpectedException? ExpectedInnerException { get; set; }
+
+ internal int ExtraStackFrames { get; }
+
+ public SecurityKey SigningKey { get; set; } = Default.SymmetricSigningKey;
+ }
+ }
+}
+#nullable restore
diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Signature.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Signature.cs
new file mode 100644
index 0000000000..ac497daa72
--- /dev/null
+++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Signature.cs
@@ -0,0 +1,266 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.IdentityModel.JsonWebTokens.Tests;
+using Microsoft.IdentityModel.Logging;
+using Microsoft.IdentityModel.TestUtils;
+using Microsoft.IdentityModel.Tokens;
+using Microsoft.IdentityModel.Tokens.Json.Tests;
+using Xunit;
+
+#nullable enable
+namespace Microsoft.IdentityModel.JsonWebTokens.Extensibility.Tests
+{
+ public partial class JsonWebTokenHandlerValidateTokenAsyncTests
+ {
+ [Theory, MemberData(nameof(Signature_ExtensibilityTestCases), DisableDiscoveryEnumeration = true)]
+ public async Task ValidateTokenAsync_SignatureValidator_Extensibility(SignatureExtensibilityTheoryData theoryData)
+ {
+ var context = TestUtilities.WriteHeader($"{this}.{nameof(ValidateTokenAsync_SignatureValidator_Extensibility)}", theoryData);
+ context.IgnoreType = false;
+ for (int i = 0; i < theoryData.ExtraStackFrames; i++)
+ theoryData.SignatureValidationError!.AddStackFrame(new StackFrame(false));
+
+ try
+ {
+ ValidationResult validationResult = await theoryData.JsonWebTokenHandler.ValidateTokenAsync(
+ theoryData.JsonWebToken!,
+ theoryData.ValidationParameters!,
+ theoryData.CallContext,
+ CancellationToken.None);
+
+ if (validationResult.IsValid)
+ {
+ // We expect the validation to fail, but it passed
+ context.AddDiff("ValidationResult is Valid.");
+ }
+ else
+ {
+ ValidationError validationError = validationResult.UnwrapError();
+ IdentityComparer.AreValidationErrorsEqual(validationError, theoryData.SignatureValidationError, context);
+ theoryData.ExpectedException.ProcessException(validationError.GetException(), context);
+ }
+ }
+ catch (Exception ex)
+ {
+ theoryData.ExpectedException.ProcessException(ex, context);
+ }
+
+ TestUtilities.AssertFailIfErrors(context);
+ }
+
+ public static TheoryData Signature_ExtensibilityTestCases
+ {
+ get
+ {
+ var theoryData = new TheoryData();
+ CallContext callContext = new CallContext();
+ var utcNow = DateTime.UtcNow;
+ var utcPlusOneHour = utcNow + TimeSpan.FromHours(1);
+
+ #region return CustomSignatureValidationError
+ // Test cases where delegate is overridden and return a CustomSignatureValidationError
+ // CustomSignatureValidationError : SignatureValidationError, ExceptionType: SecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "CustomSignatureValidatorDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.CustomSignatureValidatorDelegate,
+ extraStackFrames: 3)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorDelegate)),
+ SignatureValidationError = new CustomSignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(SecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 160))
+ });
+
+ // CustomSignatureValidationError : SignatureValidationError, ExceptionType: CustomSecurityTokenInvalidSignatureException : SecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "CustomSignatureValidatorCustomExceptionDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionDelegate,
+ extraStackFrames: 3)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionDelegate)),
+ SignatureValidationError = new CustomSignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 175)),
+ });
+
+ // CustomSignatureValidationError : SignatureValidationError, ExceptionType: NotSupportedException : SystemException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "CustomSignatureValidatorUnknownExceptionDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.CustomSignatureValidatorUnknownExceptionDelegate,
+ extraStackFrames: 3)
+ {
+ // CustomSignatureValidationError does not handle the exception type 'NotSupportedException'
+ ExpectedException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(NotSupportedException),
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorUnknownExceptionDelegate))),
+ SignatureValidationError = new CustomSignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorUnknownExceptionDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(NotSupportedException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 205)),
+ });
+
+ // CustomSignatureValidationError : SignatureValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomAudienceValidationFailureType
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate,
+ extraStackFrames: 3)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate)),
+ SignatureValidationError = new CustomSignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate), null),
+ CustomSignatureValidationError.CustomSignatureValidationFailureType,
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 190)),
+ });
+ #endregion
+
+ #region return SignatureValidationError
+ // Test cases where delegate is overridden and return an SignatureValidationError
+ // SignatureValidationError : ValidationError, ExceptionType: SecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "SignatureValidatorDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.SignatureValidatorDelegate,
+ extraStackFrames: 3)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorDelegate)),
+ SignatureValidationError = new SignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(SecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 235))
+ });
+
+ // SignatureValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidSignatureException : SecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "SignatureValidatorCustomSignatureExceptionTypeDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.SignatureValidatorCustomSignatureExceptionTypeDelegate,
+ extraStackFrames: 3)
+ {
+ // SignatureValidationError does not handle the exception type 'CustomSecurityTokenInvalidSignatureException'
+ ExpectedException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorCustomSignatureExceptionTypeDelegate))),
+ SignatureValidationError = new SignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorCustomSignatureExceptionTypeDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 259))
+ });
+
+ // SignatureValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "SignatureValidatorCustomExceptionTypeDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.SignatureValidatorCustomExceptionTypeDelegate,
+ extraStackFrames: 3)
+ {
+ // SignatureValidationError does not handle the exception type 'CustomSecurityTokenException'
+ ExpectedException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(CustomSecurityTokenException),
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorCustomExceptionTypeDelegate))),
+ SignatureValidationError = new SignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorCustomExceptionTypeDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(CustomSecurityTokenException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 274))
+ });
+
+ // SignatureValidationError : ValidationError, ExceptionType: SecurityTokenInvalidSignatureException, inner: CustomSecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "SignatureValidatorThrows",
+ utcNow,
+ CustomSignatureValidationDelegates.SignatureValidatorThrows,
+ extraStackFrames: 2)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ string.Format(Tokens.LogMessages.IDX10272),
+ typeof(CustomSecurityTokenInvalidSignatureException)),
+ SignatureValidationError = new SignatureValidationError(
+ new MessageDetail(
+ string.Format(Tokens.LogMessages.IDX10272), null),
+ ValidationFailureType.SignatureValidatorThrew,
+ typeof(SecurityTokenInvalidSignatureException),
+ new StackFrame("JsonWebTokenHandler.ValidateSignature.cs", 250),
+ null, // no inner validation error
+ new SecurityTokenInvalidSignatureException(nameof(CustomSignatureValidationDelegates.SignatureValidatorThrows))
+ )
+ });
+ #endregion
+
+ return theoryData;
+ }
+ }
+
+ public class SignatureExtensibilityTheoryData : ValidateTokenAsyncBaseTheoryData
+ {
+ internal SignatureExtensibilityTheoryData(string testId, DateTime utcNow, SignatureValidationDelegate signatureValidator, int extraStackFrames) : base(testId)
+ {
+ // The token is never read by the custom delegtes, so we create a dummy token
+ JsonWebToken = JsonUtilities.CreateUnsignedJsonWebToken("iss", "issuer");
+
+ ValidationParameters = new ValidationParameters
+ {
+ AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation,
+ AudienceValidator = SkipValidationDelegates.SkipAudienceValidation,
+ IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation,
+ IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation,
+ LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation,
+ SignatureValidator = signatureValidator,
+ TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation,
+ TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation
+ };
+
+ ExtraStackFrames = extraStackFrames;
+ }
+
+ public JsonWebToken JsonWebToken { get; }
+
+ public JsonWebTokenHandler JsonWebTokenHandler { get; } = new JsonWebTokenHandler();
+
+ public bool IsValid { get; set; }
+
+ internal SignatureValidationError? SignatureValidationError { get; set; }
+
+ internal int ExtraStackFrames { get; }
+ }
+ }
+}
+#nullable restore
diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Algorithm.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Algorithm.cs
index 36fd83ed4a..b26524130c 100644
--- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Algorithm.cs
+++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Algorithm.cs
@@ -73,9 +73,9 @@ public static TheoryData ValidateTokenAsy
validAlgorithms: [SecurityAlgorithms.Sha256]),
ExpectedIsValid = false,
ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10511:"),
- ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidAlgorithmException(
+ ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidSignatureException(
"IDX10518:",
- propertiesExpected: new() { { "InvalidAlgorithm", SecurityAlgorithms.HmacSha256Signature } }),
+ typeof(SecurityTokenInvalidAlgorithmException)),
});
return theoryData;
diff --git a/test/Microsoft.IdentityModel.TestUtils/SkipValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/SkipValidationDelegates.cs
index 5203f6ea64..2f2bb635e7 100644
--- a/test/Microsoft.IdentityModel.TestUtils/SkipValidationDelegates.cs
+++ b/test/Microsoft.IdentityModel.TestUtils/SkipValidationDelegates.cs
@@ -70,7 +70,7 @@ public static class SkipValidationDelegates
SecurityToken securityToken,
ValidationParameters validationParameters,
BaseConfiguration? configuration,
- CallContext? callContext)
+ CallContext callContext)
{
// This key is not used during the validation process. It is only used to satisfy the delegate signature.
// Follow up PR will change this to remove the SecurityKey return value.
diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomAlgorithmValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomAlgorithmValidationDelegates.cs
new file mode 100644
index 0000000000..42fa04d6de
--- /dev/null
+++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomAlgorithmValidationDelegates.cs
@@ -0,0 +1,144 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using Microsoft.IdentityModel.Tokens;
+
+#nullable enable
+namespace Microsoft.IdentityModel.TestUtils
+{
+ internal class CustomAlgorithmValidationDelegates
+ {
+ internal static ValidationResult CustomAlgorithmValidatorDelegate(
+ string algorithm,
+ SecurityKey securityKey,
+ SecurityToken securityToken,
+ ValidationParameters validationParameters,
+ CallContext callContext)
+ {
+ // Returns a CustomAlgorithmValidationError : AlgorithmValidationError
+ return new CustomAlgorithmValidationError(
+ new MessageDetail(nameof(CustomAlgorithmValidatorDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(SecurityTokenInvalidAlgorithmException),
+ ValidationError.GetCurrentStackFrame(),
+ algorithm);
+ }
+
+ internal static ValidationResult CustomAlgorithmValidatorCustomExceptionDelegate(
+ string algorithm,
+ SecurityKey securityKey,
+ SecurityToken securityToken,
+ ValidationParameters validationParameters,
+ CallContext callContext)
+ {
+ return new CustomAlgorithmValidationError(
+ new MessageDetail(nameof(CustomAlgorithmValidatorCustomExceptionDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ ValidationError.GetCurrentStackFrame(),
+ algorithm);
+ }
+
+ internal static ValidationResult CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate(
+ string algorithm,
+ SecurityKey securityKey,
+ SecurityToken securityToken,
+ ValidationParameters validationParameters,
+ CallContext callContext)
+ {
+ return new CustomAlgorithmValidationError(
+ new MessageDetail(nameof(CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate), null),
+ CustomAlgorithmValidationError.CustomAlgorithmValidationFailureType,
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ ValidationError.GetCurrentStackFrame(),
+ algorithm);
+ }
+
+ internal static ValidationResult CustomAlgorithmValidatorUnknownExceptionDelegate(
+ string algorithm,
+ SecurityKey securityKey,
+ SecurityToken securityToken,
+ ValidationParameters validationParameters,
+ CallContext callContext)
+ {
+ return new CustomAlgorithmValidationError(
+ new MessageDetail(nameof(CustomAlgorithmValidatorUnknownExceptionDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(NotSupportedException),
+ ValidationError.GetCurrentStackFrame(),
+ algorithm);
+ }
+
+ internal static ValidationResult CustomAlgorithmValidatorWithoutGetExceptionOverrideDelegate(
+ string algorithm,
+ SecurityKey securityKey,
+ SecurityToken securityToken,
+ ValidationParameters validationParameters,
+ CallContext callContext)
+ {
+ return new CustomAlgorithmWithoutGetExceptionValidationOverrideError(
+ new MessageDetail(nameof(CustomAlgorithmValidatorWithoutGetExceptionOverrideDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ ValidationError.GetCurrentStackFrame(),
+ algorithm);
+ }
+
+ internal static ValidationResult AlgorithmValidatorDelegate(
+ string algorithm,
+ SecurityKey securityKey,
+ SecurityToken securityToken,
+ ValidationParameters validationParameters,
+ CallContext callContext)
+ {
+ return new AlgorithmValidationError(
+ new MessageDetail(nameof(AlgorithmValidatorDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(SecurityTokenInvalidAlgorithmException),
+ ValidationError.GetCurrentStackFrame(),
+ algorithm);
+ }
+
+ internal static ValidationResult AlgorithmValidatorThrows(
+ string algorithm,
+ SecurityKey securityKey,
+ SecurityToken securityToken,
+ ValidationParameters validationParameters,
+ CallContext callContext)
+ {
+ throw new CustomSecurityTokenInvalidAlgorithmException(nameof(AlgorithmValidatorThrows), null);
+ }
+
+ internal static ValidationResult AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate(
+ string algorithm,
+ SecurityKey securityKey,
+ SecurityToken securityToken,
+ ValidationParameters validationParameters,
+ CallContext callContext)
+ {
+ return new AlgorithmValidationError(
+ new MessageDetail(nameof(AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ ValidationError.GetCurrentStackFrame(),
+ algorithm);
+ }
+
+ internal static ValidationResult AlgorithmValidatorCustomExceptionTypeDelegate(
+ string algorithm,
+ SecurityKey securityKey,
+ SecurityToken securityToken,
+ ValidationParameters validationParameters,
+ CallContext callContext)
+ {
+ return new AlgorithmValidationError(
+ new MessageDetail(nameof(AlgorithmValidatorCustomExceptionTypeDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(CustomSecurityTokenException),
+ ValidationError.GetCurrentStackFrame(),
+ algorithm);
+ }
+ }
+}
+#nullable restore
diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomSignatureValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomSignatureValidationDelegates.cs
new file mode 100644
index 0000000000..f80e2e46c8
--- /dev/null
+++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomSignatureValidationDelegates.cs
@@ -0,0 +1,127 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using Microsoft.IdentityModel.Tokens;
+
+#nullable enable
+namespace Microsoft.IdentityModel.TestUtils
+{
+ internal class CustomSignatureValidationDelegates
+ {
+ internal static ValidationResult CustomSignatureValidatorDelegate(
+ SecurityToken? securityToken,
+ ValidationParameters validationParameters,
+ BaseConfiguration? configuration,
+ CallContext callContext)
+ {
+ // Returns a CustomSignatureValidationError : SignatureValidationError
+ return new CustomSignatureValidationError(
+ new MessageDetail(nameof(CustomSignatureValidatorDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(SecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame());
+ }
+
+ internal static ValidationResult CustomSignatureValidatorCustomExceptionDelegate(
+ SecurityToken? securityToken,
+ ValidationParameters validationParameters,
+ BaseConfiguration? configuration,
+ CallContext callContext)
+ {
+ return new CustomSignatureValidationError(
+ new MessageDetail(nameof(CustomSignatureValidatorCustomExceptionDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame());
+ }
+
+ internal static ValidationResult CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate(
+ SecurityToken? securityToken,
+ ValidationParameters validationParameters,
+ BaseConfiguration? configuration,
+ CallContext callContext)
+ {
+ return new CustomSignatureValidationError(
+ new MessageDetail(nameof(CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate), null),
+ CustomSignatureValidationError.CustomSignatureValidationFailureType,
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame());
+ }
+
+ internal static ValidationResult CustomSignatureValidatorUnknownExceptionDelegate(
+ SecurityToken? securityToken,
+ ValidationParameters validationParameters,
+ BaseConfiguration? configuration,
+ CallContext callContext)
+ {
+ return new CustomSignatureValidationError(
+ new MessageDetail(nameof(CustomSignatureValidatorUnknownExceptionDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(NotSupportedException),
+ ValidationError.GetCurrentStackFrame());
+ }
+
+ internal static ValidationResult CustomSignatureValidatorWithoutGetExceptionOverrideDelegate(
+ SecurityToken? securityToken,
+ ValidationParameters validationParameters,
+ BaseConfiguration? configuration,
+ CallContext callContext)
+ {
+ return new CustomSignatureWithoutGetExceptionValidationOverrideError(
+ new MessageDetail(nameof(CustomSignatureValidatorWithoutGetExceptionOverrideDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame());
+ }
+
+ internal static ValidationResult SignatureValidatorDelegate(
+ SecurityToken? securityToken,
+ ValidationParameters validationParameters,
+ BaseConfiguration? configuration,
+ CallContext callContext)
+ {
+ return new SignatureValidationError(
+ new MessageDetail(nameof(SignatureValidatorDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(SecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame());
+ }
+
+ internal static ValidationResult SignatureValidatorThrows(
+ SecurityToken? securityToken,
+ ValidationParameters validationParameters,
+ BaseConfiguration? configuration,
+ CallContext callContext)
+ {
+ throw new CustomSecurityTokenInvalidSignatureException(nameof(SignatureValidatorThrows), null);
+ }
+
+ internal static ValidationResult SignatureValidatorCustomSignatureExceptionTypeDelegate(
+ SecurityToken? securityToken,
+ ValidationParameters validationParameters,
+ BaseConfiguration? configuration,
+ CallContext callContext)
+ {
+ return new SignatureValidationError(
+ new MessageDetail(nameof(SignatureValidatorCustomSignatureExceptionTypeDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ ValidationError.GetCurrentStackFrame());
+ }
+
+ internal static ValidationResult SignatureValidatorCustomExceptionTypeDelegate(
+ SecurityToken? securityToken,
+ ValidationParameters validationParameters,
+ BaseConfiguration? configuration,
+ CallContext callContext)
+ {
+ return new SignatureValidationError(
+ new MessageDetail(nameof(SignatureValidatorCustomExceptionTypeDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(CustomSecurityTokenException),
+ ValidationError.GetCurrentStackFrame());
+ }
+ }
+}
+#nullable restore
diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs
index 8f20be2168..9623161345 100644
--- a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs
+++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs
@@ -201,6 +201,98 @@ public CustomTokenTypeWithoutGetExceptionValidationOverrideError(
}
#endregion // TokenTypeValidationErrors
+ #region SignatureValidationErrors
+ internal class CustomSignatureValidationError : SignatureValidationError
+ {
+ ///
+ /// A custom validation failure type.
+ ///
+ public static readonly ValidationFailureType CustomSignatureValidationFailureType = new SignatureValidatorFailure("CustomSignatureValidationFailureType");
+ private class SignatureValidatorFailure : ValidationFailureType { internal SignatureValidatorFailure(string name) : base(name) { } }
+
+ public CustomSignatureValidationError(
+ MessageDetail messageDetail,
+ ValidationFailureType validationFailureType,
+ Type exceptionType,
+ StackFrame stackFrame,
+ ValidationError? innerValidationError = null,
+ Exception? innerException = null) :
+ base(messageDetail, validationFailureType, exceptionType, stackFrame, innerValidationError, innerException)
+ {
+ }
+ internal override Exception GetException()
+ {
+ if (ExceptionType == typeof(CustomSecurityTokenInvalidSignatureException))
+ {
+ var exception = new CustomSecurityTokenInvalidSignatureException(MessageDetail.Message, InnerException);
+ exception.SetValidationError(this);
+ return exception;
+ }
+ return base.GetException();
+ }
+ }
+
+ internal class CustomSignatureWithoutGetExceptionValidationOverrideError : SignatureValidationError
+ {
+ public CustomSignatureWithoutGetExceptionValidationOverrideError(
+ MessageDetail messageDetail,
+ ValidationFailureType validationFailureType,
+ Type exceptionType,
+ StackFrame stackFrame,
+ ValidationError? innerValidationError = null,
+ Exception? innerException = null) :
+ base(messageDetail, validationFailureType, exceptionType, stackFrame, innerValidationError, innerException)
+ {
+ }
+ }
+ #endregion // SignatureValidationErrors
+
+ #region AlgorithmValidationErrors
+ internal class CustomAlgorithmValidationError : AlgorithmValidationError
+ {
+ ///
+ /// A custom validation failure type.
+ ///
+ public static readonly ValidationFailureType CustomAlgorithmValidationFailureType = new AlgorithmValidatorFailure("CustomAlgorithmValidationFailureType");
+ private class AlgorithmValidatorFailure : ValidationFailureType { internal AlgorithmValidatorFailure(string name) : base(name) { } }
+
+ public CustomAlgorithmValidationError(
+ MessageDetail messageDetail,
+ ValidationFailureType validationFailureType,
+ Type exceptionType,
+ StackFrame stackFrame,
+ string? algorithm,
+ Exception? innerException = null)
+ : base(messageDetail, validationFailureType, exceptionType, stackFrame, algorithm, innerException)
+ {
+ }
+ internal override Exception GetException()
+ {
+ if (ExceptionType == typeof(CustomSecurityTokenInvalidAlgorithmException))
+ {
+ var exception = new CustomSecurityTokenInvalidAlgorithmException(MessageDetail.Message, InnerException) { InvalidAlgorithm = InvalidAlgorithm };
+ exception.SetValidationError(this);
+ return exception;
+ }
+ return base.GetException();
+ }
+ }
+
+ internal class CustomAlgorithmWithoutGetExceptionValidationOverrideError : AlgorithmValidationError
+ {
+ public CustomAlgorithmWithoutGetExceptionValidationOverrideError(
+ MessageDetail messageDetail,
+ ValidationFailureType validationFailureType,
+ Type exceptionType,
+ StackFrame stackFrame,
+ string? invalidAlgorithm,
+ Exception? innerException = null) :
+ base(messageDetail, validationFailureType, exceptionType, stackFrame, invalidAlgorithm, innerException)
+ {
+ }
+ }
+ #endregion // AlgorithmValidationErrors
+
// Other custom validation errors to be added here for signature validation, issuer signing key, etc.
}
#nullable restore
diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Algorithm.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Algorithm.cs
new file mode 100644
index 0000000000..b2e3171da4
--- /dev/null
+++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Algorithm.cs
@@ -0,0 +1,337 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.IdentityModel.Logging;
+using Microsoft.IdentityModel.TestUtils;
+
+using Xunit;
+
+#nullable enable
+namespace Microsoft.IdentityModel.Tokens.Saml2.Extensibility.Tests
+{
+ public partial class Saml2SecurityTokenHandlerValidateTokenAsyncTests
+ {
+ [Theory, MemberData(nameof(Algorithm_ExtensibilityTestCases), DisableDiscoveryEnumeration = true)]
+ public async Task ValidateTokenAsync_AlgorithmValidator_Extensibility(AlgorithmExtensibilityTheoryData theoryData)
+ {
+ var context = TestUtilities.WriteHeader($"{this}.{nameof(ValidateTokenAsync_AlgorithmValidator_Extensibility)}", theoryData);
+ context.IgnoreType = false;
+ for (int i = 0; i < theoryData.ExtraStackFrames; i++)
+ theoryData.ValidationError!.AddStackFrame(new StackFrame(false));
+
+ Saml2SecurityToken saml2Token = (Saml2SecurityToken)theoryData.Saml2SecurityTokenHandler.CreateToken(new SecurityTokenDescriptor()
+ {
+ Issuer = Default.Issuer,
+ Subject = Default.SamlClaimsIdentity,
+ SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2,
+ });
+ theoryData.Saml2Token = theoryData.Saml2SecurityTokenHandler.ReadSaml2Token(saml2Token.Assertion.CanonicalString);
+
+ theoryData.ValidationParameters!.IssuerSigningKeys.Add(theoryData.SigningKey);
+
+ try
+ {
+ ValidationResult validationResult = await theoryData.Saml2SecurityTokenHandler.ValidateTokenAsync(
+ theoryData.Saml2Token!,
+ theoryData.ValidationParameters!,
+ theoryData.CallContext,
+ CancellationToken.None);
+
+ if (validationResult.IsValid)
+ {
+ // We expect the validation to fail, but it passed
+ context.AddDiff("ValidationResult is Valid.");
+ }
+ else
+ {
+ ValidationError validationError = validationResult.UnwrapError();
+
+ if (validationError is SignatureValidationError signatureValidationError &&
+ signatureValidationError.InnerValidationError is not null)
+ {
+ IdentityComparer.AreValidationErrorsEqual(
+ signatureValidationError.InnerValidationError,
+ theoryData.ValidationError,
+ context);
+ }
+ else
+ {
+ IdentityComparer.AreValidationErrorsEqual(
+ validationError,
+ theoryData.ValidationError,
+ context);
+ }
+
+ var exception = validationError.GetException();
+ theoryData.ExpectedException.ProcessException(exception, context);
+ // Process the inner exception since invalid algorithm exceptions are wrapped inside
+ // invalid signature exceptions
+ if (theoryData.ExpectedInnerException is not null)
+ theoryData.ExpectedInnerException.ProcessException(exception.InnerException, context);
+ }
+ }
+ catch (Exception ex)
+ {
+ // We expect the validation to fail, but it threw an exception
+ context.AddDiff($"ValidateTokenAsync threw exception: {ex}");
+ }
+
+ TestUtilities.AssertFailIfErrors(context);
+ }
+
+ public static TheoryData Algorithm_ExtensibilityTestCases
+ {
+ get
+ {
+ var theoryData = new TheoryData();
+ CallContext callContext = new CallContext();
+ var utcNow = DateTime.UtcNow;
+ var utcPlusOneHour = utcNow + TimeSpan.FromHours(1);
+
+ #region return CustomAlgorithmValidationError
+ // Test cases where delegate is overridden and return a CustomAlgorithmValidationError
+ // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: SecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "CustomAlgorithmValidatorDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorDelegate,
+ extraStackFrames: 1)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(SecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorDelegate)),
+ ValidationError = new CustomAlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(SecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 160),
+ "algorithm")
+ });
+
+ // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: CustomSecurityTokenInvalidAlgorithmException : SecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "CustomAlgorithmValidatorCustomExceptionDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionDelegate,
+ extraStackFrames: 1)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(CustomSecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionDelegate)),
+ ValidationError = new CustomAlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 175),
+ "algorithm"),
+ });
+
+ // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: NotSupportedException : SystemException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "CustomAlgorithmValidatorUnknownExceptionDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorUnknownExceptionDelegate,
+ extraStackFrames: 1)
+ {
+ // CustomAlgorithmValidationError does not handle the exception type 'NotSupportedException'
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenException)),
+ ExpectedInnerException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(NotSupportedException),
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorUnknownExceptionDelegate))),
+ ValidationError = new CustomAlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorUnknownExceptionDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(NotSupportedException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 205),
+ "algorithm"),
+ });
+
+ // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomAudienceValidationFailureType
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate,
+ extraStackFrames: 1)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(CustomSecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate)),
+ ValidationError = new CustomAlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate), null),
+ CustomAlgorithmValidationError.CustomAlgorithmValidationFailureType,
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 190),
+ "algorithm"),
+ });
+ #endregion
+
+ #region return AlgorithmValidationError
+ // Test cases where delegate is overridden and return an AlgorithmValidationError
+ // AlgorithmValidationError : ValidationError, ExceptionType: SecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "AlgorithmValidatorDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.AlgorithmValidatorDelegate,
+ extraStackFrames: 1)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(SecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorDelegate)),
+ ValidationError = new AlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(SecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 235),
+ "algorithm")
+ });
+
+ // AlgorithmValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidAlgorithmException : SecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate,
+ extraStackFrames: 1)
+ {
+ // AlgorithmValidationError does not handle the exception type 'CustomSecurityTokenInvalidAlgorithmException'
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenException)),
+ ExpectedInnerException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate))),
+ ValidationError = new AlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 259),
+ "algorithm")
+ });
+
+ // AlgorithmValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "AlgorithmValidatorCustomExceptionTypeDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomExceptionTypeDelegate,
+ extraStackFrames: 1)
+ {
+ // AlgorithmValidationError does not handle the exception type 'CustomSecurityTokenException'
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenException)),
+ ExpectedInnerException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(CustomSecurityTokenException),
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomExceptionTypeDelegate))),
+ ValidationError = new AlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomExceptionTypeDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(CustomSecurityTokenException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 274),
+ "algorithm")
+ });
+
+ // SignatureValidationError : ValidationError, ExceptionType: SecurityTokenInvalidSignatureException, inner: CustomSecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "AlgorithmValidatorThrows",
+ utcNow,
+ CustomAlgorithmValidationDelegates.AlgorithmValidatorThrows,
+ extraStackFrames: 2)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10273:",
+ typeof(CustomSecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorThrows)),
+ ValidationError = new SignatureValidationError(
+ new MessageDetail(
+ string.Format(Tokens.LogMessages.IDX10273), null),
+ ValidationFailureType.AlgorithmValidatorThrew,
+ typeof(SecurityTokenInvalidSignatureException),
+ new StackFrame("Saml2SecurityTokenHandler.ValidateSignature.cs", 250),
+ null, // no inner validation error
+ new CustomSecurityTokenInvalidAlgorithmException(nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorThrows), null)
+ )
+ });
+ #endregion
+
+ return theoryData;
+ }
+ }
+
+ public class AlgorithmExtensibilityTheoryData : TheoryDataBase
+ {
+ internal AlgorithmExtensibilityTheoryData(string testId, DateTime utcNow, AlgorithmValidationDelegate algorithmValidator, int extraStackFrames) : base(testId)
+ {
+ ValidationParameters = new ValidationParameters
+ {
+ // We leave the default signature validator to call the custom algorithm validator
+ AlgorithmValidator = algorithmValidator,
+ AudienceValidator = SkipValidationDelegates.SkipAudienceValidation,
+ IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation,
+ IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation,
+ LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation,
+ TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation,
+ TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation,
+ };
+
+ ExtraStackFrames = extraStackFrames;
+ }
+
+ public Saml2SecurityToken? Saml2Token { get; set; }
+
+ public Saml2SecurityTokenHandler Saml2SecurityTokenHandler { get; } = new Saml2SecurityTokenHandler();
+
+ public bool IsValid { get; set; }
+
+ internal ValidationError? ValidationError { get; set; }
+
+ public ExpectedException? ExpectedInnerException { get; set; }
+
+ internal int ExtraStackFrames { get; }
+
+ internal ValidationParameters ValidationParameters { get; }
+
+ public SecurityKey SigningKey { get; set; } = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key;
+ }
+ }
+}
+#nullable restore
diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Signature.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Signature.cs
new file mode 100644
index 0000000000..d1635c6635
--- /dev/null
+++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Signature.cs
@@ -0,0 +1,270 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.IdentityModel.Logging;
+using Microsoft.IdentityModel.TestUtils;
+
+using Xunit;
+
+#nullable enable
+namespace Microsoft.IdentityModel.Tokens.Saml2.Extensibility.Tests
+{
+ public partial class Saml2SecurityTokenHandlerValidateTokenAsyncTests
+ {
+ [Theory, MemberData(nameof(Signature_ExtensibilityTestCases), DisableDiscoveryEnumeration = true)]
+ public async Task ValidateTokenAsync_SignatureValidator_Extensibility(SignatureExtensibilityTheoryData theoryData)
+ {
+ var context = TestUtilities.WriteHeader($"{this}.{nameof(ValidateTokenAsync_SignatureValidator_Extensibility)}", theoryData);
+ context.IgnoreType = false;
+ for (int i = 0; i < theoryData.ExtraStackFrames; i++)
+ theoryData.SignatureValidationError!.AddStackFrame(new StackFrame(false));
+
+ try
+ {
+ ValidationResult validationResult = await theoryData.Saml2SecurityTokenHandler.ValidateTokenAsync(
+ theoryData.Saml2Token!,
+ theoryData.ValidationParameters!,
+ theoryData.CallContext,
+ CancellationToken.None);
+
+ if (validationResult.IsValid)
+ {
+ // We expect the validation to fail, but it passed
+ context.AddDiff("ValidationResult is Valid.");
+ }
+ else
+ {
+ ValidationError validationError = validationResult.UnwrapError();
+ IdentityComparer.AreValidationErrorsEqual(validationError, theoryData.SignatureValidationError, context);
+ theoryData.ExpectedException.ProcessException(validationError.GetException(), context);
+ }
+ }
+ catch (Exception ex)
+ {
+ theoryData.ExpectedException.ProcessException(ex, context);
+ }
+
+ TestUtilities.AssertFailIfErrors(context);
+ }
+
+ public static TheoryData Signature_ExtensibilityTestCases
+ {
+ get
+ {
+ var theoryData = new TheoryData();
+ CallContext callContext = new CallContext();
+ var utcNow = DateTime.UtcNow;
+ var utcPlusOneHour = utcNow + TimeSpan.FromHours(1);
+
+ #region return CustomSignatureValidationError
+ // Test cases where delegate is overridden and return a CustomSignatureValidationError
+ // CustomSignatureValidationError : SignatureValidationError, ExceptionType: SecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "CustomSignatureValidatorDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.CustomSignatureValidatorDelegate,
+ extraStackFrames: 2)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorDelegate)),
+ SignatureValidationError = new CustomSignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(SecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 160))
+ });
+
+ // CustomSignatureValidationError : SignatureValidationError, ExceptionType: CustomSecurityTokenInvalidSignatureException : SecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "CustomSignatureValidatorCustomExceptionDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionDelegate,
+ extraStackFrames: 2)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionDelegate)),
+ SignatureValidationError = new CustomSignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 175)),
+ });
+
+ // CustomSignatureValidationError : SignatureValidationError, ExceptionType: NotSupportedException : SystemException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "CustomSignatureValidatorUnknownExceptionDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.CustomSignatureValidatorUnknownExceptionDelegate,
+ extraStackFrames: 2)
+ {
+ // CustomSignatureValidationError does not handle the exception type 'NotSupportedException'
+ ExpectedException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(NotSupportedException),
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorUnknownExceptionDelegate))),
+ SignatureValidationError = new CustomSignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorUnknownExceptionDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(NotSupportedException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 205)),
+ });
+
+ // CustomSignatureValidationError : SignatureValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomAudienceValidationFailureType
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate,
+ extraStackFrames: 2)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate)),
+ SignatureValidationError = new CustomSignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate), null),
+ CustomSignatureValidationError.CustomSignatureValidationFailureType,
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 190)),
+ });
+ #endregion
+
+ #region return SignatureValidationError
+ // Test cases where delegate is overridden and return an SignatureValidationError
+ // SignatureValidationError : ValidationError, ExceptionType: SecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "SignatureValidatorDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.SignatureValidatorDelegate,
+ extraStackFrames: 2)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorDelegate)),
+ SignatureValidationError = new SignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(SecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 235))
+ });
+
+ // SignatureValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidSignatureException : SecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "SignatureValidatorCustomSignatureExceptionTypeDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.SignatureValidatorCustomSignatureExceptionTypeDelegate,
+ extraStackFrames: 2)
+ {
+ // SignatureValidationError does not handle the exception type 'CustomSecurityTokenInvalidSignatureException'
+ ExpectedException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorCustomSignatureExceptionTypeDelegate))),
+ SignatureValidationError = new SignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorCustomSignatureExceptionTypeDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 259))
+ });
+
+ // SignatureValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "SignatureValidatorCustomExceptionTypeDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.SignatureValidatorCustomExceptionTypeDelegate,
+ extraStackFrames: 2)
+ {
+ // SignatureValidationError does not handle the exception type 'CustomSecurityTokenException'
+ ExpectedException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(CustomSecurityTokenException),
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorCustomExceptionTypeDelegate))),
+ SignatureValidationError = new SignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorCustomExceptionTypeDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(CustomSecurityTokenException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 274))
+ });
+
+ // SignatureValidationError : ValidationError, ExceptionType: SecurityTokenInvalidSignatureException, inner: CustomSecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "SignatureValidatorThrows",
+ utcNow,
+ CustomSignatureValidationDelegates.SignatureValidatorThrows,
+ extraStackFrames: 1)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ string.Format(Tokens.LogMessages.IDX10272),
+ typeof(CustomSecurityTokenInvalidSignatureException)),
+ SignatureValidationError = new SignatureValidationError(
+ new MessageDetail(
+ string.Format(Tokens.LogMessages.IDX10272), null),
+ ValidationFailureType.SignatureValidatorThrew,
+ typeof(SecurityTokenInvalidSignatureException),
+ new StackFrame("Saml2SecurityTokenHandler.ValidateSignature.cs", 250),
+ null, // no inner validation error
+ new SecurityTokenInvalidSignatureException(nameof(CustomSignatureValidationDelegates.SignatureValidatorThrows))
+ )
+ });
+ #endregion
+
+ return theoryData;
+ }
+ }
+
+ public class SignatureExtensibilityTheoryData : TheoryDataBase
+ {
+ internal SignatureExtensibilityTheoryData(string testId, DateTime utcNow, SignatureValidationDelegate signatureValidator, int extraStackFrames) : base(testId)
+ {
+ Saml2Token = (Saml2SecurityToken)Saml2SecurityTokenHandler.CreateToken(
+ new SecurityTokenDescriptor()
+ {
+ Subject = Default.SamlClaimsIdentity,
+ Issuer = Default.Issuer,
+ });
+
+ ValidationParameters = new ValidationParameters
+ {
+ AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation,
+ AudienceValidator = SkipValidationDelegates.SkipAudienceValidation,
+ IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation,
+ IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation,
+ LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation,
+ SignatureValidator = signatureValidator,
+ TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation,
+ TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation
+ };
+
+ ExtraStackFrames = extraStackFrames;
+ }
+
+ public Saml2SecurityToken Saml2Token { get; }
+
+ public Saml2SecurityTokenHandler Saml2SecurityTokenHandler { get; } = new Saml2SecurityTokenHandler();
+
+ public bool IsValid { get; set; }
+
+ internal ValidationParameters ValidationParameters { get; }
+
+ internal SignatureValidationError? SignatureValidationError { get; set; }
+
+ internal int ExtraStackFrames { get; }
+ }
+ }
+}
+#nullable restore
diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.IssuerSigningKey.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.IssuerSigningKey.cs
index 94d38d6151..38f25ddc2a 100644
--- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.IssuerSigningKey.cs
+++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.IssuerSigningKey.cs
@@ -132,7 +132,7 @@ static ValidationParameters CreateValidationParameters(
SecurityToken token,
ValidationParameters validationParameters,
BaseConfiguration? configuration,
- CallContext? callContext) =>
+ CallContext callContext) =>
{
// Set the signing key for validation
token.SigningKey = issuerSigingKey;
diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Algorithm.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Algorithm.cs
new file mode 100644
index 0000000000..ade781d8ac
--- /dev/null
+++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Algorithm.cs
@@ -0,0 +1,337 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.IdentityModel.Logging;
+using Microsoft.IdentityModel.TestUtils;
+
+using Xunit;
+
+#nullable enable
+namespace Microsoft.IdentityModel.Tokens.Saml.Extensibility.Tests
+{
+ public partial class SamlSecurityTokenHandlerValidateTokenAsyncTests
+ {
+ [Theory, MemberData(nameof(Algorithm_ExtensibilityTestCases), DisableDiscoveryEnumeration = true)]
+ public async Task ValidateTokenAsync_AlgorithmValidator_Extensibility(AlgorithmExtensibilityTheoryData theoryData)
+ {
+ var context = TestUtilities.WriteHeader($"{this}.{nameof(ValidateTokenAsync_AlgorithmValidator_Extensibility)}", theoryData);
+ context.IgnoreType = false;
+ for (int i = 0; i < theoryData.ExtraStackFrames; i++)
+ theoryData.ValidationError!.AddStackFrame(new StackFrame(false));
+
+ SamlSecurityToken SamlToken = (SamlSecurityToken)theoryData.SamlSecurityTokenHandler.CreateToken(new SecurityTokenDescriptor()
+ {
+ Issuer = Default.Issuer,
+ Subject = Default.SamlClaimsIdentity,
+ SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2,
+ });
+ theoryData.SamlToken = theoryData.SamlSecurityTokenHandler.ReadSamlToken(SamlToken.Assertion.CanonicalString);
+
+ theoryData.ValidationParameters!.IssuerSigningKeys.Add(theoryData.SigningKey);
+
+ try
+ {
+ ValidationResult validationResult = await theoryData.SamlSecurityTokenHandler.ValidateTokenAsync(
+ theoryData.SamlToken!,
+ theoryData.ValidationParameters!,
+ theoryData.CallContext,
+ CancellationToken.None);
+
+ if (validationResult.IsValid)
+ {
+ // We expect the validation to fail, but it passed
+ context.AddDiff("ValidationResult is Valid.");
+ }
+ else
+ {
+ ValidationError validationError = validationResult.UnwrapError();
+
+ if (validationError is SignatureValidationError signatureValidationError &&
+ signatureValidationError.InnerValidationError is not null)
+ {
+ IdentityComparer.AreValidationErrorsEqual(
+ signatureValidationError.InnerValidationError,
+ theoryData.ValidationError,
+ context);
+ }
+ else
+ {
+ IdentityComparer.AreValidationErrorsEqual(
+ validationError,
+ theoryData.ValidationError,
+ context);
+ }
+
+ var exception = validationError.GetException();
+ theoryData.ExpectedException.ProcessException(exception, context);
+ // Process the inner exception since invalid algorithm exceptions are wrapped inside
+ // invalid signature exceptions
+ if (theoryData.ExpectedInnerException is not null)
+ theoryData.ExpectedInnerException.ProcessException(exception.InnerException, context);
+ }
+ }
+ catch (Exception ex)
+ {
+ // We expect the validation to fail, but it threw an exception
+ context.AddDiff($"ValidateTokenAsync threw exception: {ex}");
+ }
+
+ TestUtilities.AssertFailIfErrors(context);
+ }
+
+ public static TheoryData Algorithm_ExtensibilityTestCases
+ {
+ get
+ {
+ var theoryData = new TheoryData();
+ CallContext callContext = new CallContext();
+ var utcNow = DateTime.UtcNow;
+ var utcPlusOneHour = utcNow + TimeSpan.FromHours(1);
+
+ #region return CustomAlgorithmValidationError
+ // Test cases where delegate is overridden and return a CustomAlgorithmValidationError
+ // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: SecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "CustomAlgorithmValidatorDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorDelegate,
+ extraStackFrames: 1)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(SecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorDelegate)),
+ ValidationError = new CustomAlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(SecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 160),
+ "algorithm")
+ });
+
+ // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: CustomSecurityTokenInvalidAlgorithmException : SecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "CustomAlgorithmValidatorCustomExceptionDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionDelegate,
+ extraStackFrames: 1)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(CustomSecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionDelegate)),
+ ValidationError = new CustomAlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 175),
+ "algorithm"),
+ });
+
+ // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: NotSupportedException : SystemException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "CustomAlgorithmValidatorUnknownExceptionDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorUnknownExceptionDelegate,
+ extraStackFrames: 1)
+ {
+ // CustomAlgorithmValidationError does not handle the exception type 'NotSupportedException'
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenException)),
+ ExpectedInnerException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(NotSupportedException),
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorUnknownExceptionDelegate))),
+ ValidationError = new CustomAlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorUnknownExceptionDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(NotSupportedException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 205),
+ "algorithm"),
+ });
+
+ // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomAudienceValidationFailureType
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate,
+ extraStackFrames: 1)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(CustomSecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate)),
+ ValidationError = new CustomAlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate), null),
+ CustomAlgorithmValidationError.CustomAlgorithmValidationFailureType,
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 190),
+ "algorithm"),
+ });
+ #endregion
+
+ #region return AlgorithmValidationError
+ // Test cases where delegate is overridden and return an AlgorithmValidationError
+ // AlgorithmValidationError : ValidationError, ExceptionType: SecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "AlgorithmValidatorDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.AlgorithmValidatorDelegate,
+ extraStackFrames: 1)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(SecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorDelegate)),
+ ValidationError = new AlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(SecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 235),
+ "algorithm")
+ });
+
+ // AlgorithmValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidAlgorithmException : SecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate,
+ extraStackFrames: 1)
+ {
+ // AlgorithmValidationError does not handle the exception type 'CustomSecurityTokenInvalidAlgorithmException'
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenException)),
+ ExpectedInnerException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate))),
+ ValidationError = new AlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 259),
+ "algorithm")
+ });
+
+ // AlgorithmValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "AlgorithmValidatorCustomExceptionTypeDelegate",
+ utcNow,
+ CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomExceptionTypeDelegate,
+ extraStackFrames: 1)
+ {
+ // AlgorithmValidationError does not handle the exception type 'CustomSecurityTokenException'
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10518:",
+ typeof(SecurityTokenException)),
+ ExpectedInnerException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(CustomSecurityTokenException),
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomExceptionTypeDelegate))),
+ ValidationError = new AlgorithmValidationError(
+ new MessageDetail(
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomExceptionTypeDelegate), null),
+ ValidationFailureType.AlgorithmValidationFailed,
+ typeof(CustomSecurityTokenException),
+ new StackFrame("CustomAlgorithmValidationDelegates.cs", 274),
+ "algorithm")
+ });
+
+ // SignatureValidationError : ValidationError, ExceptionType: SecurityTokenInvalidSignatureException, inner: CustomSecurityTokenInvalidAlgorithmException
+ theoryData.Add(new AlgorithmExtensibilityTheoryData(
+ "AlgorithmValidatorThrows",
+ utcNow,
+ CustomAlgorithmValidationDelegates.AlgorithmValidatorThrows,
+ extraStackFrames: 2)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ "IDX10273:",
+ typeof(CustomSecurityTokenInvalidAlgorithmException)),
+ ExpectedInnerException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidAlgorithmException),
+ nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorThrows)),
+ ValidationError = new SignatureValidationError(
+ new MessageDetail(
+ string.Format(Tokens.LogMessages.IDX10273), null),
+ ValidationFailureType.AlgorithmValidatorThrew,
+ typeof(SecurityTokenInvalidSignatureException),
+ new StackFrame("SamlSecurityTokenHandler.ValidateSignature.cs", 250),
+ null, // no inner validation error
+ new CustomSecurityTokenInvalidAlgorithmException(nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorThrows), null)
+ )
+ });
+ #endregion
+
+ return theoryData;
+ }
+ }
+
+ public class AlgorithmExtensibilityTheoryData : TheoryDataBase
+ {
+ internal AlgorithmExtensibilityTheoryData(string testId, DateTime utcNow, AlgorithmValidationDelegate algorithmValidator, int extraStackFrames) : base(testId)
+ {
+ ValidationParameters = new ValidationParameters
+ {
+ // We leave the default signature validator to call the custom algorithm validator
+ AlgorithmValidator = algorithmValidator,
+ AudienceValidator = SkipValidationDelegates.SkipAudienceValidation,
+ IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation,
+ IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation,
+ LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation,
+ TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation,
+ TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation,
+ };
+
+ ExtraStackFrames = extraStackFrames;
+ }
+
+ public SamlSecurityToken? SamlToken { get; set; }
+
+ public SamlSecurityTokenHandler SamlSecurityTokenHandler { get; } = new SamlSecurityTokenHandler();
+
+ public bool IsValid { get; set; }
+
+ internal ValidationError? ValidationError { get; set; }
+
+ public ExpectedException? ExpectedInnerException { get; set; }
+
+ internal int ExtraStackFrames { get; }
+
+ internal ValidationParameters ValidationParameters { get; }
+
+ public SecurityKey SigningKey { get; set; } = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key;
+ }
+ }
+}
+#nullable restore
diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Signature.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Signature.cs
new file mode 100644
index 0000000000..467bce3c42
--- /dev/null
+++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Signature.cs
@@ -0,0 +1,270 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.IdentityModel.Logging;
+using Microsoft.IdentityModel.TestUtils;
+
+using Xunit;
+
+#nullable enable
+namespace Microsoft.IdentityModel.Tokens.Saml.Extensibility.Tests
+{
+ public partial class SamlSecurityTokenHandlerValidateTokenAsyncTests
+ {
+ [Theory, MemberData(nameof(Signature_ExtensibilityTestCases), DisableDiscoveryEnumeration = true)]
+ public async Task ValidateTokenAsync_SignatureValidator_Extensibility(SignatureExtensibilityTheoryData theoryData)
+ {
+ var context = TestUtilities.WriteHeader($"{this}.{nameof(ValidateTokenAsync_SignatureValidator_Extensibility)}", theoryData);
+ context.IgnoreType = false;
+ for (int i = 0; i < theoryData.ExtraStackFrames; i++)
+ theoryData.SignatureValidationError!.AddStackFrame(new StackFrame(false));
+
+ try
+ {
+ ValidationResult validationResult = await theoryData.SamlSecurityTokenHandler.ValidateTokenAsync(
+ theoryData.SamlToken!,
+ theoryData.ValidationParameters!,
+ theoryData.CallContext,
+ CancellationToken.None);
+
+ if (validationResult.IsValid)
+ {
+ // We expect the validation to fail, but it passed
+ context.AddDiff("ValidationResult is Valid.");
+ }
+ else
+ {
+ ValidationError validationError = validationResult.UnwrapError();
+ IdentityComparer.AreValidationErrorsEqual(validationError, theoryData.SignatureValidationError, context);
+ theoryData.ExpectedException.ProcessException(validationError.GetException(), context);
+ }
+ }
+ catch (Exception ex)
+ {
+ theoryData.ExpectedException.ProcessException(ex, context);
+ }
+
+ TestUtilities.AssertFailIfErrors(context);
+ }
+
+ public static TheoryData Signature_ExtensibilityTestCases
+ {
+ get
+ {
+ var theoryData = new TheoryData();
+ CallContext callContext = new CallContext();
+ var utcNow = DateTime.UtcNow;
+ var utcPlusOneHour = utcNow + TimeSpan.FromHours(1);
+
+ #region return CustomSignatureValidationError
+ // Test cases where delegate is overridden and return a CustomSignatureValidationError
+ // CustomSignatureValidationError : SignatureValidationError, ExceptionType: SecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "CustomSignatureValidatorDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.CustomSignatureValidatorDelegate,
+ extraStackFrames: 2)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorDelegate)),
+ SignatureValidationError = new CustomSignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(SecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 160))
+ });
+
+ // CustomSignatureValidationError : SignatureValidationError, ExceptionType: CustomSecurityTokenInvalidSignatureException : SecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "CustomSignatureValidatorCustomExceptionDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionDelegate,
+ extraStackFrames: 2)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionDelegate)),
+ SignatureValidationError = new CustomSignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 175)),
+ });
+
+ // CustomSignatureValidationError : SignatureValidationError, ExceptionType: NotSupportedException : SystemException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "CustomSignatureValidatorUnknownExceptionDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.CustomSignatureValidatorUnknownExceptionDelegate,
+ extraStackFrames: 2)
+ {
+ // CustomSignatureValidationError does not handle the exception type 'NotSupportedException'
+ ExpectedException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(NotSupportedException),
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorUnknownExceptionDelegate))),
+ SignatureValidationError = new CustomSignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorUnknownExceptionDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(NotSupportedException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 205)),
+ });
+
+ // CustomSignatureValidationError : SignatureValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomAudienceValidationFailureType
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate,
+ extraStackFrames: 2)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate)),
+ SignatureValidationError = new CustomSignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate), null),
+ CustomSignatureValidationError.CustomSignatureValidationFailureType,
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 190)),
+ });
+ #endregion
+
+ #region return SignatureValidationError
+ // Test cases where delegate is overridden and return an SignatureValidationError
+ // SignatureValidationError : ValidationError, ExceptionType: SecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "SignatureValidatorDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.SignatureValidatorDelegate,
+ extraStackFrames: 2)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorDelegate)),
+ SignatureValidationError = new SignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(SecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 235))
+ });
+
+ // SignatureValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidSignatureException : SecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "SignatureValidatorCustomSignatureExceptionTypeDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.SignatureValidatorCustomSignatureExceptionTypeDelegate,
+ extraStackFrames: 2)
+ {
+ // SignatureValidationError does not handle the exception type 'CustomSecurityTokenInvalidSignatureException'
+ ExpectedException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorCustomSignatureExceptionTypeDelegate))),
+ SignatureValidationError = new SignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorCustomSignatureExceptionTypeDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(CustomSecurityTokenInvalidSignatureException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 259))
+ });
+
+ // SignatureValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "SignatureValidatorCustomExceptionTypeDelegate",
+ utcNow,
+ CustomSignatureValidationDelegates.SignatureValidatorCustomExceptionTypeDelegate,
+ extraStackFrames: 2)
+ {
+ // SignatureValidationError does not handle the exception type 'CustomSecurityTokenException'
+ ExpectedException = ExpectedException.SecurityTokenException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'.";
+ typeof(CustomSecurityTokenException),
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorCustomExceptionTypeDelegate))),
+ SignatureValidationError = new SignatureValidationError(
+ new MessageDetail(
+ nameof(CustomSignatureValidationDelegates.SignatureValidatorCustomExceptionTypeDelegate), null),
+ ValidationFailureType.SignatureValidationFailed,
+ typeof(CustomSecurityTokenException),
+ new StackFrame("CustomSignatureValidationDelegates.cs", 274))
+ });
+
+ // SignatureValidationError : ValidationError, ExceptionType: SecurityTokenInvalidSignatureException, inner: CustomSecurityTokenInvalidSignatureException
+ theoryData.Add(new SignatureExtensibilityTheoryData(
+ "SignatureValidatorThrows",
+ utcNow,
+ CustomSignatureValidationDelegates.SignatureValidatorThrows,
+ extraStackFrames: 1)
+ {
+ ExpectedException = new ExpectedException(
+ typeof(SecurityTokenInvalidSignatureException),
+ string.Format(Tokens.LogMessages.IDX10272),
+ typeof(CustomSecurityTokenInvalidSignatureException)),
+ SignatureValidationError = new SignatureValidationError(
+ new MessageDetail(
+ string.Format(Tokens.LogMessages.IDX10272), null),
+ ValidationFailureType.SignatureValidatorThrew,
+ typeof(SecurityTokenInvalidSignatureException),
+ new StackFrame("SamlSecurityTokenHandler.ValidateSignature.cs", 250),
+ null, // no inner validation error
+ new SecurityTokenInvalidSignatureException(nameof(CustomSignatureValidationDelegates.SignatureValidatorThrows))
+ )
+ });
+ #endregion
+
+ return theoryData;
+ }
+ }
+
+ public class SignatureExtensibilityTheoryData : TheoryDataBase
+ {
+ internal SignatureExtensibilityTheoryData(string testId, DateTime utcNow, SignatureValidationDelegate signatureValidator, int extraStackFrames) : base(testId)
+ {
+ SamlToken = (SamlSecurityToken)SamlSecurityTokenHandler.CreateToken(
+ new SecurityTokenDescriptor()
+ {
+ Subject = Default.SamlClaimsIdentity,
+ Issuer = Default.Issuer,
+ });
+
+ ValidationParameters = new ValidationParameters
+ {
+ AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation,
+ AudienceValidator = SkipValidationDelegates.SkipAudienceValidation,
+ IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation,
+ IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation,
+ LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation,
+ SignatureValidator = signatureValidator,
+ TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation,
+ TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation
+ };
+
+ ExtraStackFrames = extraStackFrames;
+ }
+
+ public SamlSecurityToken SamlToken { get; }
+
+ public SamlSecurityTokenHandler SamlSecurityTokenHandler { get; } = new SamlSecurityTokenHandler();
+
+ public bool IsValid { get; set; }
+
+ internal ValidationParameters ValidationParameters { get; }
+
+ internal SignatureValidationError? SignatureValidationError { get; set; }
+
+ internal int ExtraStackFrames { get; }
+ }
+ }
+}
+#nullable restore
diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.ValidateTokenAsyncTests.IssuerSigningKey.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.ValidateTokenAsyncTests.IssuerSigningKey.cs
index f34c777f4f..3598ad2c06 100644
--- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.ValidateTokenAsyncTests.IssuerSigningKey.cs
+++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.ValidateTokenAsyncTests.IssuerSigningKey.cs
@@ -131,7 +131,7 @@ static ValidationParameters CreateValidationParameters(
SecurityToken token,
ValidationParameters validationParameters,
BaseConfiguration? configuration,
- CallContext? callContext) =>
+ CallContext callContext) =>
{
// Set the signing key for validation
token.SigningKey = issuerSigingKey;