-
Notifications
You must be signed in to change notification settings - Fork 417
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SAML and SAML2 new model validation: Algorithm (#2984)
* Added XmlValidationError. Added ValidationError property to XmlValidationException to provide custom stack traces * Added alternative versions using ValidationParameters to XML signature validations * Added XmlValidationFailure to ValidationFailureType * Added refactored ValidateSignature method to SamlSecurityTokenHandler. Updated ValidateTokenAsync to call ValidateSignature. * Added tests to compare signature validation between the legacy and new path * Re-added API lost in merge to InternalAPI.Unshipped.txt * Migrated refactored ValidateSignature from SamlSecurityTokenHandler to Saml2SecurityTokenHandler * Updated Saml2SecurityTokenHandler's ValidateTokenAsync to validate signatures * Added tests * Update src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateSignature.cs Co-authored-by: msbw2 <[email protected]> * Addressed PR feedback in both SAML and SAML2 signature validations to keep the alignment * Optimised signature validation in SAML and SAML2 for the expected most common scenario * Removed debug information * Added tests for SAML and SAML2 algorithm validation using the new validation model --------- Co-authored-by: msbw2 <[email protected]>
- Loading branch information
Showing
2 changed files
with
401 additions
and
0 deletions.
There are no files selected for viewing
201 changes: 201 additions & 0 deletions
201
...del.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.Algorithm.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.IdentityModel.TestUtils; | ||
using Microsoft.IdentityModel.Tokens.Saml2; | ||
using Xunit; | ||
|
||
namespace Microsoft.IdentityModel.Tokens.Saml.Tests | ||
{ | ||
#nullable enable | ||
public partial class Saml2SecurityTokenHandlerTests | ||
{ | ||
[Theory, MemberData(nameof(ValidateTokenAsync_Algorithm_TestCases), DisableDiscoveryEnumeration = true)] | ||
public async Task ValidateTokenAsync_AlgorithmComparison(ValidateTokenAsyncAlgorithmTheoryData theoryData) | ||
{ | ||
var context = TestUtilities.WriteHeader($"{this}.ValidateTokenAsync_AlgorithmComparison", theoryData); | ||
|
||
Saml2SecurityTokenHandler saml2TokenHandler = new Saml2SecurityTokenHandler(); | ||
|
||
Saml2SecurityToken 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<ValidatedToken> 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; | ||
var validationResultException = validationResult.UnwrapError().GetException(); | ||
|
||
if (theoryData.TestId == "Invalid_TokenSignedWithDifferentKey_KeyIdPresent_TryAllKeysFalse") | ||
Console.WriteLine($"tokenValidationResultException: {tokenValidationResultException}"); | ||
|
||
theoryData.ExpectedException.ProcessException(tokenValidationResult.Exception, context); | ||
theoryData.ExpectedExceptionValidationParameters!.ProcessException(validationResult.UnwrapError().GetException(), context); | ||
} | ||
|
||
TestUtilities.AssertFailIfErrors(context); | ||
} | ||
} | ||
|
||
public static TheoryData<ValidateTokenAsyncAlgorithmTheoryData> ValidateTokenAsync_Algorithm_TestCases | ||
{ | ||
get | ||
{ | ||
var theoryData = new TheoryData<ValidateTokenAsyncAlgorithmTheoryData>(); | ||
|
||
theoryData.Add(new ValidateTokenAsyncAlgorithmTheoryData("Valid_AlgorithmIsValid") | ||
{ | ||
SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2, | ||
TokenValidationParameters = CreateTokenValidationParameters( | ||
KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, | ||
validAlgorithms: [SecurityAlgorithms.RsaSha256Signature]), | ||
ValidationParameters = CreateValidationParameters( | ||
KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, | ||
validAlgorithms: [SecurityAlgorithms.RsaSha256Signature]), | ||
}); | ||
|
||
theoryData.Add(new ValidateTokenAsyncAlgorithmTheoryData("Valid_ValidAlgorithmsIsNull") | ||
{ | ||
SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2, | ||
TokenValidationParameters = CreateTokenValidationParameters( | ||
KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, | ||
validAlgorithms: null), | ||
ValidationParameters = CreateValidationParameters( | ||
KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, | ||
validAlgorithms: null), | ||
}); | ||
|
||
theoryData.Add(new ValidateTokenAsyncAlgorithmTheoryData("Valid_ValidAlgorithmsIsEmptyList") | ||
{ | ||
SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2, | ||
TokenValidationParameters = CreateTokenValidationParameters( | ||
KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, validAlgorithms: []), | ||
ValidationParameters = CreateValidationParameters( | ||
KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, validAlgorithms: []), | ||
}); | ||
|
||
theoryData.Add(new ValidateTokenAsyncAlgorithmTheoryData("Invalid_TokenIsSignedWithAnInvalidAlgorithm_TryAllKeysFalse") | ||
{ | ||
// Token is signed with HmacSha256 but only sha256 is considered valid for this test's purposes | ||
SigningCredentials = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2, | ||
TokenValidationParameters = CreateTokenValidationParameters( | ||
KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key, | ||
validAlgorithms: [SecurityAlgorithms.Sha256], | ||
tryAllKeys: false), | ||
ValidationParameters = CreateValidationParameters( | ||
KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key, | ||
validAlgorithms: [SecurityAlgorithms.Sha256], | ||
tryAllKeys: false), | ||
ExpectedIsValid = false, | ||
ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), | ||
ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), | ||
}); | ||
|
||
theoryData.Add(new ValidateTokenAsyncAlgorithmTheoryData("Invalid_TokenIsSignedWithAnInvalidAlgorithm_TryAllKeysTrue") | ||
{ | ||
// Token is signed with HmacSha256 but only sha256 is considered valid for this test's purposes | ||
SigningCredentials = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2, | ||
TokenValidationParameters = CreateTokenValidationParameters( | ||
KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key, | ||
validAlgorithms: [SecurityAlgorithms.Sha256], | ||
tryAllKeys: true), | ||
ValidationParameters = CreateValidationParameters( | ||
KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key, | ||
validAlgorithms: [SecurityAlgorithms.Sha256], | ||
tryAllKeys: true), | ||
ExpectedIsValid = false, | ||
ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10512:"), | ||
ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10512:"), | ||
}); | ||
|
||
return theoryData; | ||
|
||
static ValidationParameters CreateValidationParameters( | ||
SecurityKey? signingKey = null, List<string>? validAlgorithms = null, bool tryAllKeys = false) | ||
{ | ||
ValidationParameters validationParameters = new ValidationParameters(); | ||
|
||
if (signingKey is not null) | ||
validationParameters.IssuerSigningKeys.Add(signingKey); | ||
|
||
validationParameters.ValidAlgorithms = validAlgorithms; | ||
|
||
validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; | ||
validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation; | ||
validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation; | ||
validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation; | ||
validationParameters.TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation; | ||
validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation; | ||
validationParameters.TryAllIssuerSigningKeys = tryAllKeys; | ||
|
||
return validationParameters; | ||
} | ||
|
||
static TokenValidationParameters CreateTokenValidationParameters( | ||
SecurityKey? signingKey = null, List<string>? validAlgorithms = null, bool tryAllKeys = false) | ||
{ | ||
return new TokenValidationParameters | ||
{ | ||
ValidateAudience = false, | ||
ValidateIssuer = false, | ||
ValidateLifetime = false, | ||
ValidateTokenReplay = false, | ||
ValidateIssuerSigningKey = false, | ||
RequireSignedTokens = true, | ||
RequireAudience = false, | ||
IssuerSigningKey = signingKey, | ||
ValidAlgorithms = validAlgorithms, | ||
TryAllIssuerSigningKeys = tryAllKeys, | ||
}; | ||
} | ||
} | ||
} | ||
|
||
public class ValidateTokenAsyncAlgorithmTheoryData : TheoryDataBase | ||
{ | ||
public ValidateTokenAsyncAlgorithmTheoryData(string testId) : base(testId) { } | ||
|
||
internal ExpectedException? ExpectedExceptionValidationParameters { get; set; } = ExpectedException.NoExceptionExpected; | ||
|
||
internal SigningCredentials? SigningCredentials { get; set; } = null; | ||
|
||
internal bool ExpectedIsValid { get; set; } = true; | ||
|
||
internal ValidationParameters? ValidationParameters { get; set; } | ||
|
||
internal TokenValidationParameters? TokenValidationParameters { get; set; } | ||
} | ||
|
||
} | ||
} | ||
#nullable restore |
Oops, something went wrong.