diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt index 748018b3ab..1fc631663c 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt @@ -21,6 +21,7 @@ Microsoft.IdentityModel.Tokens.Saml2.Saml2ValidationError.Saml2ValidationError(M Microsoft.IdentityModel.Tokens.Saml2.Saml2ValidationError.Saml2ValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType failureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Exception innerException) -> void override Microsoft.IdentityModel.Tokens.Saml.SamlValidationError.GetException() -> System.Exception override Microsoft.IdentityModel.Tokens.Saml2.Saml2ValidationError.GetException() -> System.Exception +static Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.StackFrames.IssuerSigningKeyValidationFailed -> System.Diagnostics.StackFrame static Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.StackFrames.IssuerValidationFailed -> System.Diagnostics.StackFrame static Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.StackFrames.SignatureValidationFailed -> System.Diagnostics.StackFrame static Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidateSignature(Microsoft.IdentityModel.Tokens.Saml.SamlSecurityToken samlToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.ValidationResult @@ -38,6 +39,7 @@ static Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.StackFrame static Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.StackFrames.AssertionConditionsValidationFailed -> System.Diagnostics.StackFrame static Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.StackFrames.AssertionNull -> System.Diagnostics.StackFrame static Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.StackFrames.AudienceValidationFailed -> System.Diagnostics.StackFrame +static Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.StackFrames.IssuerSigningKeyValidationFailed -> System.Diagnostics.StackFrame static Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.StackFrames.IssuerValidationFailed -> System.Diagnostics.StackFrame static Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.StackFrames.LifetimeValidationFailed -> System.Diagnostics.StackFrame static Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.StackFrames.OneTimeUseValidationFailed -> System.Diagnostics.StackFrame diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs index b5115646f3..7d45db493a 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs @@ -57,7 +57,7 @@ internal async Task> ValidateTokenAsync( StackFrames.TokenValidationParametersNull); } - var conditionsResult = ValidateConditions(samlToken, validationParameters, callContext); + ValidationResult conditionsResult = ValidateConditions(samlToken, validationParameters, callContext); if (!conditionsResult.IsValid) { @@ -65,7 +65,7 @@ internal async Task> ValidateTokenAsync( return conditionsResult.UnwrapError().AddStackFrame(StackFrames.AssertionConditionsValidationFailed); } - var issuerValidationResult = await validationParameters.IssuerValidatorAsync( + ValidationResult issuerValidationResult = await validationParameters.IssuerValidatorAsync( samlToken.Issuer, samlToken, validationParameters, @@ -78,13 +78,27 @@ internal async Task> ValidateTokenAsync( return issuerValidationResult.UnwrapError().AddStackFrame(StackFrames.IssuerValidationFailed); } - var signatureValidationResult = ValidateSignature(samlToken, validationParameters, callContext); + ValidationResult signatureValidationResult = ValidateSignature(samlToken, validationParameters, callContext); + if (!signatureValidationResult.IsValid) { StackFrames.SignatureValidationFailed ??= new StackFrame(true); return signatureValidationResult.UnwrapError().AddStackFrame(StackFrames.SignatureValidationFailed); } + ValidationResult issuerSigningKeyValidationResult = validationParameters.IssuerSigningKeyValidator( + samlToken.SigningKey, + samlToken, + validationParameters, + null, + callContext); + + if (!issuerSigningKeyValidationResult.IsValid) + { + StackFrames.IssuerSigningKeyValidationFailed ??= new StackFrame(true); + return issuerSigningKeyValidationResult.UnwrapError().AddStackFrame(StackFrames.IssuerSigningKeyValidationFailed); + } + return new ValidatedToken(samlToken, this, validationParameters); } diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.StackFrames.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.StackFrames.cs index faa6aad0a6..2108a5c662 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.StackFrames.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.StackFrames.cs @@ -14,6 +14,9 @@ internal static class StackFrames // Stack frames from ValidateTokenAsync using SecurityToken internal static StackFrame? TokenNull; internal static StackFrame? TokenValidationParametersNull; + internal static StackFrame? IssuerValidationFailed; + internal static StackFrame? IssuerSigningKeyValidationFailed; + internal static StackFrame? SignatureValidationFailed; // Stack frames from ValidateConditions internal static StackFrame? AudienceValidationFailed; @@ -22,9 +25,6 @@ internal static class StackFrames internal static StackFrame? AssertionConditionsValidationFailed; internal static StackFrame? LifetimeValidationFailed; internal static StackFrame? OneTimeUseValidationFailed; - - internal static StackFrame? IssuerValidationFailed; - internal static StackFrame? SignatureValidationFailed; } } } diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs index d806f6a453..2ff1a01def 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs @@ -89,6 +89,19 @@ internal async Task> ValidateTokenAsync( return signatureValidationResult.UnwrapError().AddStackFrame(StackFrames.SignatureValidationFailed); } + var issuerSigningKeyValidationResult = validationParameters.IssuerSigningKeyValidator( + samlToken.SigningKey, + samlToken, + validationParameters, + null, + callContext); + + if (!issuerSigningKeyValidationResult.IsValid) + { + StackFrames.IssuerSigningKeyValidationFailed ??= new StackFrame(true); + return issuerSigningKeyValidationResult.UnwrapError().AddStackFrame(StackFrames.IssuerSigningKeyValidationFailed); + } + return new ValidatedToken(samlToken, this, validationParameters); } diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.StackFrames.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.StackFrames.cs index 5638bf2f11..42c3ee2832 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.StackFrames.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.StackFrames.cs @@ -14,6 +14,7 @@ internal static class StackFrames // Stack frames from ValidateTokenAsync using SecurityToken internal static StackFrame? TokenNull; internal static StackFrame? TokenValidationParametersNull; + internal static StackFrame? IssuerSigningKeyValidationFailed; internal static StackFrame? IssuerValidationFailed; internal static StackFrame? SignatureValidationFailed; diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.IssuerSigningKey.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.IssuerSigningKey.cs new file mode 100644 index 0000000000..94d38d6151 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.IssuerSigningKey.cs @@ -0,0 +1,200 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils; +using Microsoft.IdentityModel.Tokens.Saml2; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml.Tests +{ + public partial class Saml2SecurityTokenHandlerTests + { + [Theory, MemberData(nameof(ValidateTokenAsync_IssuerSigningKey_TestCases), DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_IssuerSigningKeyComparison(ValidateTokenAsyncIssuerSigningKeyTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.ValidateTokenAsync_IssuerSigningKeyComparison", theoryData); + + Saml2SecurityTokenHandler saml2TokenHandler = new Saml2SecurityTokenHandler(); + + var saml2Token = CreateTokenForSignatureValidation(theoryData.SigningCredentials); + + // Validate the token using TokenValidationParameters + TokenValidationResult tokenValidationResult = + await saml2TokenHandler.ValidateTokenAsync(saml2Token.Assertion.CanonicalString, theoryData.TokenValidationParameters!); + + // Validate the token using ValidationParameters. + ValidationResult validationResult = + await saml2TokenHandler.ValidateTokenAsync( + saml2Token, + theoryData.ValidationParameters!, + theoryData.CallContext, + CancellationToken.None); + + // Ensure the validity of the results match the expected result. + if (tokenValidationResult.IsValid != validationResult.IsValid) + { + context.AddDiff($"tokenValidationResult.IsValid != validationResult.IsSuccess"); + theoryData.ExpectedExceptionValidationParameters!.ProcessException(validationResult.UnwrapError().GetException(), context); + theoryData.ExpectedException.ProcessException(tokenValidationResult.Exception, context); + } + else + { + if (tokenValidationResult.IsValid) + { + // Verify that the validated tokens from both paths match. + ValidatedToken validatedToken = validationResult.UnwrapResult(); + IdentityComparer.AreEqual(validatedToken.SecurityToken, tokenValidationResult.SecurityToken, context); + } + else + { + // Verify the exception provided by both paths match. + var tokenValidationResultException = tokenValidationResult.Exception; + theoryData.ExpectedException.ProcessException(tokenValidationResultException, context); + var validationResultException = validationResult.UnwrapError().GetException(); + theoryData.ExpectedExceptionValidationParameters!.ProcessException(validationResultException, context); + } + + TestUtilities.AssertFailIfErrors(context); + } + } + + public static TheoryData ValidateTokenAsync_IssuerSigningKey_TestCases + { + get + { + int currentYear = DateTime.UtcNow.Year; + // Mock time provider, 100 years in the future + TimeProvider futureTimeProvider = new MockTimeProvider(new DateTimeOffset(currentYear + 100, 1, 1, 0, 0, 0, new(0))); + // Mock time provider, 100 years in the past + TimeProvider pastTimeProvider = new MockTimeProvider(new DateTimeOffset(currentYear - 100, 9, 16, 0, 0, 0, new(0))); + + var theoryData = new TheoryData(); + + theoryData.Add(new ValidateTokenAsyncIssuerSigningKeyTheoryData("Valid_IssuerSigningKeyIsValid") + { + SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2, + TokenValidationParameters = CreateTokenValidationParameters(KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key), + ValidationParameters = CreateValidationParameters(KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key), + }); + + theoryData.Add(new ValidateTokenAsyncIssuerSigningKeyTheoryData("Invalid_IssuerSigningKeyIsExpired") + { + // Signing key is valid between September 2011 and December 2039 + // Mock time provider is set to 100 years in the future, after the key expired + SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2, + TokenValidationParameters = CreateTokenValidationParameters( + KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, futureTimeProvider), + ValidationParameters = CreateValidationParameters( + KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, futureTimeProvider), + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException("IDX10249:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidSigningKeyException("IDX10249:"), + }); + + theoryData.Add(new ValidateTokenAsyncIssuerSigningKeyTheoryData("Invalid_IssuerSigningKeyNotYetValid") + { + // Signing key is valid between September 2011 and December 2039 + // Mock time provider is set to 100 years in the past, before the key was valid. + SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2, + TokenValidationParameters = CreateTokenValidationParameters( + KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, pastTimeProvider), + ValidationParameters = CreateValidationParameters( + KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, pastTimeProvider), + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException("IDX10248:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidSigningKeyException("IDX10248:"), + }); + + theoryData.Add(new ValidateTokenAsyncIssuerSigningKeyTheoryData("Invalid_TokenValidationParametersAndValidationParametersAreNull") + { + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenArgumentNullException("IDX10000:"), + ExpectedIsValid = false, + }); + + return theoryData; + + static ValidationParameters CreateValidationParameters( + SecurityKey issuerSigingKey, TimeProvider? timeProvider = null) + { + ValidationParameters validationParameters = new ValidationParameters(); + validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; + validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation; + validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation; + validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation; + validationParameters.TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation; + validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation; + validationParameters.SignatureValidator = ( + SecurityToken token, + ValidationParameters validationParameters, + BaseConfiguration? configuration, + CallContext? callContext) => + { + // Set the signing key for validation + token.SigningKey = issuerSigingKey; + return issuerSigingKey; + }; + + if (issuerSigingKey is not null) + validationParameters.IssuerSigningKeys.Add(issuerSigingKey); + + if (timeProvider is not null) + validationParameters.TimeProvider = timeProvider; + + return validationParameters; + } + + static TokenValidationParameters CreateTokenValidationParameters( + SecurityKey? issuerSigningKey = null, TimeProvider? timeProvider = null) + { + var tokenValidationParameters = new TokenValidationParameters + { + ValidateAudience = false, + ValidateIssuer = false, + ValidateLifetime = false, + ValidateTokenReplay = false, + ValidateIssuerSigningKey = true, + RequireSignedTokens = true, + RequireAudience = false, + IssuerSigningKey = issuerSigningKey, + }; + + tokenValidationParameters.SignatureValidator = (token, tokenValidationParameters) => + { + // Set the signing key for validation + Saml2SecurityTokenHandler saml2SecurityTokenHandler = new Saml2SecurityTokenHandler(); + Saml2SecurityToken saml2SecurityToken = saml2SecurityTokenHandler.ReadSaml2Token(token); + saml2SecurityToken.SigningKey = issuerSigningKey; + + return saml2SecurityToken; + }; + + if (timeProvider is not null) + tokenValidationParameters.TimeProvider = timeProvider; + + return tokenValidationParameters; + } + } + } + + public class ValidateTokenAsyncIssuerSigningKeyTheoryData : TheoryDataBase + { + public ValidateTokenAsyncIssuerSigningKeyTheoryData(string testId) : base(testId) { } + + internal ExpectedException? ExpectedExceptionValidationParameters { get; set; } = ExpectedException.NoExceptionExpected; + + internal bool ExpectedIsValid { get; set; } = true; + + internal ValidationParameters? ValidationParameters { get; set; } + + internal TokenValidationParameters? TokenValidationParameters { get; set; } + + internal SigningCredentials? SigningCredentials { get; set; } + } + } +} +#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 new file mode 100644 index 0000000000..f34c777f4f --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.ValidateTokenAsyncTests.IssuerSigningKey.cs @@ -0,0 +1,199 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml.Tests +{ + public partial class SamlSecurityTokenHandlerTests + { + [Theory, MemberData(nameof(ValidateTokenAsync_IssuerSigningKey_TestCases), DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_IssuerSigningKeyComparison(ValidateTokenAsyncIssuerSigningKeyTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.ValidateTokenAsync_IssuerSigningKeyComparison", theoryData); + + SamlSecurityTokenHandler samlTokenHandler = new SamlSecurityTokenHandler(); + + var samlToken = CreateTokenForSignatureValidation(theoryData.SigningCredentials); + + // Validate the token using TokenValidationParameters + TokenValidationResult tokenValidationResult = + await samlTokenHandler.ValidateTokenAsync(samlToken.Assertion.CanonicalString, theoryData.TokenValidationParameters!); + + // Validate the token using ValidationParameters. + ValidationResult validationResult = + await samlTokenHandler.ValidateTokenAsync( + samlToken, + theoryData.ValidationParameters!, + theoryData.CallContext, + CancellationToken.None); + + // Ensure the validity of the results match the expected result. + if (tokenValidationResult.IsValid != validationResult.IsValid) + { + context.AddDiff($"tokenValidationResult.IsValid != validationResult.IsSuccess"); + theoryData.ExpectedExceptionValidationParameters!.ProcessException(validationResult.UnwrapError().GetException(), context); + theoryData.ExpectedException.ProcessException(tokenValidationResult.Exception, context); + } + else + { + if (tokenValidationResult.IsValid) + { + // Verify that the validated tokens from both paths match. + ValidatedToken validatedToken = validationResult.UnwrapResult(); + IdentityComparer.AreEqual(validatedToken.SecurityToken, tokenValidationResult.SecurityToken, context); + } + else + { + // Verify the exception provided by both paths match. + var tokenValidationResultException = tokenValidationResult.Exception; + theoryData.ExpectedException.ProcessException(tokenValidationResultException, context); + var validationResultException = validationResult.UnwrapError().GetException(); + theoryData.ExpectedExceptionValidationParameters!.ProcessException(validationResultException, context); + } + + TestUtilities.AssertFailIfErrors(context); + } + } + + public static TheoryData ValidateTokenAsync_IssuerSigningKey_TestCases + { + get + { + int currentYear = DateTime.UtcNow.Year; + // Mock time provider, 100 years in the future + TimeProvider futureTimeProvider = new MockTimeProvider(new DateTimeOffset(currentYear + 100, 1, 1, 0, 0, 0, new(0))); + // Mock time provider, 100 years in the past + TimeProvider pastTimeProvider = new MockTimeProvider(new DateTimeOffset(currentYear - 100, 9, 16, 0, 0, 0, new(0))); + + var theoryData = new TheoryData(); + + theoryData.Add(new ValidateTokenAsyncIssuerSigningKeyTheoryData("Valid_IssuerSigningKeyIsValid") + { + SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2, + TokenValidationParameters = CreateTokenValidationParameters(KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key), + ValidationParameters = CreateValidationParameters(KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key), + }); + + theoryData.Add(new ValidateTokenAsyncIssuerSigningKeyTheoryData("Invalid_IssuerSigningKeyIsExpired") + { + // Signing key is valid between September 2011 and December 2039 + // Mock time provider is set to 100 years in the future, after the key expired + SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2, + TokenValidationParameters = CreateTokenValidationParameters( + KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, futureTimeProvider), + ValidationParameters = CreateValidationParameters( + KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, futureTimeProvider), + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException("IDX10249:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidSigningKeyException("IDX10249:"), + }); + + theoryData.Add(new ValidateTokenAsyncIssuerSigningKeyTheoryData("Invalid_IssuerSigningKeyNotYetValid") + { + // Signing key is valid between September 2011 and December 2039 + // Mock time provider is set to 100 years in the past, before the key was valid. + SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2, + TokenValidationParameters = CreateTokenValidationParameters( + KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, pastTimeProvider), + ValidationParameters = CreateValidationParameters( + KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, pastTimeProvider), + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenInvalidSigningKeyException("IDX10248:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidSigningKeyException("IDX10248:"), + }); + + theoryData.Add(new ValidateTokenAsyncIssuerSigningKeyTheoryData("Invalid_TokenValidationParametersAndValidationParametersAreNull") + { + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenArgumentNullException("IDX10000:"), + ExpectedIsValid = false, + }); + + return theoryData; + + static ValidationParameters CreateValidationParameters( + SecurityKey issuerSigingKey, TimeProvider? timeProvider = null) + { + ValidationParameters validationParameters = new ValidationParameters(); + validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; + validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation; + validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation; + validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation; + validationParameters.TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation; + validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation; + validationParameters.SignatureValidator = ( + SecurityToken token, + ValidationParameters validationParameters, + BaseConfiguration? configuration, + CallContext? callContext) => + { + // Set the signing key for validation + token.SigningKey = issuerSigingKey; + return issuerSigingKey; + }; + + if (issuerSigingKey is not null) + validationParameters.IssuerSigningKeys.Add(issuerSigingKey); + + if (timeProvider is not null) + validationParameters.TimeProvider = timeProvider; + + return validationParameters; + } + + static TokenValidationParameters CreateTokenValidationParameters( + SecurityKey? issuerSigningKey = null, TimeProvider? timeProvider = null) + { + var tokenValidationParameters = new TokenValidationParameters + { + ValidateAudience = false, + ValidateIssuer = false, + ValidateLifetime = false, + ValidateTokenReplay = false, + ValidateIssuerSigningKey = true, + RequireSignedTokens = true, + RequireAudience = false, + IssuerSigningKey = issuerSigningKey, + }; + + tokenValidationParameters.SignatureValidator = (token, tokenValidationParameters) => + { + // Set the signing key for validation + SamlSecurityTokenHandler samlSecurityTokenHandler = new SamlSecurityTokenHandler(); + SamlSecurityToken samlSecurityToken = samlSecurityTokenHandler.ReadSamlToken(token); + samlSecurityToken.SigningKey = issuerSigningKey; + + return samlSecurityToken; + }; + + if (timeProvider is not null) + tokenValidationParameters.TimeProvider = timeProvider; + + return tokenValidationParameters; + } + } + } + + public class ValidateTokenAsyncIssuerSigningKeyTheoryData : TheoryDataBase + { + public ValidateTokenAsyncIssuerSigningKeyTheoryData(string testId) : base(testId) { } + + internal ExpectedException? ExpectedExceptionValidationParameters { get; set; } = ExpectedException.NoExceptionExpected; + + internal bool ExpectedIsValid { get; set; } = true; + + internal ValidationParameters? ValidationParameters { get; set; } + + internal TokenValidationParameters? TokenValidationParameters { get; set; } + + internal SigningCredentials? SigningCredentials { get; set; } + } + } +} +#nullable restore