diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index cfef45dc72..16328cc5d0 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -24,8 +24,13 @@ jobs: build: runs-on: windows-latest continue-on-error: false + name: "Build and run unit tests" steps: + - name: Set git core.longpaths flag + run: | + git config --system core.longpaths true + - name: Checkout repository uses: actions/checkout@v4.1.1 @@ -36,7 +41,7 @@ jobs: - name: Strong name bypass run: | - regedit /s .\build\strongNameBypass.reg + regedit /s .\build\strongNameBypass.reg - name: Run the tests run: dotnet test Wilson.sln --collect:"XPlat Code Coverage" --settings:./build/CodeCoverage.runsettings @@ -50,21 +55,22 @@ jobs: shell: bash run: | cat CodeCoverage/SummaryGithub.md >> $GITHUB_STEP_SUMMARY - echo "COMMENT_CONTENT_ENV_VAR<> $GITHUB_ENV - echo $(cat CodeCoverage/SummaryGithub.md) >> $GITHUB_ENV - echo "EOF" >> $GITHUB_ENV + # Temporarily disable commenting the coverage report + # echo "COMMENT_CONTENT_ENV_VAR<> $GITHUB_ENV + # echo $(cat CodeCoverage/SummaryGithub.md) >> $GITHUB_ENV + # echo "EOF" >> $GITHUB_ENV - - name: Comment coverage in PR - uses: actions/github-script@v7 - id: comment - with: - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: process.env.COMMENT_CONTENT_ENV_VAR - }) + # - name: Comment coverage in PR + # uses: actions/github-script@v7 + # id: comment + # with: + # script: | + # github.rest.issues.createComment({ + # issue_number: context.issue.number, + # owner: context.repo.owner, + # repo: context.repo.repo, + # body: process.env.COMMENT_CONTENT_ENV_VAR + # }) # Run baseline package validation - name: Pack diff --git a/CHANGELOG.md b/CHANGELOG.md index f1c14e26d7..b994ce1945 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ See the [releases](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) for details on bug fixes and added features. +8.3.0 +===== + +## New features + +### Work related to redesign of IdentityModel's token validation logic [#2711](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2711) +* SAML and SAML2 new model validation: Token Replay. See [#2994](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/2994) +* Extensibility tests: Token Type - JWT ([#3030](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3030)), Issuer - SAML and SAML2 ([#3026](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3026)), Algorithm and Signature - JWT, SAML and SAML2 ([#3034](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3034)), Token Replay - JWT, SAML and SAML2 ([#3032](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3032)), Issuer signing key - JWT, SAML and SAML2 ([#3029](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/302)) +* Avoid code duplication in extensibility testing. See [#3041](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3041) +* Extensibility Testing: Refactor. See https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3011 +* Remove duplicate code in extensibility tests. See https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3044 + +## Bug fixes +* Fix bug with AadIssuerValidator. See https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3042 +* Fixed SignedHttpRequest flaky test. See [#3037](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3037) + +## Fundamentals +* Install all .NET versions in pipeline to fix run tests task. See https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3018 +* Changelog for 8.2.1. See https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3009 +* Remove unnecessary AoT test project. See in https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3045 +* Fix powershell script for nuget update. See https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3046 +* Update to next version. See https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3010 +* Disable Coverage PR comments. See https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3048 +* Updates GitHub Action to support long paths, See https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3049 +* Stack parameters to improve reading of code. by @brentschmaltz in https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3031 + +## New Contributors +* @ssmelov made their first contribution in https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3042 + 8.2.1 ===== ### New features diff --git a/Wilson.sln b/Wilson.sln index 8d3bd81589..5d49f422d7 100644 --- a/Wilson.sln +++ b/Wilson.sln @@ -92,8 +92,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.Abs EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.AotCompatibility.TestApp", "test\Microsoft.IdentityModel.AotCompatibility.TestApp\Microsoft.IdentityModel.AotCompatibility.TestApp.csproj", "{8105289F-3D54-4054-9738-5985F3B6CF2C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.AotCompatibility.Tests", "test\Microsoft.IdentityModel.AotCompatibility.Tests\Microsoft.IdentityModel.AotCompatibility.Tests.csproj", "{CD0EEF56-7221-4420-8181-48EE82E91306}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.Benchmarks", "benchmark\Microsoft.IdentityModel.Benchmarks\Microsoft.IdentityModel.Benchmarks.csproj", "{F1BB31E4-8865-4425-8BD4-94F1815C16E0}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{BC99A01F-1C6E-4994-8991-4919A9B096E1}" @@ -225,10 +223,6 @@ Global {8105289F-3D54-4054-9738-5985F3B6CF2C}.Debug|Any CPU.Build.0 = Debug|Any CPU {8105289F-3D54-4054-9738-5985F3B6CF2C}.Release|Any CPU.ActiveCfg = Release|Any CPU {8105289F-3D54-4054-9738-5985F3B6CF2C}.Release|Any CPU.Build.0 = Release|Any CPU - {CD0EEF56-7221-4420-8181-48EE82E91306}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CD0EEF56-7221-4420-8181-48EE82E91306}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CD0EEF56-7221-4420-8181-48EE82E91306}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CD0EEF56-7221-4420-8181-48EE82E91306}.Release|Any CPU.Build.0 = Release|Any CPU {F1BB31E4-8865-4425-8BD4-94F1815C16E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F1BB31E4-8865-4425-8BD4-94F1815C16E0}.Debug|Any CPU.Build.0 = Debug|Any CPU {F1BB31E4-8865-4425-8BD4-94F1815C16E0}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -267,7 +261,6 @@ Global {C1F5A997-FAA9-45E5-8D28-D4E92D4A034D} = {EB14B99B-2255-45BC-BF14-E488DCD4A4BA} {EF9A4431-6D2C-4DD1-BF6B-6F2CC619DEE1} = {8905D2E3-4499-4A86-BF3E-F098F228DD59} {8105289F-3D54-4054-9738-5985F3B6CF2C} = {8905D2E3-4499-4A86-BF3E-F098F228DD59} - {CD0EEF56-7221-4420-8181-48EE82E91306} = {8905D2E3-4499-4A86-BF3E-F098F228DD59} {F1BB31E4-8865-4425-8BD4-94F1815C16E0} = {2F79F3C4-F4E3-46DD-8B34-8EF403A6F7F5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/build/dependencies.props b/build/dependencies.props index 850005c2c8..e8cf62de7b 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -5,6 +5,7 @@ 3.3.4 8.0.1 4.5.0 + 8.0.2 1.0.0 2.0.3 13.0.3 diff --git a/build/template-Build-run-tests-sign.yml b/build/template-Build-run-tests-sign.yml index 4c79d9baa9..f9e0b1d5e1 100644 --- a/build/template-Build-run-tests-sign.yml +++ b/build/template-Build-run-tests-sign.yml @@ -53,7 +53,10 @@ steps: dotnet nuget add source https://identitydivision.pkgs.visualstudio.com/_packaging/IDDP/nuget/v3/index.json -n IDDP dotnet nuget list source } + workingDirectory: '$(Build.SourcesDirectory)\$(WilsonSourceDirectory)' displayName: 'Remove external "NuGet" Source and add "IDDP artifacts" as a NuGet Source, if needed.' + env: + DOTNET_NOLOGO: 1 - task: DotNetCoreCLI@2 displayName: Build diff --git a/build/version.props b/build/version.props index 26edcca848..7af60d8267 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ - 8.2.1 + 8.3.1 preview-$([System.DateTime]::Now.AddYears(-2019).Year)$([System.DateTime]::Now.ToString("MMddHHmmss")) 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.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs index 18fa21fcf9..1bb3e578ca 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs @@ -255,25 +255,57 @@ private async ValueTask> ValidateJWSAsync( DateTime? expires = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Exp) ? jsonWebToken.ValidTo : null; DateTime? notBefore = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Nbf) ? jsonWebToken.ValidFrom : null; - ValidationResult lifetimeValidationResult = validationParameters.LifetimeValidator( - notBefore, expires, jsonWebToken, validationParameters, callContext); + ValidationResult lifetimeValidationResult; - if (!lifetimeValidationResult.IsValid) + try { - StackFrame lifetimeValidationFailureStackFrame = StackFrames.LifetimeValidationFailed ??= new StackFrame(true); - return lifetimeValidationResult.UnwrapError().AddStackFrame(lifetimeValidationFailureStackFrame); + lifetimeValidationResult = validationParameters.LifetimeValidator( + notBefore, expires, jsonWebToken, validationParameters, callContext); + + if (!lifetimeValidationResult.IsValid) + return lifetimeValidationResult.UnwrapError().AddCurrentStackFrame(); + } +#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 LifetimeValidationError( + new MessageDetail(TokenLogMessages.IDX10271), + ValidationFailureType.LifetimeValidatorThrew, + typeof(SecurityTokenInvalidLifetimeException), + ValidationError.GetCurrentStackFrame(), + notBefore, + expires, + ex); } if (jsonWebToken.Audiences is not IList tokenAudiences) tokenAudiences = jsonWebToken.Audiences.ToList(); - ValidationResult audienceValidationResult = validationParameters.AudienceValidator( - tokenAudiences, jsonWebToken, validationParameters, callContext); + ValidationResult audienceValidationResult; + try + { + audienceValidationResult = validationParameters.AudienceValidator( + tokenAudiences, jsonWebToken, validationParameters, callContext); - if (!audienceValidationResult.IsValid) + if (!audienceValidationResult.IsValid) + { + StackFrame audienceValidationFailureStackFrame = StackFrames.AudienceValidationFailed ??= new StackFrame(true); + return audienceValidationResult.UnwrapError().AddStackFrame(audienceValidationFailureStackFrame); + } + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { - StackFrame audienceValidationFailureStackFrame = StackFrames.AudienceValidationFailed ??= new StackFrame(true); - return audienceValidationResult.UnwrapError().AddStackFrame(audienceValidationFailureStackFrame); + return new AudienceValidationError( + new MessageDetail(TokenLogMessages.IDX10270), + ValidationFailureType.AudienceValidatorThrew, + typeof(SecurityTokenInvalidAudienceException), + ValidationError.GetCurrentStackFrame(), + tokenAudiences, + null, + ex); } ValidationResult issuerValidationResult; @@ -301,13 +333,27 @@ private async ValueTask> ValidateJWSAsync( ex); } - ValidationResult replayValidationResult = validationParameters.TokenReplayValidator( - expires, jsonWebToken.EncodedToken, validationParameters, callContext); + ValidationResult replayValidationResult; - if (!replayValidationResult.IsValid) + try { - StackFrame replayValidationFailureStackFrame = StackFrames.ReplayValidationFailed ??= new StackFrame(true); - return replayValidationResult.UnwrapError().AddStackFrame(replayValidationFailureStackFrame); + replayValidationResult = validationParameters.TokenReplayValidator( + expires, jsonWebToken.EncodedToken, validationParameters, callContext); + + if (!replayValidationResult.IsValid) + return replayValidationResult.UnwrapError().AddCurrentStackFrame(); + } +#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 TokenReplayValidationError( + new MessageDetail(TokenLogMessages.IDX10276), + ValidationFailureType.TokenReplayValidatorThrew, + typeof(SecurityTokenReplayDetectedException), + ValidationError.GetCurrentStackFrame(), + expires, + ex); } ValidationResult? actorValidationResult = null; @@ -336,12 +382,28 @@ await ValidateJWSAsync(actorToken, actorParameters, configuration, callContext, actorValidationResult = innerActorValidationResult; } - ValidationResult typeValidationResult = validationParameters.TokenTypeValidator( - jsonWebToken.Typ, jsonWebToken, validationParameters, callContext); - if (!typeValidationResult.IsValid) + ValidationResult typeValidationResult; + + try + { + typeValidationResult = validationParameters.TokenTypeValidator( + jsonWebToken.Typ, jsonWebToken, validationParameters, callContext); + + if (!typeValidationResult.IsValid) + return typeValidationResult.UnwrapError().AddCurrentStackFrame(); + } + +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { - StackFrame typeValidationFailureStackFrame = StackFrames.TypeValidationFailed ??= new StackFrame(true); - return typeValidationResult.UnwrapError().AddStackFrame(typeValidationFailureStackFrame); + return new TokenTypeValidationError( + new MessageDetail(TokenLogMessages.IDX10275), + ValidationFailureType.TokenTypeValidatorThrew, + typeof(SecurityTokenInvalidTypeException), + ValidationError.GetCurrentStackFrame(), + jsonWebToken.Typ, + ex); } // The signature validation delegate is yet to be migrated to ValidationParameters. @@ -353,13 +415,27 @@ await ValidateJWSAsync(actorToken, actorParameters, configuration, callContext, return signatureValidationResult.UnwrapError().AddStackFrame(signatureValidationFailureStackFrame); } - ValidationResult issuerSigningKeyValidationResult = - validationParameters.IssuerSigningKeyValidator( + ValidationResult issuerSigningKeyValidationResult; + + try + { + issuerSigningKeyValidationResult = validationParameters.IssuerSigningKeyValidator( jsonWebToken.SigningKey, jsonWebToken, validationParameters, configuration, callContext); - if (!issuerSigningKeyValidationResult.IsValid) + + if (!issuerSigningKeyValidationResult.IsValid) + return issuerSigningKeyValidationResult.UnwrapError().AddCurrentStackFrame(); + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { - StackFrame issuerSigningKeyValidationFailureStackFrame = StackFrames.IssuerSigningKeyValidationFailed ??= new StackFrame(true); - return issuerSigningKeyValidationResult.UnwrapError().AddStackFrame(issuerSigningKeyValidationFailureStackFrame); + return new IssuerSigningKeyValidationError( + new MessageDetail(TokenLogMessages.IDX10274), + ValidationFailureType.IssuerSigningKeyValidatorThrew, + typeof(SecurityTokenInvalidSigningKeyException), + ValidationError.GetCurrentStackFrame(), + jsonWebToken.SigningKey, + ex); } return new ValidatedToken(jsonWebToken, this, validationParameters) diff --git a/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs b/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs index 9a7c5cfc35..d099239d82 100644 --- a/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs +++ b/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs @@ -256,7 +256,7 @@ public virtual async Task GetConfigurationAsync(CancellationToken cancel) /// /// This should be called when the configuration needs to be updated either from RequestRefresh or AutomaticRefresh /// The Caller should first check the state checking state using: - /// if (Interlocked.CompareExchange(ref _configurationRetrieverState, ConfigurationRetrieverIdle, ConfigurationRetrieverRunning) != ConfigurationRetrieverRunning). + /// if (Interlocked.CompareExchange(ref _configurationRetrieverState, ConfigurationRetrieverRunning, ConfigurationRetrieverIdle) == ConfigurationRetrieverIdle). /// private void UpdateCurrentConfiguration() { diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt index 40f6b57bad..55944f8e39 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.Tokens.Saml/InternalAPI.Unshipped.txt @@ -1,6 +1,7 @@ const Microsoft.IdentityModel.Tokens.Saml.LogMessages.IDX11402 = "IDX11402: Unable to read SamlSecurityToken. Exception thrown: '{0}'." -> string const Microsoft.IdentityModel.Tokens.Saml2.LogMessages.IDX13003 = "IDX13003: Unable to read Saml2SecurityToken. Exception thrown: '{0}'." -> string Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.StackFrames +Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.CreateClaimsIdentity(Microsoft.IdentityModel.Tokens.Saml.SamlSecurityToken samlToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, string issuer) -> System.Security.Claims.ClaimsIdentity Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions.ValidatedAudience.get -> string Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions.ValidatedAudience.set -> void @@ -8,18 +9,20 @@ Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions.ValidatedConditions(string ValidatedAudience, Microsoft.IdentityModel.Tokens.ValidatedLifetime? ValidatedLifetime) -> void Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions.ValidatedLifetime.get -> Microsoft.IdentityModel.Tokens.ValidatedLifetime? Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidatedConditions.ValidatedLifetime.set -> void -Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.Saml.SamlSecurityToken samlToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> +Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidateTokenAsync(string token, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> Microsoft.IdentityModel.Tokens.Saml.SamlValidationError Microsoft.IdentityModel.Tokens.Saml.SamlValidationError.SamlValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Exception innerException = null) -> void +Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.CreateClaimsIdentity(Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken samlToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, string issuer) -> System.Security.Claims.ClaimsIdentity Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.StackFrames -Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken samlToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> +Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateTokenAsync(string token, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> Microsoft.IdentityModel.Tokens.Saml2.Saml2ValidationError Microsoft.IdentityModel.Tokens.Saml2.Saml2ValidationError.Saml2ValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Exception innerException = null) -> void +override Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.CreateClaimsIdentityInternal(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, string issuer) -> System.Security.Claims.ClaimsIdentity override Microsoft.IdentityModel.Tokens.Saml.SamlValidationError.GetException() -> System.Exception +override Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.CreateClaimsIdentityInternal(Microsoft.IdentityModel.Tokens.SecurityToken securityToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, string issuer) -> System.Security.Claims.ClaimsIdentity 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 @@ -45,6 +48,8 @@ static Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.StackFrame static Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.StackFrames.TokenNull -> System.Diagnostics.StackFrame static Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.StackFrames.TokenValidationParametersNull -> System.Diagnostics.StackFrame static Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateSignature(Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken samlToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.ValidationResult -virtual Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ReadSaml2Token(string token, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.ValidationResult +virtual Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ProcessStatements(Microsoft.IdentityModel.Tokens.Saml.SamlSecurityToken samlToken, string issuer, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters) -> System.Collections.Generic.IEnumerable virtual Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ReadSamlToken(string token, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.ValidationResult virtual Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler.ValidateConditions(Microsoft.IdentityModel.Tokens.Saml.SamlSecurityToken samlToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.ValidationResult +virtual Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ReadSaml2Token(string token, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.ValidationResult +virtual Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateOneTimeUseCondition(Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken samlToken, Microsoft.IdentityModel.Tokens.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext) -> Microsoft.IdentityModel.Tokens.ValidationError diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/InternalsVisibleTo.cs b/src/Microsoft.IdentityModel.Tokens.Saml/InternalsVisibleTo.cs new file mode 100644 index 0000000000..6918991a28 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens.Saml/InternalsVisibleTo.cs @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.TestUtils, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] + diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ClaimsIdentity.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ClaimsIdentity.cs new file mode 100644 index 0000000000..1e1cef4c4d --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ClaimsIdentity.cs @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using Microsoft.IdentityModel.Logging; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml +{ + /// + /// A designed for creating and validating Saml Tokens. See: http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf + /// + public partial class SamlSecurityTokenHandler : SecurityTokenHandler + { + internal override ClaimsIdentity CreateClaimsIdentityInternal(SecurityToken securityToken, ValidationParameters validationParameters, string issuer) + { + return CreateClaimsIdentity((SamlSecurityToken)securityToken, validationParameters, issuer); + } + + internal ClaimsIdentity CreateClaimsIdentity(SamlSecurityToken samlToken, ValidationParameters validationParameters, string issuer) + { + if (samlToken == null) + throw LogHelper.LogArgumentNullException(nameof(samlToken)); + + if (samlToken.Assertion == null) + throw LogHelper.LogArgumentNullException(LogMessages.IDX11110); + + var actualIssuer = issuer; + if (string.IsNullOrWhiteSpace(issuer)) + actualIssuer = ClaimsIdentity.DefaultIssuer; + + IEnumerable identities = ProcessStatements( + samlToken, + actualIssuer, + validationParameters); + + return identities.First(); + } + + /// + /// Processes all statements to generate claims. + /// + /// A that will be used to create the claims. + /// The issuer. + /// The to be used for validating the token. + /// A containing the claims from the . + /// if the statement is not a . + internal virtual IEnumerable ProcessStatements(SamlSecurityToken samlToken, string issuer, ValidationParameters validationParameters) + { + if (samlToken == null) + throw LogHelper.LogArgumentNullException(nameof(samlToken)); + + if (validationParameters == null) + throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + + var identityDict = new Dictionary(SamlSubjectEqualityComparer); + foreach (SamlStatement? item in samlToken.Assertion.Statements) + { + if (item is not SamlSubjectStatement statement) + throw LogHelper.LogExceptionMessage(new SamlSecurityTokenException(LogMessages.IDX11515)); + + if (!identityDict.TryGetValue(statement.Subject, out ClaimsIdentity? identity)) + { + identity = validationParameters.CreateClaimsIdentity(samlToken, issuer); + ProcessSubject(statement.Subject, identity, issuer); + identityDict.Add(statement.Subject, identity); + } + + if (statement is SamlAttributeStatement attrStatement) + ProcessAttributeStatement(attrStatement, identity, issuer); + else if (statement is SamlAuthenticationStatement authnStatement) + ProcessAuthenticationStatement(authnStatement, identity, issuer); + else if (statement is SamlAuthorizationDecisionStatement authzStatement) + ProcessAuthorizationDecisionStatement(authzStatement, identity, issuer); + else + ProcessCustomSubjectStatement(statement, identity, issuer); + } + + return identityDict.Values; + } + } +} +#nullable restore 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/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs index 7d45db493a..2e073084f8 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.Diagnostics; using System.Linq; using System.Threading; @@ -34,21 +35,34 @@ internal async Task> ValidateTokenAsync( } internal async Task> ValidateTokenAsync( - SamlSecurityToken samlToken, + SecurityToken securityToken, ValidationParameters validationParameters, CallContext callContext, #pragma warning disable CA1801 // Review unused parameters CancellationToken cancellationToken) #pragma warning restore CA1801 // Review unused parameters { - if (samlToken is null) + if (securityToken is null) { StackFrames.TokenNull ??= new StackFrame(true); return ValidationError.NullParameter( - nameof(samlToken), + nameof(securityToken), StackFrames.TokenNull); } + if (securityToken is not SamlSecurityToken samlToken) + { + return new ValidationError( + new MessageDetail( + LogMessages.IDX11400, + this, + typeof(SamlSecurityToken), + securityToken.GetType()), + ValidationFailureType.InvalidSecurityToken, + typeof(SecurityTokenArgumentException), + ValidationError.GetCurrentStackFrame()); + } + if (validationParameters is null) { StackFrames.TokenValidationParametersNull ??= new StackFrame(true); @@ -60,22 +74,62 @@ internal async Task> ValidateTokenAsync( ValidationResult conditionsResult = ValidateConditions(samlToken, validationParameters, callContext); if (!conditionsResult.IsValid) + return conditionsResult.UnwrapError().AddCurrentStackFrame(); + + ValidationResult issuerValidationResult; + + try { - StackFrames.AssertionConditionsValidationFailed ??= new StackFrame(true); - return conditionsResult.UnwrapError().AddStackFrame(StackFrames.AssertionConditionsValidationFailed); + issuerValidationResult = await validationParameters.IssuerValidatorAsync( + samlToken.Issuer, + samlToken, + validationParameters, + callContext, + cancellationToken).ConfigureAwait(false); + + if (!issuerValidationResult.IsValid) + return issuerValidationResult.UnwrapError().AddCurrentStackFrame(); + } +#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 IssuerValidationError( + new MessageDetail(Tokens.LogMessages.IDX10269), + ValidationFailureType.IssuerValidatorThrew, + typeof(SecurityTokenInvalidIssuerException), + ValidationError.GetCurrentStackFrame(), + samlToken.Issuer, + ex); } - ValidationResult issuerValidationResult = await validationParameters.IssuerValidatorAsync( - samlToken.Issuer, - samlToken, - validationParameters, - callContext, - cancellationToken).ConfigureAwait(false); + ValidationResult? tokenReplayValidationResult = null; - if (!issuerValidationResult.IsValid) + if (samlToken.Assertion.Conditions is not null) { - StackFrames.IssuerValidationFailed ??= new StackFrame(true); - return issuerValidationResult.UnwrapError().AddStackFrame(StackFrames.IssuerValidationFailed); + try + { + tokenReplayValidationResult = validationParameters.TokenReplayValidator( + samlToken.Assertion.Conditions.NotOnOrAfter, + samlToken.Assertion.CanonicalString, + validationParameters, + callContext); + + if (!tokenReplayValidationResult.Value.IsValid) + return tokenReplayValidationResult.Value.UnwrapError().AddCurrentStackFrame(); + } +#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 TokenReplayValidationError( + new MessageDetail(Tokens.LogMessages.IDX10276), + ValidationFailureType.TokenReplayValidatorThrew, + typeof(SecurityTokenReplayDetectedException), + ValidationError.GetCurrentStackFrame(), + samlToken.Assertion.Conditions.NotOnOrAfter, + ex); + } } ValidationResult signatureValidationResult = ValidateSignature(samlToken, validationParameters, callContext); @@ -86,20 +140,42 @@ internal async Task> ValidateTokenAsync( return signatureValidationResult.UnwrapError().AddStackFrame(StackFrames.SignatureValidationFailed); } - ValidationResult issuerSigningKeyValidationResult = validationParameters.IssuerSigningKeyValidator( - samlToken.SigningKey, - samlToken, - validationParameters, - null, - callContext); + ValidationResult issuerSigningKeyValidationResult; - if (!issuerSigningKeyValidationResult.IsValid) + try { - StackFrames.IssuerSigningKeyValidationFailed ??= new StackFrame(true); - return issuerSigningKeyValidationResult.UnwrapError().AddStackFrame(StackFrames.IssuerSigningKeyValidationFailed); + issuerSigningKeyValidationResult = validationParameters.IssuerSigningKeyValidator( + samlToken.SigningKey, + samlToken, + validationParameters, + null, + callContext); + + if (!issuerSigningKeyValidationResult.IsValid) + return issuerSigningKeyValidationResult.UnwrapError().AddCurrentStackFrame(); + } +#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 IssuerSigningKeyValidationError( + new MessageDetail(Tokens.LogMessages.IDX10274), + ValidationFailureType.IssuerSigningKeyValidatorThrew, + typeof(SecurityTokenInvalidSigningKeyException), + ValidationError.GetCurrentStackFrame(), + samlToken.SigningKey, + ex); } - return new ValidatedToken(samlToken, this, validationParameters); + return new ValidatedToken(samlToken, this, validationParameters) + { + ValidatedAudience = conditionsResult.UnwrapResult().ValidatedAudience, + ValidatedLifetime = conditionsResult.UnwrapResult().ValidatedLifetime, + ValidatedIssuer = issuerValidationResult.UnwrapResult(), + ValidatedTokenReplayExpirationTime = tokenReplayValidationResult?.UnwrapResult(), + ValidatedSigningKey = signatureValidationResult.UnwrapResult(), + ValidatedSigningKeyLifetime = issuerSigningKeyValidationResult.UnwrapResult(), + }; } // ValidatedConditions is basically a named tuple but using a record struct better expresses the intent. @@ -126,17 +202,32 @@ internal virtual ValidationResult ValidateConditions( StackFrames.AssertionConditionsNull); } - var lifetimeValidationResult = validationParameters.LifetimeValidator( - samlToken.Assertion.Conditions.NotBefore, - samlToken.Assertion.Conditions.NotOnOrAfter, - samlToken, - validationParameters, - callContext); + ValidationResult lifetimeValidationResult; - if (!lifetimeValidationResult.IsValid) + try { - StackFrames.LifetimeValidationFailed ??= new StackFrame(true); - return lifetimeValidationResult.UnwrapError().AddStackFrame(StackFrames.LifetimeValidationFailed); + lifetimeValidationResult = validationParameters.LifetimeValidator( + samlToken.Assertion.Conditions.NotBefore, + samlToken.Assertion.Conditions.NotOnOrAfter, + samlToken, + validationParameters, + callContext); + + if (!lifetimeValidationResult.IsValid) + return lifetimeValidationResult.UnwrapError().AddCurrentStackFrame(); + } +#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 LifetimeValidationError( + new MessageDetail(Tokens.LogMessages.IDX10271), + ValidationFailureType.LifetimeValidatorThrew, + typeof(SecurityTokenInvalidLifetimeException), + ValidationError.GetCurrentStackFrame(), + samlToken.Assertion.Conditions.NotBefore, + samlToken.Assertion.Conditions.NotOnOrAfter, + ex); } string? validatedAudience = null; @@ -145,18 +236,34 @@ internal virtual ValidationResult ValidateConditions( if (condition is SamlAudienceRestrictionCondition audienceRestriction) { - // AudienceRestriction.Audiences is an ICollection so we need make a conversion to List before calling our audience validator var audiencesAsList = audienceRestriction.Audiences.Select(static x => x.OriginalString).ToList(); + ValidationResult audienceValidationResult; - var audienceValidationResult = validationParameters.AudienceValidator( - audiencesAsList, - samlToken, - validationParameters, - callContext); + try + { + audienceValidationResult = validationParameters.AudienceValidator( + audiencesAsList, + samlToken, + validationParameters, + callContext); - if (!audienceValidationResult.IsValid) - return audienceValidationResult.UnwrapError(); + if (!audienceValidationResult.IsValid) + return audienceValidationResult.UnwrapError().AddCurrentStackFrame(); + } +#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 AudienceValidationError( + new MessageDetail(Tokens.LogMessages.IDX10270), + ValidationFailureType.AudienceValidatorThrew, + typeof(SecurityTokenInvalidAudienceException), + ValidationError.GetCurrentStackFrame(), + audiencesAsList, + validationParameters.ValidAudiences, + ex); + } validatedAudience = audienceValidationResult.UnwrapResult(); } 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 2108a5c662..c7a05acfc0 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.StackFrames.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.StackFrames.cs @@ -15,7 +15,6 @@ internal static class StackFrames internal static StackFrame? TokenNull; internal static StackFrame? TokenValidationParametersNull; internal static StackFrame? IssuerValidationFailed; - internal static StackFrame? IssuerSigningKeyValidationFailed; internal static StackFrame? SignatureValidationFailed; // Stack frames from ValidateConditions diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/LogMessages.cs index a0e71cf811..d60b4b4a5a 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/LogMessages.cs @@ -24,7 +24,7 @@ internal static class LogMessages internal const string IDX13511 = "IDX13511: The Saml2SecurityToken cannot be validated because the Assertion specifies a ProxyRestriction condition.Enforcement of the ProxyRestriction condition is not supported by default. To customize the enforcement of Saml2Conditions, extend Saml2SecurityTokenHandler and override ValidateConditions."; internal const string IDX13512 = "IDX13512: Unable to validate token. A Saml2SamlAttributeStatement can only have one Saml2Attribute of type 'Actor'. This special Saml2Attribute is used in delegation scenarios."; internal const string IDX13513 = "IDX13513: NotBefore '{0}', is after NotOnOrAfter '{1}'."; - internal const string IDX13514 = "IDX13514: NotOnOrAfter '{0}', is before NotBefore '{1}'."; + //internal const string IDX13514 = "IDX13514: NotOnOrAfter '{0}', is before NotBefore '{1}'."; internal const string IDX13515 = "IDX13515: SamlId value threw on XmlConvert.VerifyNCName. value: '{0}'"; internal const string IDX13516 = "IDX13516: A Saml2Statement of type: '{0}' was found when ProcessingStatements and creating the ClaimsIdentity. These claims have been skipped. If you need to process this Statement, you will need to derive a custom Saml2SecurityTokenHandler and override ProcessStatements."; diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2Conditions.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2Conditions.cs index c339042d4c..f7983af30a 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2Conditions.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2Conditions.cs @@ -78,19 +78,7 @@ public DateTime? NotBefore public DateTime? NotOnOrAfter { get { return _notOnOrAfter; } - set - { - value = DateTimeUtil.ToUniversalTime(value); - - //This should not be checked here, instead fail during validation of the token. Will remove this code once we release new validation model bug #2905 - /* if (value != null && NotBefore.HasValue) - { - if (value.Value <= NotBefore.Value) - throw LogExceptionMessage(new ArgumentException(FormatInvariant(LogMessages.IDX13514, MarkAsNonPII(value), MarkAsNonPII(NotBefore)))); - }*/ - - _notOnOrAfter = value; - } + set { _notOnOrAfter = DateTimeUtil.ToUniversalTime(value); } } /// diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ClaimsIdentity.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ClaimsIdentity.cs new file mode 100644 index 0000000000..f719f1ef17 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ClaimsIdentity.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Security.Claims; +using Microsoft.IdentityModel.Logging; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml2 +{ + /// + /// A designed for creating and validating Saml2 Tokens. See: http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf + /// + public partial class Saml2SecurityTokenHandler : SecurityTokenHandler + { + internal override ClaimsIdentity CreateClaimsIdentityInternal(SecurityToken securityToken, ValidationParameters validationParameters, string issuer) + { + return CreateClaimsIdentity((Saml2SecurityToken)securityToken, validationParameters, issuer); + } + + internal ClaimsIdentity CreateClaimsIdentity(Saml2SecurityToken samlToken, ValidationParameters validationParameters, string issuer) + { + if (samlToken == null) + throw LogHelper.LogArgumentNullException(nameof(samlToken)); + + if (samlToken.Assertion == null) + throw LogHelper.LogArgumentNullException(LogMessages.IDX13110); + + if (validationParameters == null) + throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + + string actualIssuer = issuer; + if (string.IsNullOrWhiteSpace(issuer)) + actualIssuer = ClaimsIdentity.DefaultIssuer; + + ClaimsIdentity identity = validationParameters.CreateClaimsIdentity(samlToken, actualIssuer); + + ProcessSubject(samlToken.Assertion.Subject, identity, actualIssuer); + ProcessStatements(samlToken.Assertion.Statements, identity, actualIssuer); + + return identity; + } + } +} +#nullable restore 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.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs index 2ff1a01def..4b9f18d590 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; @@ -35,19 +36,32 @@ internal async Task> ValidateTokenAsync( } internal async Task> ValidateTokenAsync( - Saml2SecurityToken samlToken, + SecurityToken securityToken, ValidationParameters validationParameters, CallContext callContext, CancellationToken cancellationToken) { - if (samlToken is null) + if (securityToken is null) { StackFrames.TokenNull ??= new StackFrame(true); return ValidationError.NullParameter( - nameof(samlToken), + nameof(securityToken), StackFrames.TokenNull); } + if (securityToken is not Saml2SecurityToken samlToken) + { + return new ValidationError( + new MessageDetail( + Tokens.Saml.LogMessages.IDX11400, + this, + typeof(Saml2SecurityToken), + securityToken.GetType()), + ValidationFailureType.InvalidSecurityToken, + typeof(SecurityTokenArgumentException), + ValidationError.GetCurrentStackFrame()); + } + if (validationParameters is null) { StackFrames.TokenValidationParametersNull ??= new StackFrame(true); @@ -64,22 +78,62 @@ internal async Task> ValidateTokenAsync( callContext); if (!conditionsResult.IsValid) + return conditionsResult.UnwrapError().AddCurrentStackFrame(); + + ValidationResult issuerValidationResult; + + try { - StackFrames.AssertionConditionsValidationFailed ??= new StackFrame(true); - return conditionsResult.UnwrapError().AddStackFrame(StackFrames.AssertionConditionsValidationFailed); + issuerValidationResult = await validationParameters.IssuerValidatorAsync( + samlToken.Issuer, + samlToken, + validationParameters, + callContext, + cancellationToken).ConfigureAwait(false); + + if (!issuerValidationResult.IsValid) + return issuerValidationResult.UnwrapError().AddCurrentStackFrame(); + } +#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 IssuerValidationError( + new MessageDetail(Tokens.LogMessages.IDX10269), + ValidationFailureType.IssuerValidatorThrew, + typeof(SecurityTokenInvalidIssuerException), + ValidationError.GetCurrentStackFrame(), + samlToken.Issuer, + ex); } - ValidationResult validatedIssuerResult = await validationParameters.IssuerValidatorAsync( - samlToken.Issuer, - samlToken, - validationParameters, - callContext, - cancellationToken).ConfigureAwait(false); + ValidationResult? tokenReplayValidationResult = null; - if (!validatedIssuerResult.IsValid) + if (samlToken.Assertion.Conditions is not null) { - StackFrames.IssuerValidationFailed ??= new StackFrame(true); - return validatedIssuerResult.UnwrapError().AddStackFrame(StackFrames.IssuerValidationFailed); + try + { + tokenReplayValidationResult = validationParameters.TokenReplayValidator( + samlToken.Assertion.Conditions.NotOnOrAfter, + samlToken.Assertion.CanonicalString, + validationParameters, + callContext); + + if (!tokenReplayValidationResult.Value.IsValid) + return tokenReplayValidationResult.Value.UnwrapError().AddCurrentStackFrame(); + } +#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 TokenReplayValidationError( + new MessageDetail(Tokens.LogMessages.IDX10276), + ValidationFailureType.TokenReplayValidatorThrew, + typeof(SecurityTokenReplayDetectedException), + ValidationError.GetCurrentStackFrame(), + samlToken.Assertion.Conditions.NotOnOrAfter, + ex); + } } var signatureValidationResult = ValidateSignature(samlToken, validationParameters, callContext); @@ -89,20 +143,42 @@ internal async Task> ValidateTokenAsync( return signatureValidationResult.UnwrapError().AddStackFrame(StackFrames.SignatureValidationFailed); } - var issuerSigningKeyValidationResult = validationParameters.IssuerSigningKeyValidator( - samlToken.SigningKey, - samlToken, - validationParameters, - null, - callContext); + ValidationResult issuerSigningKeyValidationResult; - if (!issuerSigningKeyValidationResult.IsValid) + try { - StackFrames.IssuerSigningKeyValidationFailed ??= new StackFrame(true); - return issuerSigningKeyValidationResult.UnwrapError().AddStackFrame(StackFrames.IssuerSigningKeyValidationFailed); + issuerSigningKeyValidationResult = validationParameters.IssuerSigningKeyValidator( + samlToken.SigningKey, + samlToken, + validationParameters, + null, + callContext); + + if (!issuerSigningKeyValidationResult.IsValid) + return issuerSigningKeyValidationResult.UnwrapError().AddCurrentStackFrame(); + } +#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 IssuerSigningKeyValidationError( + new MessageDetail(Tokens.LogMessages.IDX10274), + ValidationFailureType.IssuerSigningKeyValidatorThrew, + typeof(SecurityTokenInvalidSigningKeyException), + ValidationError.GetCurrentStackFrame(), + samlToken.SigningKey, + ex); } - return new ValidatedToken(samlToken, this, validationParameters); + return new ValidatedToken(samlToken, this, validationParameters) + { + ValidatedAudience = conditionsResult.UnwrapResult().ValidatedAudience, + ValidatedLifetime = conditionsResult.UnwrapResult().ValidatedLifetime, + ValidatedIssuer = issuerValidationResult.UnwrapResult(), + ValidatedTokenReplayExpirationTime = tokenReplayValidationResult?.UnwrapResult(), + ValidatedSigningKey = signatureValidationResult.UnwrapResult(), + ValidatedSigningKeyLifetime = issuerSigningKeyValidationResult.UnwrapResult(), + }; } // ValidatedConditions is basically a named tuple but using a record struct better expresses the intent. @@ -129,48 +205,51 @@ internal virtual ValidationResult ValidateConditions( StackFrames.AssertionConditionsNull); } - var lifetimeValidationResult = validationParameters.LifetimeValidator( - samlToken.Assertion.Conditions.NotBefore, - samlToken.Assertion.Conditions.NotOnOrAfter, - samlToken, - validationParameters, - callContext); + ValidationResult lifetimeValidationResult; - if (!lifetimeValidationResult.IsValid) + try { - StackFrames.LifetimeValidationFailed ??= new StackFrame(true); - return lifetimeValidationResult.UnwrapError().AddStackFrame(StackFrames.LifetimeValidationFailed); + lifetimeValidationResult = validationParameters.LifetimeValidator( + samlToken.Assertion.Conditions.NotBefore, + samlToken.Assertion.Conditions.NotOnOrAfter, + samlToken, + validationParameters, + callContext); + + if (!lifetimeValidationResult.IsValid) + return lifetimeValidationResult.UnwrapError().AddCurrentStackFrame(); + } +#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 LifetimeValidationError( + new MessageDetail(Tokens.LogMessages.IDX10271), + ValidationFailureType.LifetimeValidatorThrew, + typeof(SecurityTokenInvalidLifetimeException), + ValidationError.GetCurrentStackFrame(), + samlToken.Assertion.Conditions.NotBefore, + samlToken.Assertion.Conditions.NotOnOrAfter, + ex); } if (samlToken.Assertion.Conditions.OneTimeUse) { - //ValidateOneTimeUseCondition(samlToken, validationParameters); - // We can keep an overridable method for this, or rely on the TokenReplayValidator delegate. - var oneTimeUseValidationResult = validationParameters.TokenReplayValidator( - samlToken.Assertion.Conditions.NotOnOrAfter, - samlToken.Assertion.CanonicalString, - validationParameters, - callContext); + var oneTimeUseValidationError = ValidateOneTimeUseCondition(samlToken, validationParameters, callContext); - if (!oneTimeUseValidationResult.IsValid) - { - StackFrames.OneTimeUseValidationFailed ??= new StackFrame(true); - return oneTimeUseValidationResult.UnwrapError().AddStackFrame(StackFrames.OneTimeUseValidationFailed); - } + if (oneTimeUseValidationError is not null) + return oneTimeUseValidationError.AddCurrentStackFrame(); } - if (samlToken.Assertion.Conditions.ProxyRestriction != null) + if (samlToken.Assertion.Conditions.ProxyRestriction is not null) { - //throw LogExceptionMessage(new SecurityTokenValidationException(LogMessages.IDX13511)); var proxyValidationError = ValidateProxyRestriction( samlToken, validationParameters, callContext); if (proxyValidationError is not null) - { - return proxyValidationError; - } + return proxyValidationError.AddCurrentStackFrame(); } string? validatedAudience = null; @@ -181,15 +260,31 @@ internal virtual ValidationResult ValidateConditions( if (audienceRestriction.Audiences is not List audiencesAsList) audiencesAsList = [.. audienceRestriction.Audiences]; - var audienceValidationResult = validationParameters.AudienceValidator( - audiencesAsList, - samlToken, - validationParameters, - callContext); - if (!audienceValidationResult.IsValid) + ValidationResult audienceValidationResult; + + try + { + audienceValidationResult = validationParameters.AudienceValidator( + audiencesAsList, + samlToken, + validationParameters, + callContext); + + if (!audienceValidationResult.IsValid) + return audienceValidationResult.UnwrapError().AddCurrentStackFrame(); + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { - StackFrames.AudienceValidationFailed ??= new StackFrame(true); - return audienceValidationResult.UnwrapError().AddStackFrame(StackFrames.AudienceValidationFailed); + return new AudienceValidationError( + new MessageDetail(Tokens.LogMessages.IDX10270), + ValidationFailureType.AudienceValidatorThrew, + typeof(SecurityTokenInvalidAudienceException), + ValidationError.GetCurrentStackFrame(), + audiencesAsList, + validationParameters.ValidAudiences, + ex); } // Audience is valid, save it for later. @@ -203,7 +298,13 @@ internal virtual ValidationResult ValidateConditions( internal virtual ValidationError? ValidateProxyRestriction(Saml2SecurityToken samlToken, ValidationParameters validationParameters, CallContext callContext) #pragma warning restore CA1801 // Review unused parameters { - // return an error, or ignore and allow overriding? + return null; + } + +#pragma warning disable CA1801 // Review unused parameters + internal virtual ValidationError? ValidateOneTimeUseCondition(Saml2SecurityToken samlToken, ValidationParameters validationParameters, CallContext callContext) +#pragma warning restore CA1801 // Review unused parameters + { return null; } } diff --git a/src/Microsoft.IdentityModel.Tokens/AsymmetricAdapter.cs b/src/Microsoft.IdentityModel.Tokens/AsymmetricAdapter.cs index 5bca1e9317..7b09c89324 100644 --- a/src/Microsoft.IdentityModel.Tokens/AsymmetricAdapter.cs +++ b/src/Microsoft.IdentityModel.Tokens/AsymmetricAdapter.cs @@ -47,14 +47,23 @@ internal AsymmetricAdapter(SecurityKey key, string algorithm, bool requirePrivat { } - internal AsymmetricAdapter(SecurityKey key, string algorithm, HashAlgorithm hashAlgorithm, HashAlgorithmName hashAlgorithmName, bool requirePrivateKey) + internal AsymmetricAdapter( + SecurityKey key, + string algorithm, + HashAlgorithm hashAlgorithm, + HashAlgorithmName hashAlgorithmName, + bool requirePrivateKey) : this(key, algorithm, hashAlgorithm, requirePrivateKey) { HashAlgorithmName = hashAlgorithmName; } - internal AsymmetricAdapter(SecurityKey key, string algorithm, HashAlgorithm hashAlgorithm, bool requirePrivateKey) + internal AsymmetricAdapter( + SecurityKey key, + string algorithm, + HashAlgorithm hashAlgorithm, + bool requirePrivateKey) { HashAlgorithm = hashAlgorithm; @@ -79,7 +88,11 @@ internal AsymmetricAdapter(SecurityKey key, string algorithm, HashAlgorithm hash else if (securityKey is ECDsaSecurityKey edcsaSecurityKeyFromJsonWebKey) InitializeUsingEcdsaSecurityKey(edcsaSecurityKeyFromJsonWebKey); else - throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10684, LogHelper.MarkAsNonPII(algorithm), key))); + throw LogHelper.LogExceptionMessage( + new NotSupportedException( + LogHelper.FormatInvariant( + LogMessages.IDX10684, + LogHelper.MarkAsNonPII(algorithm), key))); } } else if (key is ECDsaSecurityKey ecdsaKey) @@ -87,7 +100,11 @@ internal AsymmetricAdapter(SecurityKey key, string algorithm, HashAlgorithm hash InitializeUsingEcdsaSecurityKey(ecdsaKey); } else - throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10684, LogHelper.MarkAsNonPII(algorithm), key))); + throw LogHelper.LogExceptionMessage( + new NotSupportedException( + LogHelper.FormatInvariant( + LogMessages.IDX10684, + LogHelper.MarkAsNonPII(algorithm), key))); } internal byte[] Decrypt(byte[] data) @@ -233,7 +250,10 @@ private void InitializeUsingRsaSecurityKey(RsaSecurityKey rsaSecurityKey, string } } - private void InitializeUsingX509SecurityKey(X509SecurityKey x509SecurityKey, string algorithm, bool requirePrivateKey) + private void InitializeUsingX509SecurityKey( + X509SecurityKey x509SecurityKey, + string algorithm, + bool requirePrivateKey) { if (requirePrivateKey) InitializeUsingRsa(x509SecurityKey.PrivateKey as RSA, algorithm); @@ -249,7 +269,10 @@ internal byte[] Sign(byte[] bytes) } #if NET6_0_OR_GREATER - internal bool SignUsingSpan(ReadOnlySpan data, Span destination, out int bytesWritten) + internal bool SignUsingSpan( + ReadOnlySpan data, + Span destination, + out int bytesWritten) { return _signUsingSpanFunction(data, destination, out bytesWritten); } @@ -274,7 +297,10 @@ private static byte[] SignUsingOffsetNotFound(byte[] b, int c, int d) #if NET6_0_OR_GREATER #pragma warning disable CA1801 // Review unused parameters - private static bool SignUsingSpanNotFound(ReadOnlySpan data, Span destination, out int bytesWritten) + private static bool SignUsingSpanNotFound( + ReadOnlySpan data, + Span destination, + out int bytesWritten) #pragma warning restore CA1801 // Review unused parameters { // we should never get here, its a bug if we do. @@ -288,7 +314,10 @@ private byte[] SignECDsa(byte[] bytes) } #if NET6_0_OR_GREATER - internal bool SignUsingSpanECDsa(ReadOnlySpan data, Span destination, out int bytesWritten) + internal bool SignUsingSpanECDsa( + ReadOnlySpan data, + Span destination, + out int bytesWritten) { // ECDSA.TrySignData will return true and set bytesWritten = 64, if destination is null. if (destination.Length == 0) @@ -397,7 +426,11 @@ private bool VerifyUsingOffsetRsa(byte[] bytes, int offset, int count, byte[] si #if NET6_0_OR_GREATER return VerifyUsingSpan(isRSA: true, bytes.AsSpan(offset, count), signature); #else - return RSA.VerifyHash(HashAlgorithm.ComputeHash(bytes, offset, count), signature, HashAlgorithmName, RSASignaturePadding); + return RSA.VerifyHash( + HashAlgorithm.ComputeHash(bytes, offset, count), + signature, + HashAlgorithmName, + RSASignaturePadding); #endif } diff --git a/src/Microsoft.IdentityModel.Tokens/AsymmetricSignatureProvider.cs b/src/Microsoft.IdentityModel.Tokens/AsymmetricSignatureProvider.cs index b767713bb1..0117c21b5e 100644 --- a/src/Microsoft.IdentityModel.Tokens/AsymmetricSignatureProvider.cs +++ b/src/Microsoft.IdentityModel.Tokens/AsymmetricSignatureProvider.cs @@ -22,7 +22,7 @@ public class AsymmetricSignatureProvider : SignatureProvider /// /// Mapping from algorithm to minimum .KeySize when creating signatures. /// - public static readonly Dictionary DefaultMinimumAsymmetricKeySizeInBitsForSigningMap = new Dictionary() + public static readonly Dictionary DefaultMinimumAsymmetricKeySizeInBitsForSigningMap = new() { { SecurityAlgorithms.EcdsaSha256, 256 }, { SecurityAlgorithms.EcdsaSha384, 256 }, @@ -47,7 +47,7 @@ public class AsymmetricSignatureProvider : SignatureProvider /// /// Mapping from algorithm to minimum .KeySize when verifying signatures. /// - public static readonly Dictionary DefaultMinimumAsymmetricKeySizeInBitsForVerifyingMap = new Dictionary() + public static readonly Dictionary DefaultMinimumAsymmetricKeySizeInBitsForVerifyingMap = new() { { SecurityAlgorithms.EcdsaSha256, 256 }, { SecurityAlgorithms.EcdsaSha384, 256 }, @@ -69,13 +69,20 @@ public class AsymmetricSignatureProvider : SignatureProvider { SecurityAlgorithms.RsaSsaPssSha512Signature, 1040 } }; - internal AsymmetricSignatureProvider(SecurityKey key, string algorithm, CryptoProviderFactory cryptoProviderFactory) + internal AsymmetricSignatureProvider( + SecurityKey key, + string algorithm, + CryptoProviderFactory cryptoProviderFactory) : this(key, algorithm) { _cryptoProviderFactory = cryptoProviderFactory; } - internal AsymmetricSignatureProvider(SecurityKey key, string algorithm, bool willCreateSignatures, CryptoProviderFactory cryptoProviderFactory) + internal AsymmetricSignatureProvider( + SecurityKey key, + string algorithm, + bool willCreateSignatures, + CryptoProviderFactory cryptoProviderFactory) : this(key, algorithm, willCreateSignatures) { _cryptoProviderFactory = cryptoProviderFactory; @@ -104,7 +111,10 @@ public AsymmetricSignatureProvider(SecurityKey key, string algorithm) /// Thrown if is true and is less than the required size for signing. /// Thrown if is less than the required size for verifying signatures. /// Thrown if the runtime is unable to create a suitable cryptographic provider. - public AsymmetricSignatureProvider(SecurityKey key, string algorithm, bool willCreateSignatures) + public AsymmetricSignatureProvider( + SecurityKey key, + string algorithm, + bool willCreateSignatures) : base(key, algorithm) { _cryptoProviderFactory = key.CryptoProviderFactory; @@ -116,13 +126,21 @@ public AsymmetricSignatureProvider(SecurityKey key, string algorithm, bool willC JsonWebKeyConverter.TryConvertToSecurityKey(jsonWebKey, out SecurityKey _); if (willCreateSignatures && FoundPrivateKey(key) == PrivateKeyStatus.DoesNotExist) - throw LogHelper.LogExceptionMessage(new InvalidOperationException(LogHelper.FormatInvariant(LogMessages.IDX10638, key))); + throw LogHelper.LogExceptionMessage( + new InvalidOperationException( + LogHelper.FormatInvariant(LogMessages.IDX10638, key))); if (!_cryptoProviderFactory.IsSupportedAlgorithm(algorithm, key)) - throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10634, LogHelper.MarkAsNonPII((algorithm)), key))); + throw LogHelper.LogExceptionMessage( + new NotSupportedException( + LogHelper.FormatInvariant( + LogMessages.IDX10634, + LogHelper.MarkAsNonPII((algorithm)), key))); WillCreateSignatures = willCreateSignatures; - _asymmetricAdapterObjectPool = new DisposableObjectPool(CreateAsymmetricAdapter, _cryptoProviderFactory.SignatureProviderObjectPoolCacheSize); + _asymmetricAdapterObjectPool = new DisposableObjectPool( + CreateAsymmetricAdapter, + _cryptoProviderFactory.SignatureProviderObjectPoolCacheSize); } /// @@ -171,8 +189,13 @@ protected virtual HashAlgorithmName GetHashAlgorithmName(string algorithm) private AsymmetricAdapter CreateAsymmetricAdapter() { - var hashAlgoritmName = GetHashAlgorithmName(Algorithm); - return new AsymmetricAdapter(Key, Algorithm, _cryptoProviderFactory.CreateHashAlgorithm(hashAlgoritmName), hashAlgoritmName, WillCreateSignatures); + HashAlgorithmName hashAlgorithmName = GetHashAlgorithmName(Algorithm); + return new AsymmetricAdapter( + Key, + Algorithm, + _cryptoProviderFactory.CreateHashAlgorithm(hashAlgorithmName), + hashAlgorithmName, + WillCreateSignatures); } internal bool ValidKeySize() @@ -188,7 +211,10 @@ internal bool ValidKeySize() #if NET6_0_OR_GREATER /// - public override bool Sign(ReadOnlySpan input, Span signature, out int bytesWritten) + public override bool Sign( + ReadOnlySpan input, + Span signature, + out int bytesWritten) { if (input.Length == 0) throw LogHelper.LogArgumentNullException(nameof(input)); @@ -219,12 +245,14 @@ public override bool Sign(ReadOnlySpan input, Span signature, out in #endif /// - /// Produces a signature over the 'input' using the and algorithm passed to . + /// Produces a signature over the 'input' using the and algorithm passed + /// to . /// /// The bytes to be signed. /// A signature over the input. /// Thrown if is null or has length of 0. - /// Thrown If has been called. + /// Thrown if + /// has been called. /// Sign is thread safe. public override byte[] Sign(byte[] input) { @@ -317,23 +345,41 @@ public virtual void ValidateAsymmetricSecurityKeySize(SecurityKey key, string al if (convertedSecurityKey is AsymmetricSecurityKey convertedAsymmetricKey) keySize = convertedAsymmetricKey.KeySize; else if (convertedSecurityKey is SymmetricSecurityKey) - throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10704, key))); + throw LogHelper.LogExceptionMessage( + new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10704, key))); } else { - throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10704, key))); + throw LogHelper.LogExceptionMessage( + new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10704, key))); } if (willCreateSignatures) { if (MinimumAsymmetricKeySizeInBitsForSigningMap.ContainsKey(algorithm) && keySize < MinimumAsymmetricKeySizeInBitsForSigningMap[algorithm]) - throw LogHelper.LogExceptionMessage(new ArgumentOutOfRangeException(nameof(key), LogHelper.FormatInvariant(LogMessages.IDX10630, key, LogHelper.MarkAsNonPII(MinimumAsymmetricKeySizeInBitsForSigningMap[algorithm]), LogHelper.MarkAsNonPII(keySize)))); + throw LogHelper.LogExceptionMessage( + new ArgumentOutOfRangeException( + nameof(key), + LogHelper.FormatInvariant( + LogMessages.IDX10630, + key, + LogHelper.MarkAsNonPII( + MinimumAsymmetricKeySizeInBitsForSigningMap[algorithm]), + LogHelper.MarkAsNonPII(keySize)))); } else if (MinimumAsymmetricKeySizeInBitsForVerifyingMap.ContainsKey(algorithm) && keySize < MinimumAsymmetricKeySizeInBitsForVerifyingMap[algorithm]) { - throw LogHelper.LogExceptionMessage(new ArgumentOutOfRangeException(nameof(key), LogHelper.FormatInvariant(LogMessages.IDX10631, key, LogHelper.MarkAsNonPII(MinimumAsymmetricKeySizeInBitsForVerifyingMap[algorithm]), LogHelper.MarkAsNonPII(keySize)))); + throw LogHelper.LogExceptionMessage( + new ArgumentOutOfRangeException( + nameof(key), + LogHelper.FormatInvariant( + LogMessages.IDX10631, + key, + LogHelper.MarkAsNonPII( + MinimumAsymmetricKeySizeInBitsForVerifyingMap[algorithm]), + LogHelper.MarkAsNonPII(keySize)))); } } @@ -386,7 +432,13 @@ public override bool Verify(byte[] input, byte[] signature) } /// - public override bool Verify(byte[] input, int inputOffset, int inputLength, byte[] signature, int signatureOffset, int signatureLength) + public override bool Verify( + byte[] input, + int inputOffset, + int inputLength, + byte[] signature, + int signatureOffset, + int signatureLength) { if (input == null || input.Length == 0) throw LogHelper.LogArgumentNullException(nameof(input)); @@ -460,7 +512,7 @@ public override bool Verify(byte[] input, int inputOffset, int inputLength, byte } else { - // AsymetricAdapter.Verify could do this. + // AsymmetricAdapter.Verify could do this. // Having the logic here, handles EC and RSA. We can revisit when we start using spans in 3.1+. byte[] signatureBytes = new byte[signatureLength]; Array.Copy(signature, 0, signatureBytes, 0, signatureLength); @@ -490,7 +542,7 @@ protected override void Dispose(bool disposing) _disposed = true; if (disposing) { - foreach (var item in _asymmetricAdapterObjectPool.Items) + foreach (DisposableObjectPool.Element item in _asymmetricAdapterObjectPool.Items) item.Value?.Dispose(); CryptoProviderCache?.TryRemove(this); diff --git a/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs b/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs index bdbe960b47..850f2d5657 100644 --- a/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs +++ b/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs @@ -45,7 +45,14 @@ public static CryptoProviderFactory Default public static int DefaultSignatureProviderObjectPoolCacheSize { get => _defaultSignatureProviderObjectPoolCacheSize; - set => _defaultSignatureProviderObjectPoolCacheSize = value > 0 ? value : throw LogHelper.LogExceptionMessage(new ArgumentOutOfRangeException(nameof(value), LogHelper.FormatInvariant(LogMessages.IDX10698, LogHelper.MarkAsNonPII(value)))); + set => _defaultSignatureProviderObjectPoolCacheSize = value > 0 + ? value + : throw LogHelper.LogExceptionMessage( + new ArgumentOutOfRangeException( + nameof(value), + LogHelper.FormatInvariant( + LogMessages.IDX10698, + LogHelper.MarkAsNonPII(value)))); } /// @@ -96,8 +103,9 @@ public CryptoProviderFactory(CryptoProviderFactory other) /// /// Extensibility point for creating custom cryptographic operators. /// - /// By default, if set, will be called before creating cryptographic operators. - /// If true is returned, then will be called. The will throw if the + /// By default, if set, will be called before + /// creating cryptographic operators. If true is returned, then will be called. + /// The will throw if the /// Cryptographic operator returned is not of the correct type. public ICryptoProvider CustomCryptoProvider { get; set; } @@ -114,23 +122,33 @@ public int SignatureProviderObjectPoolCacheSize { get => _signatureProviderObjectPoolCacheSize; - set => _signatureProviderObjectPoolCacheSize = value > 0 ? value : throw LogHelper.LogExceptionMessage(new ArgumentOutOfRangeException(nameof(value), LogHelper.FormatInvariant(LogMessages.IDX10698, LogHelper.MarkAsNonPII(value)))); + set => _signatureProviderObjectPoolCacheSize = value > 0 + ? value + : throw LogHelper.LogExceptionMessage( + new ArgumentOutOfRangeException( + nameof(value), + LogHelper.FormatInvariant( + LogMessages.IDX10698, + LogHelper.MarkAsNonPII(value)))); } /// - /// Creates an instance of for a specific and . + /// Creates an instance of for a specific + /// and . /// /// The to use. /// The algorithm to use. /// Thrown if is null. /// Thrown if is null or empty. - /// Thrown if the combination of and is not supported. - /// Thrown if the type returned by is not assignable to . + /// Thrown if the combination of and + /// is not supported. + /// Thrown if the type returned by + /// is not assignable to . /// - /// If is set and returns true, - /// is called to obtain the . + /// If is set and + /// returns true, is called to obtain the . /// - /// Once done with the , call . + /// When you are finished with the , call . /// /// An instance of . public virtual AuthenticatedEncryptionProvider CreateAuthenticatedEncryptionProvider(SecurityKey key, string algorithm) @@ -145,7 +163,13 @@ public virtual AuthenticatedEncryptionProvider CreateAuthenticatedEncryptionProv { var cryptoProvider = CustomCryptoProvider.Create(algorithm, key) as AuthenticatedEncryptionProvider; if (cryptoProvider == null) - throw LogHelper.LogExceptionMessage(new InvalidOperationException(LogHelper.FormatInvariant(LogMessages.IDX10646, LogHelper.MarkAsNonPII(algorithm), key, LogHelper.MarkAsNonPII(typeof(AuthenticatedEncryptionProvider))))); + throw LogHelper.LogExceptionMessage( + new InvalidOperationException( + LogHelper.FormatInvariant( + LogMessages.IDX10646, + LogHelper.MarkAsNonPII(algorithm), + key, + LogHelper.MarkAsNonPII(typeof(AuthenticatedEncryptionProvider))))); return cryptoProvider; } @@ -153,7 +177,12 @@ public virtual AuthenticatedEncryptionProvider CreateAuthenticatedEncryptionProv if (SupportedAlgorithms.IsSupportedEncryptionAlgorithm(algorithm, key)) return new AuthenticatedEncryptionProvider(key, algorithm); - throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10652, LogHelper.MarkAsNonPII(algorithm)), nameof(algorithm))); + throw LogHelper.LogExceptionMessage( + new ArgumentException( + LogHelper.FormatInvariant( + LogMessages.IDX10652, + LogHelper.MarkAsNonPII(algorithm)), + nameof(algorithm))); } /// @@ -163,13 +192,15 @@ public virtual AuthenticatedEncryptionProvider CreateAuthenticatedEncryptionProv /// The algorithm to use. /// Thrown if is null. /// Thrown if is null or empty. - /// Thrown if the combination of and is not supported. - /// Thrown if the type returned by is not assignable to . + /// Thrown if the combination of and + /// is not supported. + /// Thrown if the type returned by + /// is not assignable to . /// - /// If is set and returns true, - /// is called to obtain the . + /// If is set and + /// returns true, is called to obtain the . /// - /// Once done with the , call . + /// When you are finished with the , call . /// /// An instance of . public virtual KeyWrapProvider CreateKeyWrapProvider(SecurityKey key, string algorithm) @@ -184,13 +215,15 @@ public virtual KeyWrapProvider CreateKeyWrapProvider(SecurityKey key, string alg /// The algorithm to use. /// Thrown if is null. /// Thrown if is null or empty. - /// Thrown if the combination of and is not supported. - /// Thrown if the type returned by is not assignable to . + /// Thrown if the combination of and + /// is not supported. + /// Thrown if the type returned by + /// is not assignable to . /// - /// If is set and returns true, - /// is called to obtain the . + /// If is set and + /// returns true, is called to obtain the . /// - /// Once done with the , call . + /// When you are finished with the , call . /// /// An instance of . public virtual KeyWrapProvider CreateKeyWrapProviderForUnwrap(SecurityKey key, string algorithm) @@ -209,7 +242,13 @@ private KeyWrapProvider CreateKeyWrapProvider(SecurityKey key, string algorithm, if (CustomCryptoProvider != null && CustomCryptoProvider.IsSupportedAlgorithm(algorithm, key, willUnwrap)) { if (!(CustomCryptoProvider.Create(algorithm, key, willUnwrap) is KeyWrapProvider keyWrapProvider)) - throw LogHelper.LogExceptionMessage(new InvalidOperationException(LogHelper.FormatInvariant(LogMessages.IDX10646, LogHelper.MarkAsNonPII(algorithm), key, LogHelper.MarkAsNonPII(typeof(SignatureProvider))))); + throw LogHelper.LogExceptionMessage( + new InvalidOperationException( + LogHelper.FormatInvariant( + LogMessages.IDX10646, + LogHelper.MarkAsNonPII(algorithm), + key, + LogHelper.MarkAsNonPII(typeof(SignatureProvider))))); return keyWrapProvider; } @@ -220,7 +259,12 @@ private KeyWrapProvider CreateKeyWrapProvider(SecurityKey key, string algorithm, if (SupportedAlgorithms.IsSupportedSymmetricKeyWrap(algorithm, key)) return new SymmetricKeyWrapProvider(key, algorithm); - throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10661, LogHelper.MarkAsNonPII(algorithm), key))); + throw LogHelper.LogExceptionMessage( + new NotSupportedException( + LogHelper.FormatInvariant( + LogMessages.IDX10661, + LogHelper.MarkAsNonPII(algorithm), + key))); } /// @@ -231,14 +275,16 @@ private KeyWrapProvider CreateKeyWrapProvider(SecurityKey key, string algorithm, /// Thrown if is null. /// Thrown if is null or empty. /// Thrown if is too small. - /// Thrown if is not assignable from or . + /// Thrown if is not assignable from + /// or . /// Thrown if the key or algorithm combination is not supported. - /// Thrown if the type returned by is not assignable to . + /// Thrown if the type returned by + /// is not assignable to . /// /// AsymmetricSignatureProviders require access to a PrivateKey for signing. - /// Once done with the , call . - /// If is set and returns true, - /// is called to obtain the . + /// When you are finished with the , call . + /// If is set and + /// returns true, is called to obtain the . /// /// /// A instance that can be used to create a signature. @@ -256,12 +302,15 @@ public virtual SignatureProvider CreateForSigning(SecurityKey key, string algori /// Thrown if is null. /// Thrown if is null or empty. /// Thrown if is too small. - /// Thrown if is not assignable from or . - /// Thrown if the combination of and is not supported. - /// Thrown if the type returned by is not assignable to . + /// Thrown if is not assignable from + /// or . + /// Thrown if the combination of and + /// is not supported. + /// Thrown if the type returned by + /// is not assignable to . /// /// AsymmetricSignatureProviders require access to a PrivateKey for signing. - /// Once done with the , call . + /// When you are finished with the , call . /// If is set and returns true, /// is called to obtain the . /// @@ -280,16 +329,19 @@ public virtual SignatureProvider CreateForSigning(SecurityKey key, string algori /// Thrown if is null. /// Thrown if is null or empty. /// Thrown if is too small. - /// Thrown if is not assignable from or . + /// Thrown if is not assignable from + /// or . /// Thrown if the combination of and is not supported. - /// Thrown if the type returned by is not assignable to . + /// Thrown if the type returned by + /// is not assignable to . /// - /// Once done with the , call . + /// When you are finished with the , call . /// If is set and returns true, /// is called to obtain the . /// /// - /// A instance that can be used to validate signatures using the and algorithm. + /// A instance that can be used to validate signatures using the + /// and algorithm. public virtual SignatureProvider CreateForVerifying(SecurityKey key, string algorithm) { return CreateForVerifying(key, algorithm, CacheSignatureProviders); @@ -304,11 +356,13 @@ public virtual SignatureProvider CreateForVerifying(SecurityKey key, string algo /// Thrown if is null. /// Thrown if is null or empty. /// Thrown if is too small. - /// Thrown if is not assignable from or . + /// Thrown if is not assignable from + /// or . /// Thrown if the combination of and is not supported. - /// Thrown if the type returned by is not assignable to . + /// Thrown if the type returned by + /// is not assignable to . /// - /// Once done with the , call . + /// When you are finished with the , call . /// If is set and returns true, /// is called to obtain the . /// @@ -324,10 +378,11 @@ public virtual SignatureProvider CreateForVerifying(SecurityKey key, string algo /// /// The name of the hash algorithm to create. /// Thrown if is null or empty. - /// Thrown if returns a type that is not assignable to . + /// Thrown if + /// returns a type that is not assignable to . /// Thrown if is not supported. /// - /// Once done with the , call . + /// When you are finished with the , call . /// If is set and returns true, /// is called to obtain the . /// @@ -338,7 +393,12 @@ public virtual HashAlgorithm CreateHashAlgorithm(HashAlgorithmName algorithm) if (CustomCryptoProvider != null && CustomCryptoProvider.IsSupportedAlgorithm(algorithm.Name)) { if (!(CustomCryptoProvider.Create(algorithm.Name) is HashAlgorithm hashAlgorithm)) - throw LogHelper.LogExceptionMessage(new InvalidOperationException(LogHelper.FormatInvariant(LogMessages.IDX10647, LogHelper.MarkAsNonPII(algorithm), LogHelper.MarkAsNonPII(typeof(HashAlgorithm))))); + throw LogHelper.LogExceptionMessage( + new InvalidOperationException( + LogHelper.FormatInvariant( + LogMessages.IDX10647, + LogHelper.MarkAsNonPII(algorithm), + LogHelper.MarkAsNonPII(typeof(HashAlgorithm))))); _typeToAlgorithmMap[hashAlgorithm.GetType().ToString()] = algorithm.Name; return hashAlgorithm; @@ -353,7 +413,11 @@ public virtual HashAlgorithm CreateHashAlgorithm(HashAlgorithmName algorithm) if (algorithm == HashAlgorithmName.SHA512) return SHA512.Create(); - throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10640, LogHelper.MarkAsNonPII(algorithm)))); + throw LogHelper.LogExceptionMessage( + new NotSupportedException( + LogHelper.FormatInvariant( + LogMessages.IDX10640, + LogHelper.MarkAsNonPII(algorithm)))); } /// @@ -361,10 +425,11 @@ public virtual HashAlgorithm CreateHashAlgorithm(HashAlgorithmName algorithm) /// /// The name of the hash algorithm to create. /// Thrown if is null or empty. - /// Thrown if returns a type that is not assignable to . + /// Thrown if returns a type that + /// is not assignable to . /// Thrown if is not supported. /// - /// Once done with the , call . + /// When you are finished with the , call . /// If is set and returns true, /// is called to obtain the . /// @@ -378,7 +443,12 @@ public virtual HashAlgorithm CreateHashAlgorithm(string algorithm) if (CustomCryptoProvider != null && CustomCryptoProvider.IsSupportedAlgorithm(algorithm)) { if (!(CustomCryptoProvider.Create(algorithm) is HashAlgorithm hashAlgorithm)) - throw LogHelper.LogExceptionMessage(new InvalidOperationException(LogHelper.FormatInvariant(LogMessages.IDX10647, LogHelper.MarkAsNonPII(algorithm), LogHelper.MarkAsNonPII(typeof(HashAlgorithm))))); + throw LogHelper.LogExceptionMessage( + new InvalidOperationException( + LogHelper.FormatInvariant( + LogMessages.IDX10647, + LogHelper.MarkAsNonPII(algorithm), + LogHelper.MarkAsNonPII(typeof(HashAlgorithm))))); _typeToAlgorithmMap[hashAlgorithm.GetType().ToString()] = algorithm; @@ -400,7 +470,11 @@ public virtual HashAlgorithm CreateHashAlgorithm(string algorithm) return SHA512.Create(); } - throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10640, LogHelper.MarkAsNonPII(algorithm)))); + throw LogHelper.LogExceptionMessage( + new NotSupportedException( + LogHelper.FormatInvariant( + LogMessages.IDX10640, + LogHelper.MarkAsNonPII(algorithm)))); } /// @@ -410,10 +484,11 @@ public virtual HashAlgorithm CreateHashAlgorithm(string algorithm) /// The name of the keyed hash algorithm to create. /// Thrown if is null. /// Thrown if is null or empty. - /// Thrown if returns a type that is not assignable to . + /// Thrown if returns a type that + /// is not assignable to . /// Thrown if is not supported. /// - /// Once done with the , call . + /// When you are finished with the , call . /// If is set and returns true, /// is called to obtain the . /// @@ -485,11 +560,18 @@ public virtual KeyedHashAlgorithm CreateKeyedHashAlgorithm(byte[] keyBytes, stri } default: - throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10666, LogHelper.MarkAsNonPII(algorithm)))); + throw LogHelper.LogExceptionMessage( + new NotSupportedException( + LogHelper.FormatInvariant( + LogMessages.IDX10666, + LogHelper.MarkAsNonPII(algorithm)))); } } - private static void ValidateKeySize(byte[] keyBytes, string algorithm, int expectedNumberOfBytes) + private static void ValidateKeySize( + byte[] keyBytes, + string algorithm, + int expectedNumberOfBytes) { if (keyBytes.Length < expectedNumberOfBytes) throw LogHelper.LogExceptionMessage( @@ -501,7 +583,11 @@ private static void ValidateKeySize(byte[] keyBytes, string algorithm, int expec LogHelper.MarkAsNonPII(keyBytes.Length * 8)))); } - private SignatureProvider CreateSignatureProvider(SecurityKey key, string algorithm, bool willCreateSignatures, bool cacheProvider) + private SignatureProvider CreateSignatureProvider( + SecurityKey key, + string algorithm, + bool willCreateSignatures, + bool cacheProvider) { if (key == null) throw LogHelper.LogArgumentNullException(nameof(key)); @@ -562,7 +648,13 @@ private SignatureProvider CreateSignatureProvider(SecurityKey key, string algori } catch (Exception ex) { - throw LogHelper.LogExceptionMessage(new InvalidOperationException(LogHelper.FormatInvariant(LogMessages.IDX10694, key, ex), ex)); + throw LogHelper.LogExceptionMessage( + new InvalidOperationException( + LogHelper.FormatInvariant( + LogMessages.IDX10694, + key, + ex), + ex)); } } else if (key is SymmetricSecurityKey) @@ -584,14 +676,24 @@ private SignatureProvider CreateSignatureProvider(SecurityKey key, string algori if (CacheSignatureProviders && cacheProvider) { - if (CryptoProviderCache.TryGetSignatureProvider(key, algorithm, typeofSignatureProvider, willCreateSignatures, out signatureProvider)) + if (CryptoProviderCache.TryGetSignatureProvider( + key, + algorithm, + typeofSignatureProvider, + willCreateSignatures, + out signatureProvider)) { signatureProvider.AddRef(); return signatureProvider; } if (!IsSupportedAlgorithm(algorithm, key)) - throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10634, LogHelper.MarkAsNonPII(algorithm), key))); + throw LogHelper.LogExceptionMessage( + new NotSupportedException( + LogHelper.FormatInvariant( + LogMessages.IDX10634, + LogHelper.MarkAsNonPII(algorithm), + key))); if (createAsymmetric) signatureProvider = new AsymmetricSignatureProvider(key, algorithm, willCreateSignatures, this); @@ -611,7 +713,12 @@ private SignatureProvider CreateSignatureProvider(SecurityKey key, string algori else { if (!IsSupportedAlgorithm(algorithm, key)) - throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10634, LogHelper.MarkAsNonPII(algorithm), key))); + throw LogHelper.LogExceptionMessage( + new NotSupportedException( + LogHelper.FormatInvariant( + LogMessages.IDX10634, + LogHelper.MarkAsNonPII(algorithm), + key))); if (createAsymmetric) { @@ -701,7 +808,9 @@ public virtual void ReleaseHashAlgorithm(HashAlgorithm hashAlgorithm) { if (hashAlgorithm == null) throw LogHelper.LogArgumentNullException(nameof(hashAlgorithm)); - else if (CustomCryptoProvider != null && _typeToAlgorithmMap.TryGetValue(hashAlgorithm.GetType().ToString(), out var algorithm) && CustomCryptoProvider.IsSupportedAlgorithm(algorithm)) + else if (CustomCryptoProvider != null + && _typeToAlgorithmMap.TryGetValue(hashAlgorithm.GetType().ToString(), out string algorithm) + && CustomCryptoProvider.IsSupportedAlgorithm(algorithm)) CustomCryptoProvider.Release(hashAlgorithm); else hashAlgorithm.Dispose(); 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 50e2ca6243..5552393beb 100644 --- a/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt @@ -1,8 +1,13 @@ 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.IDX10270 = "IDX10270: AudienceValidationDelegate threw an exception, see inner exception." -> string const Microsoft.IdentityModel.Tokens.LogMessages.IDX10271 = "IDX10271: LifetimeValidationDelegate 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 +const Microsoft.IdentityModel.Tokens.LogMessages.IDX10276 = "IDX10276: TokenReplayValidationDelegate threw an exception, see inner exception." -> string const Microsoft.IdentityModel.Tokens.Telemetry.IdentityModelTelemetry.IdentityModelConfigurationManagerCounterDescription = "Counter capturing configuration manager operations." -> string const Microsoft.IdentityModel.Tokens.Telemetry.IdentityModelTelemetry.IdentityModelConfigurationManagerCounterName = "IdentityModelConfigurationManager" -> string const Microsoft.IdentityModel.Tokens.Telemetry.IdentityModelTelemetry.MeterName = "MicrosoftIdentityModel_Meter" -> string @@ -29,8 +34,7 @@ Microsoft.IdentityModel.Tokens.AudienceValidationError.ValidAudiences.get -> Sys 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.IssuerSigningKeyValidationError.IssuerSigningKeyValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, Microsoft.IdentityModel.Tokens.SecurityKey invalidSigningKey, 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 @@ -38,6 +42,18 @@ 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.LoggingEventId +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.TokenReplayValidationError +Microsoft.IdentityModel.Tokens.TokenReplayValidationError.ExpirationTime.get -> System.DateTime? +Microsoft.IdentityModel.Tokens.TokenReplayValidationError.TokenReplayValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.DateTime? expirationTime, System.Exception innerException = null) -> void Microsoft.IdentityModel.Tokens.Telemetry.IdentityModelTelemetry Microsoft.IdentityModel.Tokens.Telemetry.IdentityModelTelemetry.IdentityModelTelemetry() -> void Microsoft.IdentityModel.Tokens.Telemetry.ITelemetryInstrumentation @@ -54,26 +70,39 @@ Microsoft.IdentityModel.Tokens.Telemetry.TelemetryInstrumentation.LogConfigurati Microsoft.IdentityModel.Tokens.Telemetry.TelemetryInstrumentation.LogConfigurationRetrievalDuration(System.TimeSpan operationDuration, System.Exception exception) -> void Microsoft.IdentityModel.Tokens.Telemetry.TelemetryInstrumentation.TelemetryInstrumentation() -> void Microsoft.IdentityModel.Tokens.TokenTypeValidationError +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.TokenTypeValidationError._invalidTokenType -> string Microsoft.IdentityModel.Tokens.TokenValidationParameters.TimeProvider.get -> System.TimeProvider Microsoft.IdentityModel.Tokens.TokenValidationParameters.TimeProvider.set -> void +Microsoft.IdentityModel.Tokens.ValidatedToken.Log(Microsoft.Extensions.Logging.ILogger logger) -> void Microsoft.IdentityModel.Tokens.ValidationError.AddCurrentStackFrame(string filePath = "", int lineNumber = 0, int skipFrames = 1) -> Microsoft.IdentityModel.Tokens.ValidationError Microsoft.IdentityModel.Tokens.ValidationError.GetException(System.Type exceptionType, System.Exception innerException) -> System.Exception -Microsoft.IdentityModel.Tokens.ValidationError.ValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType failureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Exception innerException = null) -> void +Microsoft.IdentityModel.Tokens.ValidationError.Log(Microsoft.Extensions.Logging.ILogger logger) -> void +Microsoft.IdentityModel.Tokens.ValidationError.ValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Exception innerException = null) -> void Microsoft.IdentityModel.Tokens.ValidationParameters.TokenTypeValidator.get -> Microsoft.IdentityModel.Tokens.TokenTypeValidationDelegate Microsoft.IdentityModel.Tokens.ValidationParameters.TokenTypeValidator.set -> void Microsoft.IdentityModel.Tokens.ValidationResult.Error.get -> Microsoft.IdentityModel.Tokens.ValidationError Microsoft.IdentityModel.Tokens.ValidationResult.IsValid.get -> bool +Microsoft.IdentityModel.Tokens.ValidationResult.Log(Microsoft.Extensions.Logging.ILogger logger) -> void 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.TokenReplayValidationError.GetException() -> System.Exception override Microsoft.IdentityModel.Tokens.TokenTypeValidationError.GetException() -> System.Exception static Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError.NullParameter(string parameterName, System.Diagnostics.StackFrame stackFrame) -> Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError +static Microsoft.IdentityModel.Tokens.SignatureValidationError.NullParameter(string parameterName, System.Diagnostics.StackFrame stackFrame) -> Microsoft.IdentityModel.Tokens.SignatureValidationError +static Microsoft.IdentityModel.Tokens.TokenReplayValidationError.NullParameter(string parameterName, System.Diagnostics.StackFrame stackFrame) -> Microsoft.IdentityModel.Tokens.TokenReplayValidationError +static Microsoft.IdentityModel.Tokens.TokenTypeValidationError.NullParameter(string parameterName, System.Diagnostics.StackFrame stackFrame) -> Microsoft.IdentityModel.Tokens.TokenTypeValidationError static Microsoft.IdentityModel.Tokens.Telemetry.IdentityModelTelemetry.IncrementConfigurationRefreshRequestCounter(in System.Diagnostics.TagList tagList) -> void static Microsoft.IdentityModel.Tokens.Telemetry.IdentityModelTelemetry.RecordConfigurationRetrievalDurationHistogram(long requestDurationInMs, in System.Diagnostics.TagList tagList) -> void 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.LoggingEventId.TokenValidationFailed -> Microsoft.Extensions.Logging.EventId +static readonly Microsoft.IdentityModel.Tokens.LoggingEventId.TokenValidationSucceeded -> Microsoft.Extensions.Logging.EventId +static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.AlgorithmValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType +static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.AudienceValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType +static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.IssuerSigningKeyValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType static readonly Microsoft.IdentityModel.Tokens.Telemetry.IdentityModelTelemetry.ConfigurationManagerCounter -> System.Diagnostics.Metrics.Counter static readonly Microsoft.IdentityModel.Tokens.Telemetry.IdentityModelTelemetry.IdentityModelMeter -> System.Diagnostics.Metrics.Meter static readonly Microsoft.IdentityModel.Tokens.Telemetry.IdentityModelTelemetry.TotalDurationHistogram -> System.Diagnostics.Metrics.Histogram @@ -82,7 +111,9 @@ static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.LifetimeVal 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.TokenReplayValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.TokenTypeValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.XmlValidationFailed -> Microsoft.IdentityModel.Tokens.ValidationFailureType diff --git a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs index 59c48703f3..0d10c18695 100644 --- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs @@ -88,7 +88,13 @@ 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 IDX10270 = "IDX10270: AudienceValidationDelegate threw an exception, see inner exception."; + public const string IDX10271 = "IDX10271: LifetimeValidationDelegate 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 IDX10274 = "IDX10274: IssuerSigningKeyValidationDelegate threw an exception, see inner exception."; + public const string IDX10275 = "IDX10275: TokenTypeValidationDelegate threw an exception, see inner exception."; + public const string IDX10276 = "IDX10276: TokenReplayValidationDelegate threw an exception, see inner exception."; // 10500 - SignatureValidation public const string IDX10500 = "IDX10500: Signature validation failed. No security keys were provided to validate the signature."; diff --git a/src/Microsoft.IdentityModel.Tokens/LoggingEventId.cs b/src/Microsoft.IdentityModel.Tokens/LoggingEventId.cs new file mode 100644 index 0000000000..aa682a3dd5 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/LoggingEventId.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Extensions.Logging; + +namespace Microsoft.IdentityModel.Tokens +{ + internal static class LoggingEventId + { + // TokenValidation EventIds 100+ + internal static readonly EventId TokenValidationFailed = new(100, "TokenValidationFailed"); + internal static readonly EventId TokenValidationSucceeded = new(101, "TokenValidationSucceeded"); + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Microsoft.IdentityModel.Tokens.csproj b/src/Microsoft.IdentityModel.Tokens/Microsoft.IdentityModel.Tokens.csproj index 459a772334..2e3b454bbf 100644 --- a/src/Microsoft.IdentityModel.Tokens/Microsoft.IdentityModel.Tokens.csproj +++ b/src/Microsoft.IdentityModel.Tokens/Microsoft.IdentityModel.Tokens.csproj @@ -72,5 +72,9 @@ + + + + diff --git a/src/Microsoft.IdentityModel.Tokens/SignatureProvider.cs b/src/Microsoft.IdentityModel.Tokens/SignatureProvider.cs index 4f011c9c97..f6423c4870 100644 --- a/src/Microsoft.IdentityModel.Tokens/SignatureProvider.cs +++ b/src/Microsoft.IdentityModel.Tokens/SignatureProvider.cs @@ -147,11 +147,11 @@ public virtual bool Sign(ReadOnlySpan data, Span destination, out in /// Verifies that a signature created over the 'input' matches the signature. Using and 'algorithm' passed to . /// /// The bytes to verify. - /// offset in to input bytes to caculate hash. + /// offset in to input bytes to calculate hash. /// number of bytes of signature to use. /// signature to compare against. /// offset into signature array. - /// how many bytes to verfiy. + /// how many bytes to verify. /// true if computed signature matches the signature parameter, false otherwise. /// 'input' is null. /// 'signature' is null. diff --git a/src/Microsoft.IdentityModel.Tokens/SymmetricSignatureProvider.cs b/src/Microsoft.IdentityModel.Tokens/SymmetricSignatureProvider.cs index bcc4f4a198..72f12a05a8 100644 --- a/src/Microsoft.IdentityModel.Tokens/SymmetricSignatureProvider.cs +++ b/src/Microsoft.IdentityModel.Tokens/SymmetricSignatureProvider.cs @@ -73,13 +73,29 @@ public SymmetricSignatureProvider(SecurityKey key, string algorithm, bool willCr : base(key, algorithm) { if (!key.CryptoProviderFactory.IsSupportedAlgorithm(algorithm, key)) - throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10634, LogHelper.MarkAsNonPII((algorithm)), key))); + throw LogHelper.LogExceptionMessage( + new NotSupportedException( + LogHelper.FormatInvariant( + LogMessages.IDX10634, + LogHelper.MarkAsNonPII((algorithm)), key))); if (key.KeySize < MinimumSymmetricKeySizeInBits) - throw LogHelper.LogExceptionMessage(new ArgumentOutOfRangeException(nameof(key), LogHelper.FormatInvariant(LogMessages.IDX10653, LogHelper.MarkAsNonPII((algorithm)), LogHelper.MarkAsNonPII(MinimumSymmetricKeySizeInBits), key, LogHelper.MarkAsNonPII(key.KeySize)))); + throw LogHelper.LogExceptionMessage( + new ArgumentOutOfRangeException( + nameof(key), + LogHelper.FormatInvariant( + LogMessages.IDX10653, + LogHelper.MarkAsNonPII( + (algorithm)), + LogHelper.MarkAsNonPII( + MinimumSymmetricKeySizeInBits), + key, + LogHelper.MarkAsNonPII(key.KeySize)))); WillCreateSignatures = willCreateSignatures; - _keyedHashObjectPool = new DisposableObjectPool(CreateKeyedHashAlgorithm, key.CryptoProviderFactory.SignatureProviderObjectPoolCacheSize); + _keyedHashObjectPool = new DisposableObjectPool( + CreateKeyedHashAlgorithm, + key.CryptoProviderFactory.SignatureProviderObjectPoolCacheSize); } /// @@ -95,7 +111,12 @@ public int MinimumSymmetricKeySizeInBits set { if (value < DefaultMinimumSymmetricKeySizeInBits) - throw LogHelper.LogExceptionMessage(new ArgumentOutOfRangeException(nameof(value), LogHelper.FormatInvariant(LogMessages.IDX10628, LogHelper.MarkAsNonPII(DefaultMinimumSymmetricKeySizeInBits)))); + throw LogHelper.LogExceptionMessage( + new ArgumentOutOfRangeException( + nameof(value), + LogHelper.FormatInvariant( + LogMessages.IDX10628, + LogHelper.MarkAsNonPII(DefaultMinimumSymmetricKeySizeInBits)))); _minimumSymmetricKeySizeInBits = value; } @@ -130,7 +151,7 @@ protected virtual byte[] GetKeyBytes(SecurityKey key) /// Returns a . /// This method is called just before a cryptographic operation. /// This provides the opportunity to obtain the from an object pool. - /// If this method is overridden, it is importont to override + /// If this method is overridden, it is important to override /// if custom releasing of the is desired. /// /// The hash algorithm to use to create the hash value. @@ -164,14 +185,16 @@ protected virtual void ReleaseKeyedHashAlgorithm(KeyedHashAlgorithm keyedHashAlg } /// - /// Produces a signature over the 'input' using the and 'algorithm' passed to . + /// Produces a signature over the 'input' using the and 'algorithm' + /// passed to . /// /// The bytes to sign. /// Signed bytes /// 'input' is null. /// 'input.Length' == 0. /// has been called. - /// is null. This can occur if a derived type deletes it or does not create it. + /// is null. + /// This can occur if a derived type deletes it or does not create it. /// Sign is thread safe. public override byte[] Sign(byte[] input) { @@ -268,7 +291,8 @@ public override byte[] Sign(byte[] input, int offset, int count) } /// - /// Verifies that a signature created over the 'input' matches the signature. Using and 'algorithm' passed to . + /// Verifies that a signature created over the 'input' matches the signature. Using and 'algorithm' + /// passed to . /// /// The bytes to verify. /// signature to compare against. @@ -278,7 +302,8 @@ public override byte[] Sign(byte[] input, int offset, int count) /// 'input.Length' == 0. /// 'signature.Length' == 0. /// has been called. - /// If the internal is null. This can occur if a derived type deletes it or does not create it. + /// If the internal is null. + /// This can occur if a derived type deletes it or does not create it. /// Verify is thread safe. public override bool Verify(byte[] input, byte[] signature) { @@ -315,7 +340,8 @@ public override bool Verify(byte[] input, byte[] signature) } /// - /// Verifies that a signature created over the 'input' matches the signature. Using and 'algorithm' passed to . + /// Verifies that a signature created over the 'input' matches the signature. Using and 'algorithm' + /// passed to . /// /// The bytes to verify. /// signature to compare against. @@ -327,7 +353,8 @@ public override bool Verify(byte[] input, byte[] signature) /// 'signature.Length' == 0. /// 'length < 1' /// has been called. - /// If the internal is null. This can occur if a derived type deletes it or does not create it. + /// If the internal is null. + /// This can occur if a derived type deletes it or does not create it. public bool Verify(byte[] input, byte[] signature, int length) { if (input == null) @@ -343,22 +370,31 @@ public override bool Verify(byte[] input, int inputOffset, int inputLength, byte } /// - /// This internal method is called from the AuthenticatedEncryptionProvider which passes in the algorithm that defines the size expected for the signature. + /// This internal method is called from the AuthenticatedEncryptionProvider which passes in the algorithm that defines + /// the size expected for the signature. /// The reason is the way the AuthenticationTag is validated. - /// For example when "A128CBC-HS256" is specified, SHA256 will used to create the HMAC and 32 bytes will be generated, but only the first 16 will be validated. + /// For example when "A128CBC-HS256" is specified, SHA256 will used to create the HMAC and 32 bytes will be generated, + /// but only the first 16 will be validated. /// /// The bytes to verify. - /// offset in to input bytes to caculate hash. + /// offset into the input bytes to calculate the hash. /// number of bytes of signature to use. /// signature to compare against. /// offset into signature array. - /// how many bytes to verfiy. + /// how many bytes to verify. /// algorithm passed by AuthenticatedEncryptionProvider. /// true if computed signature matches the signature parameter, false otherwise. #if NET6_0_OR_GREATER [SkipLocalsInit] #endif - internal bool Verify(byte[] input, int inputOffset, int inputLength, byte[] signature, int signatureOffset, int signatureLength, string algorithm) + internal bool Verify( + byte[] input, + int inputOffset, + int inputLength, + byte[] signature, + int signatureOffset, + int signatureLength, + string algorithm) { if (input == null || input.Length == 0) throw LogHelper.LogArgumentNullException(nameof(input)); @@ -448,7 +484,8 @@ internal bool Verify(byte[] input, int inputOffset, int inputLength, byte[] sign scoped Span hash; #if NET6_0_OR_GREATER - hash = stackalloc byte[keyedHashAlgorithm.HashSize / 8]; // only known algorithms are used, all of which have a small enough hash size to stackalloc + // only known algorithms are used, all of which have a small enough hash size to stackalloc + hash = stackalloc byte[keyedHashAlgorithm.HashSize / 8]; keyedHashAlgorithm.TryComputeHash(input.AsSpan(inputOffset, inputLength), hash, out int bytesWritten); Debug.Assert(bytesWritten == hash.Length); #else 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/IssuerSigningKeyValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/IssuerSigningKeyValidationError.cs new file mode 100644 index 0000000000..8f380847ae --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/IssuerSigningKeyValidationError.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Diagnostics; +using System; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens +{ + internal class IssuerSigningKeyValidationError : ValidationError + { + internal IssuerSigningKeyValidationError( + MessageDetail messageDetail, + ValidationFailureType validationFailureType, + Type exceptionType, + StackFrame stackFrame, + SecurityKey? invalidSigningKey, + Exception? innerException = null) + : base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException) + { + InvalidSigningKey = invalidSigningKey; + } + + internal override Exception GetException() + { + if (ExceptionType == typeof(SecurityTokenInvalidSigningKeyException)) + { + SecurityTokenInvalidSigningKeyException? exception = new(MessageDetail.Message, InnerException) + { + SigningKey = InvalidSigningKey + }; + exception.SetValidationError(this); + + return exception; + } + + return base.GetException(); + } + + internal static new IssuerSigningKeyValidationError NullParameter(string parameterName, StackFrame stackFrame) => new( + MessageDetail.NullParameter(parameterName), + ValidationFailureType.NullArgument, + typeof(SecurityTokenArgumentNullException), + stackFrame, + null); // InvalidSigningKey + + protected SecurityKey? InvalidSigningKey { 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/TokenReplayValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenReplayValidationError.cs new file mode 100644 index 0000000000..02d2c53d4a --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenReplayValidationError.cs @@ -0,0 +1,54 @@ +// 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 TokenReplayValidationError : ValidationError + { + internal TokenReplayValidationError( + MessageDetail messageDetail, + ValidationFailureType validationFailureType, + Type exceptionType, + StackFrame stackFrame, + DateTime? expirationTime, + Exception? innerException = null) + : base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException) + { + ExpirationTime = expirationTime; + } + + internal override Exception GetException() + { + if (ExceptionType == typeof(SecurityTokenReplayDetectedException)) + { + SecurityTokenReplayDetectedException exception = new(MessageDetail.Message, InnerException); + exception.SetValidationError(this); + + return exception; + } + else if (ExceptionType == typeof(SecurityTokenReplayAddFailedException)) + { + SecurityTokenReplayAddFailedException exception = new(MessageDetail.Message, InnerException); + exception.SetValidationError(this); + + return exception; + } + + return base.GetException(); + } + + internal static new TokenReplayValidationError NullParameter(string parameterName, StackFrame stackFrame) => new( + MessageDetail.NullParameter(parameterName), + ValidationFailureType.NullArgument, + typeof(SecurityTokenArgumentNullException), + stackFrame, + null); + + protected DateTime? ExpirationTime { get; } + } +} +#nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenTypeValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenTypeValidationError.cs index b97697e248..298abeefce 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenTypeValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenTypeValidationError.cs @@ -9,8 +9,6 @@ namespace Microsoft.IdentityModel.Tokens { internal class TokenTypeValidationError : ValidationError { - protected string? _invalidTokenType; - internal TokenTypeValidationError( MessageDetail messageDetail, ValidationFailureType validationFailureType, @@ -20,7 +18,7 @@ internal TokenTypeValidationError( Exception? innerException = null) : base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException) { - _invalidTokenType = invalidTokenType; + InvalidTokenType = invalidTokenType; } internal override Exception GetException() @@ -29,14 +27,24 @@ internal override Exception GetException() { SecurityTokenInvalidTypeException exception = new(MessageDetail.Message, InnerException) { - InvalidType = _invalidTokenType + InvalidType = InvalidTokenType }; + exception.SetValidationError(this); return exception; } return base.GetException(); } + + internal static new TokenTypeValidationError NullParameter(string parameterName, StackFrame stackFrame) => new( + MessageDetail.NullParameter(parameterName), + ValidationFailureType.NullArgument, + typeof(SecurityTokenArgumentNullException), + stackFrame, + null); // invalidTokenType + + protected string? InvalidTokenType { 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..b4e0a6714f 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; +using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Logging; #nullable enable @@ -22,13 +23,13 @@ internal class ValidationError /// Creates an instance of /// /// contains information about the exception that is used to generate the exception message. - /// is the type of validation failure that occurred. + /// is the type of validation failure that occurred. /// is the type of exception that occurred. /// is the stack frame where the exception occurred. /// is the inner exception that occurred. internal ValidationError( MessageDetail messageDetail, - ValidationFailureType failureType, + ValidationFailureType validationFailureType, Type exceptionType, StackFrame stackFrame, Exception? innerException = null) @@ -36,7 +37,7 @@ internal ValidationError( InnerException = innerException; MessageDetail = messageDetail; _exceptionType = exceptionType; - FailureType = failureType; + FailureType = validationFailureType; StackFrames = new List(4) { stackFrame @@ -66,6 +67,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 +126,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)) @@ -179,6 +184,11 @@ internal Exception GetException(Type exceptionType, Exception? innerException) return exception; } + internal void Log(ILogger logger) + { + Logger.TokenValidationFailed(logger, FailureType.Name, MessageDetail.Message); + } + internal static ValidationError NullParameter(string parameterName, StackFrame stackFrame) => new( MessageDetail.NullParameter(parameterName), ValidationFailureType.NullArgument, @@ -257,6 +267,27 @@ internal static StackFrame GetCurrentStackFrame( // ConcurrentDictionary is thread-safe and only locks when adding a new item. private static ConcurrentDictionary CachedStackFrames { get; } = new(); + + private static class Logger + { + private static readonly Action s_tokenValidationFailed = + LoggerMessage.Define( + LogLevel.Information, + LoggingEventId.TokenValidationFailed, + "[MsIdentityModel] The token validation was unsuccessful due to: {ValidationFailureType} " + + "Error message provided: {ValidationErrorMessage}"); + + /// + /// Logger for handling failures in token validation. + /// + /// ILogger. + /// The cause of the failure. + /// The message provided as part of the failure. + public static void TokenValidationFailed( + ILogger logger, + string validationFailureType, + string messageDetail) => s_tokenValidationFailed(logger, validationFailureType, messageDetail, null); + } } } #nullable restore diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs index 3722fc4e8e..7eccd96c75 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/ValidatedToken.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Security.Claims; using System.Threading; +using Microsoft.Extensions.Logging; #nullable enable namespace Microsoft.IdentityModel.Tokens @@ -34,11 +35,17 @@ internal ValidatedToken( /// /// Logs the validation result. /// -#pragma warning disable CA1822 // Mark members as static - public void Log() -#pragma warning restore CA1822 // Mark members as static + public void Log(ILogger logger) { - // TODO - Do we need this, how will it work? + Logger.TokenValidationSucceeded( + logger, + ValidatedAudience ?? "none", + ValidatedLifetime, + ValidatedIssuer, + ValidatedTokenType, + ValidatedSigningKey?.KeyId ?? "none", + ActorValidationResult is not null + ); } public SecurityToken SecurityToken { get; private set; } @@ -168,6 +175,68 @@ private object ClaimsIdentitySyncObj } } #endregion + + #region Logging + private static class Logger + { + private static readonly Action s_tokenValidationFailed = + LoggerMessage.Define( + LogLevel.Information, + LoggingEventId.TokenValidationFailed, + "[MsIdentityModel] The token validation was unsuccessful due to: {ValidationFailureType} " + + "Error message provided: {ValidationErrorMessage}"); + + /// + /// Logger for handling failures in token validation. + /// + /// ILogger. + /// The cause of the failure. + /// The message provided as part of the failure. + public static void TokenValidationFailed( + ILogger logger, + ValidationFailureType validationFailureType, + MessageDetail messageDetail) => s_tokenValidationFailed(logger, validationFailureType.Name, messageDetail.Message, null); + + private static readonly Action s_tokenValidationSucceeded = + LoggerMessage.Define( + LogLevel.Debug, + LoggingEventId.TokenValidationSucceeded, + "[MsIdentityModel] The token validation was successful. " + + "Validated audience: {ValidatedAudience} " + + "Validated lifetime: {ValidatedLifetime} " + + "Validated issuer: {ValidatedIssuer} " + + "Validated token type: {ValidatedTokenType} " + + "Validated signing key id: {ValidatedSigningKeyId} " + + "Actor was validated: {ActorWasValidated}"); + + /// + /// Logger for handling successful token validation. + /// + /// The instance to be used to log. + /// The audience that was validated. + /// The lifetime that was validated. + /// The issuer that was validated. + /// The token type that was validated. + /// The signing key id that was validated. + /// Whether the actor was validated. + public static void TokenValidationSucceeded( + ILogger logger, + string validatedAudience, + ValidatedLifetime? validatedLifetime, + ValidatedIssuer? validatedIssuer, + ValidatedTokenType? validatedTokenType, + string validatedSigningKeyId, + bool actorWasValidated) => s_tokenValidationSucceeded( + logger, + validatedAudience, + validatedLifetime, + validatedIssuer, + validatedTokenType, + validatedSigningKeyId, + actorWasValidated, + null); + } + #endregion } } #nullable disable diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs index e512d2af5d..2556fea941 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs @@ -84,8 +84,8 @@ private class SignatureAlgorithmValidationFailure : ValidationFailureType { inte /// /// Defines a type that represents that signing key validation failed. /// - public static readonly ValidationFailureType SigningKeyValidationFailed = new SigningKeyValidationFailure("SigningKeyValidationFailed"); - private class SigningKeyValidationFailure : ValidationFailureType { internal SigningKeyValidationFailure(string name) : base(name) { } } + public static readonly ValidationFailureType SigningKeyValidationFailed = new IssuerSigningKeyValidationFailure("IssuerSigningKeyValidationFailed"); + private class IssuerSigningKeyValidationFailure : ValidationFailureType { internal IssuerSigningKeyValidationFailure(string name) : base(name) { } } /// /// Defines a type that represents that lifetime validation failed. @@ -130,9 +130,44 @@ private class InvalidSecurityTokenFailure : ValidationFailureType { internal Inv private class XmlValidationFailure : ValidationFailureType { internal XmlValidationFailure(string name) : base(name) { } } /// - /// Defines a type that represents that a token is invalid. + /// 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 the fact that the audience validation delegate threw an exception. + /// + public static readonly ValidationFailureType AudienceValidatorThrew = new AudienceValidationFailure("AudienceValidatorThrew"); + + /// + /// Defines a type that represents the fact that the issuer validation delegate threw an exception. /// 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 lifetime validation delegate threw an exception. + /// + public static readonly ValidationFailureType LifetimeValidatorThrew = new LifetimeValidationFailure("LifetimeValidatorThrew"); + + /// + /// Defines a type that represents the fact that the issuer signing key validation delegate threw an exception. + /// + public static readonly ValidationFailureType IssuerSigningKeyValidatorThrew = new IssuerSigningKeyValidationFailure("IssuerSigningKeyValidatorThrew"); + + /// + /// 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 replay validation delegate threw an exception. + /// + public static readonly ValidationFailureType TokenReplayValidatorThrew = new TokenReplayValidationFailure("TokenReplayValidatorThrew"); + + /// + /// Defines a type that represents the fact that the token type validation delegate threw an exception. + /// + public static readonly ValidationFailureType TokenTypeValidatorThrew = new TokenTypeValidationFailure("TokenTypeValidatorThrew"); } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs index 3e27f653e4..6e4ef95132 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.IssuerSigningKey.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using System.Security.Cryptography.X509Certificates; using Microsoft.IdentityModel.Logging; @@ -26,7 +25,7 @@ internal delegate ValidationResult IssuerSigningKey SecurityToken securityToken, ValidationParameters validationParameters, BaseConfiguration? configuration, - CallContext? callContext); + CallContext callContext); /// /// SigningKeyValidation @@ -55,21 +54,22 @@ internal static ValidationResult ValidateIssuerSign CallContext? callContext) { if (validationParameters == null) - return ValidationError.NullParameter( + return IssuerSigningKeyValidationError.NullParameter( nameof(validationParameters), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame()); if (securityKey == null) - return new ValidationError( + return new IssuerSigningKeyValidationError( new MessageDetail(LogMessages.IDX10253, nameof(securityKey)), ValidationFailureType.SigningKeyValidationFailed, typeof(SecurityTokenArgumentNullException), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame(), + securityKey); if (securityToken == null) - return ValidationError.NullParameter( + return IssuerSigningKeyValidationError.NullParameter( nameof(securityToken), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame()); return ValidateIssuerSigningKeyLifeTime(securityKey, validationParameters, callContext); } @@ -98,28 +98,30 @@ internal static ValidationResult ValidateIssuerSign notAfterUtc = cert.NotAfter.ToUniversalTime(); if (notBeforeUtc > DateTimeUtil.Add(utcNow, validationParameters.ClockSkew)) - return new ValidationError( + return new IssuerSigningKeyValidationError( new MessageDetail( LogMessages.IDX10248, LogHelper.MarkAsNonPII(notBeforeUtc), LogHelper.MarkAsNonPII(utcNow)), ValidationFailureType.SigningKeyValidationFailed, typeof(SecurityTokenInvalidSigningKeyException), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame(), + securityKey); //TODO: Move to CallContext //if (LogHelper.IsEnabled(EventLogLevel.Informational)) // LogHelper.LogInformation(LogMessages.IDX10250, LogHelper.MarkAsNonPII(notBeforeUtc), LogHelper.MarkAsNonPII(utcNow)); if (notAfterUtc < DateTimeUtil.Add(utcNow, validationParameters.ClockSkew.Negate())) - return new ValidationError( + return new IssuerSigningKeyValidationError( new MessageDetail( LogMessages.IDX10249, LogHelper.MarkAsNonPII(notAfterUtc), LogHelper.MarkAsNonPII(utcNow)), ValidationFailureType.SigningKeyValidationFailed, typeof(SecurityTokenInvalidSigningKeyException), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame(), + securityKey); // TODO: Move to CallContext //if (LogHelper.IsEnabled(EventLogLevel.Informational)) diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs index 064a6cc491..3e8de4ffd9 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenReplay.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; namespace Microsoft.IdentityModel.Tokens { @@ -43,44 +42,47 @@ public static partial class Validators #pragma warning restore CA1801 // Review unused parameters { if (string.IsNullOrWhiteSpace(securityToken)) - return ValidationError.NullParameter( + return TokenReplayValidationError.NullParameter( nameof(securityToken), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame()); if (validationParameters == null) - return ValidationError.NullParameter( + return TokenReplayValidationError.NullParameter( nameof(validationParameters), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame()); // check if token if replay cache is set, then there must be an expiration time. if (validationParameters.TokenReplayCache != null) { if (expirationTime == null) - return new ValidationError( + return new TokenReplayValidationError( new MessageDetail( LogMessages.IDX10227, securityToken), ValidationFailureType.TokenReplayValidationFailed, typeof(SecurityTokenNoExpirationException), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame(), + expirationTime); if (validationParameters.TokenReplayCache.TryFind(securityToken)) - return new ValidationError( + return new TokenReplayValidationError( new MessageDetail( LogMessages.IDX10228, securityToken), ValidationFailureType.TokenReplayValidationFailed, typeof(SecurityTokenReplayDetectedException), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame(), + expirationTime); if (!validationParameters.TokenReplayCache.TryAdd(securityToken, expirationTime.Value)) - return new ValidationError( + return new TokenReplayValidationError( new MessageDetail( LogMessages.IDX10229, securityToken), ValidationFailureType.TokenReplayValidationFailed, typeof(SecurityTokenReplayAddFailedException), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame(), + expirationTime); } // if it reaches here, that means no token replay is detected. diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs index fc685240d5..a95a60b363 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.TokenType.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using System.Linq; using Microsoft.IdentityModel.Logging; @@ -45,14 +44,14 @@ internal static ValidationResult ValidateTokenType( #pragma warning restore CA1801 // TODO: remove pragma disable once callContext is used for logging { if (securityToken == null) - return ValidationError.NullParameter( + return TokenTypeValidationError.NullParameter( nameof(securityToken), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame()); if (validationParameters == null) - return ValidationError.NullParameter( + return TokenTypeValidationError.NullParameter( nameof(validationParameters), - new StackFrame(true)); + ValidationError.GetCurrentStackFrame()); if (validationParameters.ValidTypes.Count == 0) { @@ -66,7 +65,7 @@ internal static ValidationResult ValidateTokenType( new MessageDetail(LogMessages.IDX10256), ValidationFailureType.TokenTypeValidationFailed, typeof(SecurityTokenInvalidTypeException), - new StackFrame(true), + ValidationError.GetCurrentStackFrame(), null); // even if it is empty, we report null to match the original behaviour. if (!validationParameters.ValidTypes.Contains(type, StringComparer.Ordinal)) @@ -78,7 +77,7 @@ internal static ValidationResult ValidateTokenType( LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidTypes))), ValidationFailureType.TokenTypeValidationFailed, typeof(SecurityTokenInvalidTypeException), - new StackFrame(true), + ValidationError.GetCurrentStackFrame(), type); } diff --git a/src/Microsoft.IdentityModel.Tokens/Validators.cs b/src/Microsoft.IdentityModel.Tokens/Validators.cs index 157588bd51..49946550cd 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validators.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validators.cs @@ -409,13 +409,13 @@ internal static void ValidateIssuerSigningKeyLifeTime(SecurityKey securityKey, T var notAfterUtc = cert.NotAfter.ToUniversalTime(); if (notBeforeUtc > DateTimeUtil.Add(utcNow, validationParameters.ClockSkew)) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10248, LogHelper.MarkAsNonPII(notBeforeUtc), LogHelper.MarkAsNonPII(utcNow)))); + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10248, LogHelper.MarkAsNonPII(notBeforeUtc), LogHelper.MarkAsNonPII(utcNow))) { SigningKey = securityKey }); if (LogHelper.IsEnabled(EventLogLevel.Informational)) LogHelper.LogInformation(LogMessages.IDX10250, LogHelper.MarkAsNonPII(notBeforeUtc), LogHelper.MarkAsNonPII(utcNow)); if (notAfterUtc < DateTimeUtil.Add(utcNow, validationParameters.ClockSkew.Negate())) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10249, LogHelper.MarkAsNonPII(notAfterUtc), LogHelper.MarkAsNonPII(utcNow)))); + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10249, LogHelper.MarkAsNonPII(notAfterUtc), LogHelper.MarkAsNonPII(utcNow))) { SigningKey = securityKey }); if (LogHelper.IsEnabled(EventLogLevel.Informational)) LogHelper.LogInformation(LogMessages.IDX10251, LogHelper.MarkAsNonPII(notAfterUtc), LogHelper.MarkAsNonPII(utcNow)); diff --git a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs index dd95322e06..72771086b5 100644 --- a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs +++ b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs @@ -399,7 +399,7 @@ internal static bool IsValidIssuer(string issuerTemplate, string tenantId, strin return false; // Ensure tokenIssuer is atleast as long as issuerTemplate with tenantIdTemplate replaced - if (tokenIssuer.Length <= templateTenantIdPosition + tenantId.Length) + if (tokenIssuer.Length < templateTenantIdPosition + tenantId.Length) return false; // Ensure the tenant ID in the token issuer matches the expected tenant ID 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.AotCompatibility.Tests/AotCompatibilityTests.cs b/test/Microsoft.IdentityModel.AotCompatibility.Tests/AotCompatibilityTests.cs deleted file mode 100644 index 1c5af61ae1..0000000000 --- a/test/Microsoft.IdentityModel.AotCompatibility.Tests/AotCompatibilityTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Diagnostics; -using System.IO; -using Xunit; -using Xunit.Abstractions; - -namespace Microsoft.IdentityModel.AotCompatibility.Tests -{ - public class AotCompatibilityTests - { - private ITestOutputHelper _testOutputHelper; - - public AotCompatibilityTests(ITestOutputHelper testOutputHelper) - { - _testOutputHelper = testOutputHelper; - } - - /// - /// This test ensures that the intended APIs of the Microsoft.IdentityModel libraries are - /// trimming and NativeAOT compatible. - /// - /// This test follows the instructions in https://learn.microsoft.com/dotnet/core/deploying/trimming/prepare-libraries-for-trimming#show-all-warnings-with-sample-application - /// - /// If this test fails, it is due to adding trimming and/or AOT incompatible changes - /// to code that is supposed to be compatible. - /// - /// To diagnose the problem, inspect the test output which will contain the trimming and AOT errors. For example: - /// - /// error IL2091: 'T' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors' - /// - /// You can also 'dotnet publish' the 'Microsoft.IdentityModel.AotCompatibility.TestApp.csproj' as well to get the errors. - /// - [IgnoreOnAzureDevopsFact] - public void EnsureAotCompatibility() - { - string testAppPath = Path.Combine("..", "..", "..", "..", "Microsoft.IdentityModel.AotCompatibility.TestApp"); - string testAppProject = "Microsoft.IdentityModel.AotCompatibility.TestApp.csproj"; - -#if NET9_0_OR_GREATER - string framework = "net9.0"; -#else - string framework = "net8.0"; -#endif - - // ensure we run a clean publish every time - DirectoryInfo testObjDir = new DirectoryInfo(Path.Combine(testAppPath, "obj")); - if (testObjDir.Exists) - { - testObjDir.Delete(recursive: true); - } - - var process = new Process(); - // set '-nodereuse:false /p:UseSharedCompilation=false' so the MSBuild and Roslyn server processes don't hang around, which may hang the test in CI - process.StartInfo = new ProcessStartInfo("dotnet", $"publish {testAppProject} --self-contained --framework {framework} -nodereuse:false /p:UseSharedCompilation=false") - { - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true, - WorkingDirectory = testAppPath - }; - process.OutputDataReceived += (sender, eventArgs) => - { - if (eventArgs.Data is not null) - _testOutputHelper.WriteLine(eventArgs.Data); - }; - process.Start(); - process.BeginOutputReadLine(); - - Assert.True(process.WaitForExit(milliseconds: 180_000), "dotnet publish command timed out after 3 minutes."); - - Assert.True(process.ExitCode == 0, "Publishing the AotCompatibility app failed. See test output for more details."); - } - } -} diff --git a/test/Microsoft.IdentityModel.AotCompatibility.Tests/IgnoreOnAzureDevopsFactAttribute.cs b/test/Microsoft.IdentityModel.AotCompatibility.Tests/IgnoreOnAzureDevopsFactAttribute.cs deleted file mode 100644 index cc3c994837..0000000000 --- a/test/Microsoft.IdentityModel.AotCompatibility.Tests/IgnoreOnAzureDevopsFactAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using Xunit; - -namespace Microsoft.IdentityModel.AotCompatibility.Tests -{ - public sealed class IgnoreOnAzureDevopsFactAttribute : FactAttribute - { - public IgnoreOnAzureDevopsFactAttribute() - { - if (!IsRunningOnAzureDevOps()) - { - return; - } - - Skip = "Ignored on Azure DevOps"; - } - - /// Determine if runtime is Azure DevOps. - /// True if being executed in Azure DevOps, false otherwise. - public static bool IsRunningOnAzureDevOps() - { - return Environment.GetEnvironmentVariable("SYSTEM_DEFINITIONID") != null; - } - } -} diff --git a/test/Microsoft.IdentityModel.AotCompatibility.Tests/Microsoft.IdentityModel.AotCompatibility.Tests.csproj b/test/Microsoft.IdentityModel.AotCompatibility.Tests/Microsoft.IdentityModel.AotCompatibility.Tests.csproj deleted file mode 100644 index c72cdd01e8..0000000000 --- a/test/Microsoft.IdentityModel.AotCompatibility.Tests/Microsoft.IdentityModel.AotCompatibility.Tests.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - net8.0;net9.0 - $(TargetFrameworks); - 12 - 1.0.0-preview - - - - - - - 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..7de840b3c3 --- /dev/null +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Algorithm.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.JsonWebTokens.Extensibility.Tests +{ + public partial class JsonWebTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateAlgorithmExtensibilityTestCases), + parameters: ["JWT", 1], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_AlgorithmValidator_Extensibility( + AlgorithmExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_AlgorithmValidator_Extensibility)); + } + + public static TheoryData GenerateAlgorithmExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateAlgorithmExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "JsonWebTokenHandler.ValidateSignature.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Audience.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Audience.cs new file mode 100644 index 0000000000..d0d7e02b77 --- /dev/null +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Audience.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.JsonWebTokens.Extensibility.Tests +{ + public partial class JsonWebTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateAudienceExtensibilityTestCases), + parameters: ["JWT", 2], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_AudienceValidator_Extensibility( + AudienceExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_AudienceValidator_Extensibility)); + } + + public static TheoryData GenerateAudienceExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateAudienceExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "JsonWebTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Issuer.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Issuer.cs new file mode 100644 index 0000000000..37bf08994d --- /dev/null +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Issuer.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.JsonWebTokens.Extensibility.Tests +{ + public partial class JsonWebTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateIssuerExtensibilityTestCases), + parameters: ["JWT", 2], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_IssuerValidator_Extensibility( + IssuerExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_IssuerValidator_Extensibility)); + } + + public static TheoryData GenerateIssuerExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateIssuerExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "JsonWebTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.IssuerSigningKey.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.IssuerSigningKey.cs new file mode 100644 index 0000000000..fb24e4a51f --- /dev/null +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.IssuerSigningKey.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.JsonWebTokens.Extensibility.Tests +{ + public partial class JsonWebTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateIssuerSigningKeyExtensibilityTestCases), + parameters: ["JWT", 2], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_IssuerSigningKeyValidator_Extensibility( + IssuerSigningKeyExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_IssuerSigningKeyValidator_Extensibility)); + } + + public static TheoryData GenerateIssuerSigningKeyExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateIssuerSigningKeyExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "JsonWebTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Lifetime.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Lifetime.cs new file mode 100644 index 0000000000..9ce18c744d --- /dev/null +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Lifetime.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.JsonWebTokens.Extensibility.Tests +{ + public partial class JsonWebTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateLifetimeExtensibilityTestCases), + parameters: ["JWT", 2], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_LifetimeValidator_Extensibility( + LifetimeExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_LifetimeValidator_Extensibility)); + } + + public static TheoryData GenerateLifetimeExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateLifetimeExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "JsonWebTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#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..8c6c8a75d0 --- /dev/null +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.Signature.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.JsonWebTokens.Extensibility.Tests +{ + public partial class JsonWebTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateSignatureExtensibilityTestCases), + parameters: ["JWT", 3], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_SignatureValidator_Extensibility( + SignatureExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_SignatureValidator_Extensibility)); + } + + public static TheoryData GenerateSignatureExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateSignatureExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "JsonWebTokenHandler.ValidateSignature.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.TokenReplay.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.TokenReplay.cs new file mode 100644 index 0000000000..7e17dd4ca8 --- /dev/null +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.TokenReplay.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.JsonWebTokens.Extensibility.Tests +{ + public partial class JsonWebTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateTokenReplayExtensibilityTestCases), + parameters: ["JWT", 2], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_TokenReplayValidator_Extensibility( + TokenReplayExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_TokenReplayValidator_Extensibility)); + } + + public static TheoryData GenerateTokenReplayExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateTokenReplayExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "JsonWebTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.TokenType.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.TokenType.cs new file mode 100644 index 0000000000..ecc7f63439 --- /dev/null +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Extensibility.TokenType.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.JsonWebTokens.Extensibility.Tests +{ + public partial class JsonWebTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateTokenTypeExtensibilityTestCases), + parameters: ["JWT", 2], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_TokenTypeValidator_Extensibility( + TokenTypeExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_TokenTypeValidator_Extensibility)); + } + + public static TheoryData GenerateTokenTypeExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateTokenTypeExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "JsonWebTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Issuer.Extensibility.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Issuer.Extensibility.cs deleted file mode 100644 index bb14541c7d..0000000000 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Issuer.Extensibility.cs +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.IdentityModel.JsonWebTokens.Tests; -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(Issuer_ExtensibilityTestCases), DisableDiscoveryEnumeration = true)] - public async Task ValidateTokenAsync_IssuerValidator_Extensibility(IssuerExtensibilityTheoryData theoryData) - { - var context = TestUtilities.WriteHeader($"{this}.{nameof(ValidateTokenAsync_IssuerValidator_Extensibility)}", theoryData); - context.IgnoreType = false; - for (int i = 1; i < theoryData.StackFrames.Count; i++) - theoryData.IssuerValidationError!.AddStackFrame(theoryData.StackFrames[i]); - - try - { - ValidationResult validationResult = await theoryData.JsonWebTokenHandler.ValidateTokenAsync( - theoryData.JsonWebToken!, - theoryData.ValidationParameters!, - theoryData.CallContext, - CancellationToken.None); - - if (validationResult.IsValid) - { - ValidatedToken validatedToken = validationResult.UnwrapResult(); - if (validatedToken.ValidatedIssuer.HasValue) - IdentityComparer.AreValidatedIssuersEqual(validatedToken.ValidatedIssuer.Value, theoryData.ValidatedIssuer, context); - } - else - { - ValidationError validationError = validationResult.UnwrapError(); - IdentityComparer.AreValidationErrorsEqual(validationError, theoryData.IssuerValidationError, context); - theoryData.ExpectedException.ProcessException(validationError.GetException(), context); - } - } - catch (Exception ex) - { - theoryData.ExpectedException.ProcessException(ex, context); - } - - TestUtilities.AssertFailIfErrors(context); - } - - public static TheoryData Issuer_ExtensibilityTestCases - { - get - { - var theoryData = new TheoryData(); - CallContext callContext = new CallContext(); - string issuerGuid = Guid.NewGuid().ToString(); - - #region return CustomIssuerValidationError - // Test cases where delegate is overridden and return an CustomIssuerValidationError - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync, - [ - new StackFrame("CustomIssuerValidationDelegates", 88), - new StackFrame(false), - new StackFrame(false) - ]) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync)), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(SecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates", 88), - issuerGuid) - }); - - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorCustomExceptionDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync, - [ - new StackFrame("CustomIssuerValidationDelegates", 107), - new StackFrame(false), - new StackFrame(false) - ]) - { - ExpectedException = new ExpectedException( - typeof(CustomSecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync)), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(CustomSecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates", 107), - issuerGuid), - }); - - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: NotSupportedException : SystemException - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorUnknownExceptionDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync, - [ - new StackFrame("CustomIssuerValidationDelegates", 139), - new StackFrame(false), - new StackFrame(false) - ]) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync)), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(NotSupportedException), - new StackFrame("CustomIssuerValidationDelegates", 139), - issuerGuid), - }); - - // CustomIssuerValidationError : IssuerValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomIssuerValidationFailureType - theoryData.Add(new IssuerExtensibilityTheoryData( - "CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegate", - issuerGuid, - CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync, - [ - new StackFrame("CustomIssuerValidationDelegates", 123), - new StackFrame(false), - new StackFrame(false) - ]) - { - ExpectedException = new ExpectedException( - typeof(CustomSecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync)), - IssuerValidationError = new CustomIssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync), null), - CustomIssuerValidationError.CustomIssuerValidationFailureType, - typeof(CustomSecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates", 123), - issuerGuid, - null), - }); - #endregion - - #region return IssuerValidationError - // Test cases where delegate is overridden and return an IssuerValidationError - // IssuerValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorDelegate", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync, - [ - new StackFrame("CustomIssuerValidationDelegates", 169), - new StackFrame(false), - new StackFrame(false) - ]) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenInvalidIssuerException), - nameof(CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync)), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(SecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates", 169), - issuerGuid) - }); - - // IssuerValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorCustomIssuerExceptionTypeDelegate", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync, - [ - new StackFrame("CustomIssuerValidationDelegates", 196), - new StackFrame(false), - new StackFrame(false) - ]) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenException), - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync)), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(CustomSecurityTokenInvalidIssuerException), - new StackFrame("CustomIssuerValidationDelegates", 196), - issuerGuid) - }); - - // IssuerValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorCustomExceptionTypeDelegate", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync, - [ - new StackFrame("CustomIssuerValidationDelegates", 210), - new StackFrame(false), - new StackFrame(false) - ]) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenException), - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync)), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync), null), - ValidationFailureType.IssuerValidationFailed, - typeof(CustomSecurityTokenException), - new StackFrame("CustomIssuerValidationDelegates", 210), - issuerGuid) - }); - - // IssuerValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerException, inner: CustomSecurityTokenInvalidIssuerException - theoryData.Add(new IssuerExtensibilityTheoryData( - "IssuerValidatorThrows", - issuerGuid, - CustomIssuerValidationDelegates.IssuerValidatorThrows, - [ - new StackFrame("JsonWebTokenHandler.ValidateToken.Internal.cs", 300), - new StackFrame(false) - ]) - { - ExpectedException = new ExpectedException( - typeof(SecurityTokenInvalidIssuerException), - string.Format(Tokens.LogMessages.IDX10269), - typeof(CustomSecurityTokenInvalidIssuerException)), - IssuerValidationError = new IssuerValidationError( - new MessageDetail( - string.Format(Tokens.LogMessages.IDX10269), null), - ValidationFailureType.IssuerValidatorThrew, - typeof(SecurityTokenInvalidIssuerException), - new StackFrame("JsonWebTokenHandler.ValidateToken.Internal.cs", 300), - issuerGuid, - new SecurityTokenInvalidIssuerException(nameof(CustomIssuerValidationDelegates.IssuerValidatorThrows)) - ) - }); - #endregion - - return theoryData; - } - } - - public class IssuerExtensibilityTheoryData : ValidateTokenAsyncBaseTheoryData - { - internal IssuerExtensibilityTheoryData(string testId, string issuer, IssuerValidationDelegateAsync issuerValidator, IList stackFrames) : base(testId) - { - JsonWebToken = JsonUtilities.CreateUnsignedJsonWebToken("iss", issuer); - ValidationParameters = new ValidationParameters - { - AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation, - AudienceValidator = SkipValidationDelegates.SkipAudienceValidation, - IssuerValidatorAsync = issuerValidator, - IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation, - LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation, - SignatureValidator = SkipValidationDelegates.SkipSignatureValidation, - TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation, - TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation - }; - - StackFrames = stackFrames; - } - - public JsonWebToken JsonWebToken { get; } - - public JsonWebTokenHandler JsonWebTokenHandler { get; } = new JsonWebTokenHandler(); - - public bool IsValid { get; set; } - - internal ValidatedIssuer ValidatedIssuer { get; set; } - - internal IssuerValidationError? IssuerValidationError { get; set; } - - internal IList StackFrames { 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.Protocols.SignedHttpRequest.Tests/PopKeyResolvingTests.cs b/test/Microsoft.IdentityModel.Protocols.SignedHttpRequest.Tests/PopKeyResolvingTests.cs index ef06cc18c5..70cf3eb5ce 100644 --- a/test/Microsoft.IdentityModel.Protocols.SignedHttpRequest.Tests/PopKeyResolvingTests.cs +++ b/test/Microsoft.IdentityModel.Protocols.SignedHttpRequest.Tests/PopKeyResolvingTests.cs @@ -813,7 +813,7 @@ public static TheoryData GetPopKeysFromJkuAsyncTheoryDa { RequireHttpsForJkuResourceRetrieval = false, }, - ExpectedException = new ExpectedException(typeof(SignedHttpRequestInvalidPopKeyException), "IDX23022", typeof(ArgumentException)), + ExpectedException = new ExpectedException(typeof(SignedHttpRequestInvalidPopKeyException), "IDX23022", null, true), }, new ResolvePopKeyTheoryData("Valid0KeysReturned") { diff --git a/test/Microsoft.IdentityModel.TestUtils/SamlClaimsIdentityComparisonTestBase.cs b/test/Microsoft.IdentityModel.TestUtils/SamlClaimsIdentityComparisonTestBase.cs new file mode 100644 index 0000000000..00f8656c52 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/SamlClaimsIdentityComparisonTestBase.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Tokens.Saml; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils +{ + public partial class SamlClaimsIdentityComparisonTestBase + { + public static async Task ValidateTokenAsync_ClaimsIdentity_Comparison( + object testInstance, string methodName, string tokenHandlerType) + { + var context = new CompareContext($"{testInstance}.{methodName}"); + + List issuers = [Default.Issuer]; + List audiences = [Default.Audience]; + var signingKey = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key; + + ValidationParameters validationParameters = CreateValidationParameters( + issuers, audiences, signingKey); + TokenValidationParameters tokenValidationParameters = CreateTokenValidationParameters( + issuers, audiences, signingKey); + + ITestingTokenHandler tokenHandler = ExtensibilityTheoryData + .CreateSecurityTokenHandlerForType(tokenHandlerType); + + DateTime utcNow = DateTime.UtcNow; + string token = tokenHandler.CreateStringToken(new() + { + Subject = Default.SamlClaimsIdentity, + Issuer = Default.Issuer, + Audience = Default.Audience, + SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2, + IssuedAt = utcNow.AddHours(-1), + Expires = utcNow.AddHours(1), + NotBefore = utcNow.AddHours(-1), + }); + + ValidationResult validationResult = await tokenHandler.ValidateTokenAsync( + token, + validationParameters, + new CallContext(), + CancellationToken.None); + + TokenValidationResult tokenValidationResult = await tokenHandler.ValidateTokenAsync( + token, + tokenValidationParameters); + + IdentityComparer.AreBoolsEqual( + validationResult.IsValid, + tokenValidationResult.IsValid, context); + + IdentityComparer.AreClaimsIdentitiesEqual( + validationResult.UnwrapResult().ClaimsIdentity, + tokenValidationResult.ClaimsIdentity, context); + + TestUtilities.AssertFailIfErrors(context); + } + + static ValidationParameters CreateValidationParameters( + List issuers, + List audiences, + SecurityKey signingKey) + { + var validationParameters = new ValidationParameters(); + validationParameters.IssuerSigningKeys.Add(signingKey); + audiences.ForEach(validationParameters.ValidAudiences.Add); + issuers.ForEach(validationParameters.ValidIssuers.Add); + return validationParameters; + } + + static TokenValidationParameters CreateTokenValidationParameters( + List issuers, + List audiences, + SecurityKey signingKey) + { + var tokenValidationParameters = new TokenValidationParameters(); + tokenValidationParameters.ValidAudiences = audiences; + tokenValidationParameters.ValidIssuers = issuers; + tokenValidationParameters.IssuerSigningKey = signingKey; + return tokenValidationParameters; + } + } +} diff --git a/test/Microsoft.IdentityModel.TestUtils/SkipValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/SkipValidationDelegates.cs index 5203f6ea64..90b49be8aa 100644 --- a/test/Microsoft.IdentityModel.TestUtils/SkipValidationDelegates.cs +++ b/test/Microsoft.IdentityModel.TestUtils/SkipValidationDelegates.cs @@ -48,7 +48,7 @@ public static class SkipValidationDelegates SecurityToken securityToken, ValidationParameters validationParameters, BaseConfiguration? configuration, - CallContext? callContext) + CallContext callContext) { return new ValidatedSigningKeyLifetime( null, // ValidFrom @@ -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/CustomAudienceValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomAudienceValidationDelegates.cs new file mode 100644 index 0000000000..8b5010a8be --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomAudienceValidationDelegates.cs @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using Microsoft.IdentityModel.Tokens; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils +{ + internal class CustomAudienceValidationDelegates + { + internal static ValidationResult CustomAudienceValidatorDelegate( + IList tokenAudiences, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + // Returns a CustomAudienceValidationError : AudienceValidationError + return new CustomAudienceValidationError( + new MessageDetail(nameof(CustomAudienceValidatorDelegate), null), + ValidationFailureType.AudienceValidationFailed, + typeof(SecurityTokenInvalidAudienceException), + ValidationError.GetCurrentStackFrame(), + tokenAudiences, + null); + } + + internal static ValidationResult CustomAudienceValidatorCustomExceptionDelegate( + IList tokenAudiences, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomAudienceValidationError( + new MessageDetail(nameof(CustomAudienceValidatorCustomExceptionDelegate), null), + ValidationFailureType.AudienceValidationFailed, + typeof(CustomSecurityTokenInvalidAudienceException), + ValidationError.GetCurrentStackFrame(), + tokenAudiences, + null); + } + + internal static ValidationResult CustomAudienceValidatorCustomExceptionCustomFailureTypeDelegate( + IList tokenAudiences, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomAudienceValidationError( + new MessageDetail(nameof(CustomAudienceValidatorCustomExceptionCustomFailureTypeDelegate), null), + CustomAudienceValidationError.CustomAudienceValidationFailureType, + typeof(CustomSecurityTokenInvalidAudienceException), + ValidationError.GetCurrentStackFrame(), + tokenAudiences, + null); + } + + internal static ValidationResult CustomAudienceValidatorUnknownExceptionDelegate( + IList tokenAudiences, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomAudienceValidationError( + new MessageDetail(nameof(CustomAudienceValidatorUnknownExceptionDelegate), null), + ValidationFailureType.AudienceValidationFailed, + typeof(NotSupportedException), + ValidationError.GetCurrentStackFrame(), + tokenAudiences, + null); + } + + internal static ValidationResult CustomAudienceValidatorWithoutGetExceptionOverrideDelegate( + IList tokenAudiences, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomAudienceWithoutGetExceptionValidationOverrideError( + new MessageDetail(nameof(CustomAudienceValidatorWithoutGetExceptionOverrideDelegate), null), + typeof(CustomSecurityTokenInvalidAudienceException), + ValidationError.GetCurrentStackFrame(), + tokenAudiences, + null); + } + + internal static ValidationResult AudienceValidatorDelegate( + IList tokenAudiences, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new AudienceValidationError( + new MessageDetail(nameof(AudienceValidatorDelegate), null), + ValidationFailureType.AudienceValidationFailed, + typeof(SecurityTokenInvalidAudienceException), + ValidationError.GetCurrentStackFrame(), + tokenAudiences, + null); + } + + internal static ValidationResult AudienceValidatorThrows( + IList tokenAudiences, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + throw new CustomSecurityTokenInvalidAudienceException(nameof(AudienceValidatorThrows), null); + } + + internal static ValidationResult AudienceValidatorCustomAudienceExceptionTypeDelegate( + IList tokenAudiences, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new AudienceValidationError( + new MessageDetail(nameof(AudienceValidatorCustomAudienceExceptionTypeDelegate), null), + ValidationFailureType.AudienceValidationFailed, + typeof(CustomSecurityTokenInvalidAudienceException), + ValidationError.GetCurrentStackFrame(), + tokenAudiences, + null); + } + + internal static ValidationResult AudienceValidatorCustomExceptionTypeDelegate( + IList tokenAudiences, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new AudienceValidationError( + new MessageDetail(nameof(AudienceValidatorCustomExceptionTypeDelegate), null), + ValidationFailureType.AudienceValidationFailed, + typeof(CustomSecurityTokenException), + ValidationError.GetCurrentStackFrame(), + tokenAudiences, + null); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerSigningKeyValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerSigningKeyValidationDelegates.cs new file mode 100644 index 0000000000..2c94cdaf92 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerSigningKeyValidationDelegates.cs @@ -0,0 +1,150 @@ +// 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 CustomIssuerSigningKeyValidationDelegates + { + internal static ValidationResult CustomIssuerSigningKeyValidatorDelegate( + SecurityKey signingKey, + SecurityToken securityToken, + ValidationParameters validationParameters, + BaseConfiguration? configuration, + CallContext callContext) + { + // Returns a CustomIssuerSigningKeyValidationError : IssuerSigningKeyValidationError + return new CustomIssuerSigningKeyValidationError( + new MessageDetail(nameof(CustomIssuerSigningKeyValidatorDelegate), null), + ValidationFailureType.SigningKeyValidationFailed, + typeof(SecurityTokenInvalidSigningKeyException), + ValidationError.GetCurrentStackFrame(), + signingKey, + null); + } + + internal static ValidationResult CustomIssuerSigningKeyValidatorCustomExceptionDelegate( + SecurityKey signingKey, + SecurityToken securityToken, + ValidationParameters validationParameters, + BaseConfiguration? configuration, + CallContext callContext) + { + return new CustomIssuerSigningKeyValidationError( + new MessageDetail(nameof(CustomIssuerSigningKeyValidatorCustomExceptionDelegate), null), + ValidationFailureType.SigningKeyValidationFailed, + typeof(CustomSecurityTokenInvalidSigningKeyException), + ValidationError.GetCurrentStackFrame(), + signingKey, + null); + } + + internal static ValidationResult CustomIssuerSigningKeyValidatorCustomExceptionCustomFailureTypeDelegate( + SecurityKey signingKey, + SecurityToken securityToken, + ValidationParameters validationParameters, + BaseConfiguration? configuration, + CallContext callContext) + { + return new CustomIssuerSigningKeyValidationError( + new MessageDetail(nameof(CustomIssuerSigningKeyValidatorCustomExceptionCustomFailureTypeDelegate), null), + CustomIssuerSigningKeyValidationError.CustomIssuerSigningKeyValidationFailureType, + typeof(CustomSecurityTokenInvalidSigningKeyException), + ValidationError.GetCurrentStackFrame(), + signingKey); + } + + internal static ValidationResult CustomIssuerSigningKeyValidatorUnknownExceptionDelegate( + SecurityKey signingKey, + SecurityToken securityToken, + ValidationParameters validationParameters, + BaseConfiguration? configuration, + CallContext callContext) + { + return new CustomIssuerSigningKeyValidationError( + new MessageDetail(nameof(CustomIssuerSigningKeyValidatorUnknownExceptionDelegate), null), + ValidationFailureType.SigningKeyValidationFailed, + typeof(NotSupportedException), + ValidationError.GetCurrentStackFrame(), + signingKey, + null); + } + + internal static ValidationResult CustomIssuerSigningKeyValidatorWithoutGetExceptionOverrideDelegate( + SecurityKey signingKey, + SecurityToken securityToken, + ValidationParameters validationParameters, + BaseConfiguration? configuration, + CallContext callContext) + { + return new CustomIssuerSigningKeyWithoutGetExceptionValidationOverrideError( + new MessageDetail(nameof(CustomIssuerSigningKeyValidatorWithoutGetExceptionOverrideDelegate), null), + typeof(CustomSecurityTokenInvalidSigningKeyException), + ValidationError.GetCurrentStackFrame(), + signingKey, + null); + } + + internal static ValidationResult IssuerSigningKeyValidatorDelegate( + SecurityKey signingKey, + SecurityToken securityToken, + ValidationParameters validationParameters, + BaseConfiguration? configuration, + CallContext callContext) + { + return new IssuerSigningKeyValidationError( + new MessageDetail(nameof(IssuerSigningKeyValidatorDelegate), null), + ValidationFailureType.SigningKeyValidationFailed, + typeof(SecurityTokenInvalidSigningKeyException), + ValidationError.GetCurrentStackFrame(), + signingKey, + null); + } + + internal static ValidationResult IssuerSigningKeyValidatorThrows( + SecurityKey signingKey, + SecurityToken securityToken, + ValidationParameters validationParameters, + BaseConfiguration? configuration, + CallContext callContext) + { + throw new CustomSecurityTokenInvalidSigningKeyException(nameof(IssuerSigningKeyValidatorThrows), null); + } + + internal static ValidationResult IssuerSigningKeyValidatorCustomIssuerSigningKeyExceptionTypeDelegate( + SecurityKey signingKey, + SecurityToken securityToken, + ValidationParameters validationParameters, + BaseConfiguration? configuration, + CallContext callContext) + { + return new IssuerSigningKeyValidationError( + new MessageDetail(nameof(IssuerSigningKeyValidatorCustomIssuerSigningKeyExceptionTypeDelegate), null), + ValidationFailureType.SigningKeyValidationFailed, + typeof(CustomSecurityTokenInvalidSigningKeyException), + ValidationError.GetCurrentStackFrame(), + signingKey, + null); + } + + internal static ValidationResult IssuerSigningKeyValidatorCustomExceptionTypeDelegate( + SecurityKey signingKey, + SecurityToken securityToken, + ValidationParameters validationParameters, + BaseConfiguration? configuration, + CallContext callContext) + { + return new IssuerSigningKeyValidationError( + new MessageDetail(nameof(IssuerSigningKeyValidatorCustomExceptionTypeDelegate), null), + ValidationFailureType.SigningKeyValidationFailed, + typeof(CustomSecurityTokenException), + ValidationError.GetCurrentStackFrame(), + signingKey, + null); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomLifetimeValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomLifetimeValidationDelegates.cs new file mode 100644 index 0000000000..141eb4a584 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomLifetimeValidationDelegates.cs @@ -0,0 +1,159 @@ +// 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 CustomLifetimeValidationDelegates + { + internal static ValidationResult CustomLifetimeValidatorDelegate( + DateTime? notBefore, + DateTime? expires, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + // Returns a CustomLifetimeValidationError : LifetimeValidationError + return new CustomLifetimeValidationError( + new MessageDetail(nameof(CustomLifetimeValidatorDelegate), null), + ValidationFailureType.LifetimeValidationFailed, + typeof(SecurityTokenInvalidLifetimeException), + ValidationError.GetCurrentStackFrame(), + notBefore, + expires, + null); + } + + internal static ValidationResult CustomLifetimeValidatorCustomExceptionDelegate( + DateTime? notBefore, + DateTime? expires, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomLifetimeValidationError( + new MessageDetail(nameof(CustomLifetimeValidatorCustomExceptionDelegate), null), + ValidationFailureType.LifetimeValidationFailed, + typeof(CustomSecurityTokenInvalidLifetimeException), + ValidationError.GetCurrentStackFrame(), + notBefore, + expires, + null); + } + + internal static ValidationResult CustomLifetimeValidatorCustomExceptionCustomFailureTypeDelegate( + DateTime? notBefore, + DateTime? expires, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomLifetimeValidationError( + new MessageDetail(nameof(CustomLifetimeValidatorCustomExceptionCustomFailureTypeDelegate), null), + CustomLifetimeValidationError.CustomLifetimeValidationFailureType, + typeof(CustomSecurityTokenInvalidLifetimeException), + ValidationError.GetCurrentStackFrame(), + notBefore, + expires); + } + + internal static ValidationResult CustomLifetimeValidatorUnknownExceptionDelegate( + DateTime? notBefore, + DateTime? expires, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomLifetimeValidationError( + new MessageDetail(nameof(CustomLifetimeValidatorUnknownExceptionDelegate), null), + ValidationFailureType.LifetimeValidationFailed, + typeof(NotSupportedException), + ValidationError.GetCurrentStackFrame(), + notBefore, + expires, + null); + } + + internal static ValidationResult CustomLifetimeValidatorWithoutGetExceptionOverrideDelegate( + DateTime? notBefore, + DateTime? expires, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomLifetimeWithoutGetExceptionValidationOverrideError( + new MessageDetail(nameof(CustomLifetimeValidatorWithoutGetExceptionOverrideDelegate), null), + ValidationFailureType.LifetimeValidationFailed, + typeof(CustomSecurityTokenInvalidLifetimeException), + ValidationError.GetCurrentStackFrame(), + notBefore, + expires, + null); + } + + internal static ValidationResult LifetimeValidatorDelegate( + DateTime? notBefore, + DateTime? expires, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new LifetimeValidationError( + new MessageDetail(nameof(LifetimeValidatorDelegate), null), + ValidationFailureType.LifetimeValidationFailed, + typeof(SecurityTokenInvalidLifetimeException), + ValidationError.GetCurrentStackFrame(), + notBefore, + expires, + null); + } + + internal static ValidationResult LifetimeValidatorThrows( + DateTime? notBefore, + DateTime? expires, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + throw new CustomSecurityTokenInvalidLifetimeException(nameof(LifetimeValidatorThrows), null); + } + + internal static ValidationResult LifetimeValidatorCustomLifetimeExceptionTypeDelegate( + DateTime? notBefore, + DateTime? expires, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new LifetimeValidationError( + new MessageDetail(nameof(LifetimeValidatorCustomLifetimeExceptionTypeDelegate), null), + ValidationFailureType.LifetimeValidationFailed, + typeof(CustomSecurityTokenInvalidLifetimeException), + ValidationError.GetCurrentStackFrame(), + notBefore, + expires, + null); + } + + internal static ValidationResult LifetimeValidatorCustomExceptionTypeDelegate( + DateTime? notBefore, + DateTime? expires, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new LifetimeValidationError( + new MessageDetail(nameof(LifetimeValidatorCustomExceptionTypeDelegate), null), + ValidationFailureType.LifetimeValidationFailed, + typeof(CustomSecurityTokenException), + ValidationError.GetCurrentStackFrame(), + notBefore, + expires, + null); + } + } +} +#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/CustomTokenReplayValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomTokenReplayValidationDelegates.cs new file mode 100644 index 0000000000..6e10f6cba8 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomTokenReplayValidationDelegates.cs @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading.Tasks; +using Microsoft.IdentityModel.Tokens; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils +{ + internal class CustomTokenReplayValidationDelegates + { + internal static ValidationResult CustomTokenReplayValidationDelegate( + DateTime? expirationTime, + string securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + // Returns a CustomTokenReplayValidationError : IssuerValidationError + return new CustomTokenReplayValidationError( + new MessageDetail(nameof(CustomTokenReplayValidationDelegate), null), + ValidationFailureType.TokenReplayValidationFailed, + typeof(SecurityTokenReplayDetectedException), + ValidationError.GetCurrentStackFrame(), + expirationTime); + } + + internal static ValidationResult CustomTokenReplayValidatorCustomExceptionDelegate( + DateTime? expirationTime, + string securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomTokenReplayValidationError( + new MessageDetail(nameof(CustomTokenReplayValidatorCustomExceptionDelegate), null), + ValidationFailureType.TokenReplayValidationFailed, + typeof(CustomSecurityTokenReplayDetectedException), + ValidationError.GetCurrentStackFrame(), + expirationTime); + } + + internal static ValidationResult CustomTokenReplayValidatorCustomExceptionCustomFailureTypeDelegate( + DateTime? expirationTime, + string securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomTokenReplayValidationError( + new MessageDetail(nameof(CustomTokenReplayValidatorCustomExceptionCustomFailureTypeDelegate), null), + CustomTokenReplayValidationError.CustomTokenReplayValidationFailureType, + typeof(CustomSecurityTokenReplayDetectedException), + ValidationError.GetCurrentStackFrame(), + expirationTime, + null); + } + + internal static ValidationResult CustomTokenReplayValidatorUnknownExceptionDelegate( + DateTime? expirationTime, + string securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomTokenReplayValidationError( + new MessageDetail(nameof(CustomTokenReplayValidatorUnknownExceptionDelegate), null), + ValidationFailureType.TokenReplayValidationFailed, + typeof(NotSupportedException), + ValidationError.GetCurrentStackFrame(), + expirationTime); + } + + internal static ValidationResult CustomTokenReplayValidatorWithoutGetExceptionOverrideDelegate( + DateTime? expirationTime, + string securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomTokenReplayWithoutGetExceptionValidationOverrideError( + new MessageDetail(nameof(CustomTokenReplayValidatorWithoutGetExceptionOverrideDelegate), null), + typeof(CustomSecurityTokenReplayDetectedException), + ValidationError.GetCurrentStackFrame(), + expirationTime); + } + + internal static ValidationResult TokenReplayValidationDelegate( + DateTime? expirationTime, + string securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new TokenReplayValidationError( + new MessageDetail(nameof(TokenReplayValidationDelegate), null), + ValidationFailureType.TokenReplayValidationFailed, + typeof(SecurityTokenReplayDetectedException), + ValidationError.GetCurrentStackFrame(), + expirationTime); + } + + internal static ValidationResult TokenReplayValidatorThrows( + DateTime? expirationTime, + string securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + throw new CustomSecurityTokenReplayDetectedException(nameof(TokenReplayValidatorThrows), null); + } + + internal static ValidationResult TokenReplayValidatorCustomTokenReplayDetectedExceptionTypeDelegate( + DateTime? expirationTime, + string securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new TokenReplayValidationError( + new MessageDetail(nameof(TokenReplayValidatorCustomTokenReplayDetectedExceptionTypeDelegate), null), + ValidationFailureType.TokenReplayValidationFailed, + typeof(CustomSecurityTokenReplayDetectedException), + ValidationError.GetCurrentStackFrame(), + expirationTime); + } + internal static ValidationResult TokenReplayValidatorCustomExceptionTypeDelegate( + DateTime? expirationTime, + string securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new TokenReplayValidationError( + new MessageDetail(nameof(TokenReplayValidatorCustomExceptionTypeDelegate), null), + ValidationFailureType.TokenReplayValidationFailed, + typeof(CustomSecurityTokenException), + ValidationError.GetCurrentStackFrame(), + expirationTime); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomTokenTypeValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomTokenTypeValidationDelegates.cs new file mode 100644 index 0000000000..ebc10c95e4 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomTokenTypeValidationDelegates.cs @@ -0,0 +1,141 @@ +// 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 CustomTokenTypeValidationDelegates + { + internal static ValidationResult CustomTokenTypeValidatorDelegate( + string? type, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + // Returns a CustomTokenTypeValidationError : TokenTypeValidationError + return new CustomTokenTypeValidationError( + new MessageDetail(nameof(CustomTokenTypeValidatorDelegate), null), + ValidationFailureType.TokenTypeValidationFailed, + typeof(SecurityTokenInvalidTypeException), + ValidationError.GetCurrentStackFrame(), + type, + null); + } + + internal static ValidationResult CustomTokenTypeValidatorCustomExceptionDelegate( + string? type, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomTokenTypeValidationError( + new MessageDetail(nameof(CustomTokenTypeValidatorCustomExceptionDelegate), null), + ValidationFailureType.TokenTypeValidationFailed, + typeof(CustomSecurityTokenInvalidTypeException), + ValidationError.GetCurrentStackFrame(), + type, + null); + } + + internal static ValidationResult CustomTokenTypeValidatorCustomExceptionCustomFailureTypeDelegate( + string? type, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomTokenTypeValidationError( + new MessageDetail(nameof(CustomTokenTypeValidatorCustomExceptionCustomFailureTypeDelegate), null), + CustomTokenTypeValidationError.CustomTokenTypeValidationFailureType, + typeof(CustomSecurityTokenInvalidTypeException), + ValidationError.GetCurrentStackFrame(), + type); + } + + internal static ValidationResult CustomTokenTypeValidatorUnknownExceptionDelegate( + string? type, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomTokenTypeValidationError( + new MessageDetail(nameof(CustomTokenTypeValidatorUnknownExceptionDelegate), null), + ValidationFailureType.TokenTypeValidationFailed, + typeof(NotSupportedException), + ValidationError.GetCurrentStackFrame(), + type, + null); + } + + internal static ValidationResult CustomTokenTypeValidatorWithoutGetExceptionOverrideDelegate( + string? type, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new CustomTokenTypeWithoutGetExceptionValidationOverrideError( + new MessageDetail(nameof(CustomTokenTypeValidatorWithoutGetExceptionOverrideDelegate), null), + typeof(CustomSecurityTokenInvalidTypeException), + ValidationError.GetCurrentStackFrame(), + type, + null); + } + + internal static ValidationResult TokenTypeValidatorDelegate( + string? type, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new TokenTypeValidationError( + new MessageDetail(nameof(TokenTypeValidatorDelegate), null), + ValidationFailureType.TokenTypeValidationFailed, + typeof(SecurityTokenInvalidTypeException), + ValidationError.GetCurrentStackFrame(), + type, + null); + } + + internal static ValidationResult TokenTypeValidatorThrows( + string? type, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + throw new CustomSecurityTokenInvalidTypeException(nameof(TokenTypeValidatorThrows), null); + } + + internal static ValidationResult TokenTypeValidatorCustomTokenTypeExceptionTypeDelegate( + string? type, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new TokenTypeValidationError( + new MessageDetail(nameof(TokenTypeValidatorCustomTokenTypeExceptionTypeDelegate), null), + ValidationFailureType.TokenTypeValidationFailed, + typeof(CustomSecurityTokenInvalidTypeException), + ValidationError.GetCurrentStackFrame(), + type, + null); + } + + internal static ValidationResult TokenTypeValidatorCustomExceptionTypeDelegate( + string? type, + SecurityToken? securityToken, + ValidationParameters validationParameters, + CallContext callContext) + { + return new TokenTypeValidationError( + new MessageDetail(nameof(TokenTypeValidatorCustomExceptionTypeDelegate), null), + ValidationFailureType.TokenTypeValidationFailed, + typeof(CustomSecurityTokenException), + ValidationError.GetCurrentStackFrame(), + type, + null); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs index 382a57a634..7571d4f8aa 100644 --- a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs @@ -156,6 +156,237 @@ public CustomLifetimeWithoutGetExceptionValidationOverrideError( } #endregion + #region IssuerSigningKeyValidationErrors + internal class CustomIssuerSigningKeyValidationError : IssuerSigningKeyValidationError + { + /// + /// A custom validation failure type. + /// + public static readonly ValidationFailureType CustomIssuerSigningKeyValidationFailureType = new IssuerSigningKeyValidationFailure("CustomIssuerSigningKeyValidationFailureType"); + private class IssuerSigningKeyValidationFailure : ValidationFailureType { internal IssuerSigningKeyValidationFailure(string name) : base(name) { } } + + public CustomIssuerSigningKeyValidationError( + MessageDetail messageDetail, + ValidationFailureType validationFailureType, + Type exceptionType, + StackFrame stackFrame, + SecurityKey? securityKey, + Exception? innerException = null) + : base(messageDetail, validationFailureType, exceptionType, stackFrame, securityKey, innerException) + { + } + + internal override Exception GetException() + { + if (ExceptionType == typeof(CustomSecurityTokenInvalidSigningKeyException)) + { + var exception = new CustomSecurityTokenInvalidSigningKeyException(MessageDetail.Message, InnerException) { SigningKey = InvalidSigningKey }; + exception.SetValidationError(this); + return exception; + } + return base.GetException(); + } + } + + internal class CustomIssuerSigningKeyWithoutGetExceptionValidationOverrideError : IssuerSigningKeyValidationError + { + public CustomIssuerSigningKeyWithoutGetExceptionValidationOverrideError( + MessageDetail messageDetail, + Type exceptionType, + StackFrame stackFrame, + SecurityKey? securityKey, + Exception? innerException = null) + : base(messageDetail, ValidationFailureType.SigningKeyValidationFailed, exceptionType, stackFrame, securityKey, innerException) + { + } + } + #endregion // IssuerSigningKeyValidationErrors + + #region TokenTypeValidationErrors + internal class CustomTokenTypeValidationError : TokenTypeValidationError + { + /// + /// A custom validation failure type. + /// + public static readonly ValidationFailureType CustomTokenTypeValidationFailureType = new TokenTypeValidationFailure("CustomTokenTypeValidationFailureType"); + private class TokenTypeValidationFailure : ValidationFailureType { internal TokenTypeValidationFailure(string name) : base(name) { } } + + public CustomTokenTypeValidationError( + MessageDetail messageDetail, + ValidationFailureType validationFailureType, + Type exceptionType, + StackFrame stackFrame, + string? invalidTokenType, + Exception? innerException = null) + : base(messageDetail, validationFailureType, exceptionType, stackFrame, invalidTokenType, innerException) + { + } + internal override Exception GetException() + { + if (ExceptionType == typeof(CustomSecurityTokenInvalidTypeException)) + { + var exception = new CustomSecurityTokenInvalidTypeException(MessageDetail.Message, InnerException) { InvalidType = InvalidTokenType }; + exception.SetValidationError(this); + return exception; + } + return base.GetException(); + } + } + + internal class CustomTokenTypeWithoutGetExceptionValidationOverrideError : TokenTypeValidationError + { + public CustomTokenTypeWithoutGetExceptionValidationOverrideError( + MessageDetail messageDetail, + Type exceptionType, + StackFrame stackFrame, + string? invalidTokenType, + Exception? innerException = null) + : base(messageDetail, ValidationFailureType.TokenTypeValidationFailed, exceptionType, stackFrame, invalidTokenType, innerException) + { + } + } + #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 + + #region TokenReplayValidationErrors + internal class CustomTokenReplayValidationError : TokenReplayValidationError + { + /// + /// A custom validation failure type. + /// + public static readonly ValidationFailureType CustomTokenReplayValidationFailureType = new TokenReplayValidationFailure("CustomTokenReplayValidationFailureType"); + private class TokenReplayValidationFailure : ValidationFailureType { internal TokenReplayValidationFailure(string name) : base(name) { } } + + public CustomTokenReplayValidationError( + MessageDetail messageDetail, + ValidationFailureType validationFailureType, + Type exceptionType, + StackFrame stackFrame, + DateTime? expirationTime, + Exception? innerException = null) + : base(messageDetail, validationFailureType, exceptionType, stackFrame, expirationTime, innerException) + { + } + + internal override Exception GetException() + { + if (ExceptionType == typeof(CustomSecurityTokenReplayDetectedException)) + { + var exception = new CustomSecurityTokenReplayDetectedException(MessageDetail.Message, InnerException); + exception.SetValidationError(this); + + return exception; + } + + return base.GetException(); + } + } + + internal class CustomTokenReplayWithoutGetExceptionValidationOverrideError : TokenReplayValidationError + { + public CustomTokenReplayWithoutGetExceptionValidationOverrideError( + MessageDetail messageDetail, + Type exceptionType, + StackFrame stackFrame, + DateTime? expirationTime, + Exception? innerException = null) + : base(messageDetail, ValidationFailureType.TokenReplayValidationFailed, exceptionType, stackFrame, expirationTime, innerException) + { + } + } + #endregion + // Other custom validation errors to be added here for signature validation, issuer signing key, etc. } #nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/AlgorithmExtensibilityTestCases.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/AlgorithmExtensibilityTestCases.cs new file mode 100644 index 0000000000..d28fefce62 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/AlgorithmExtensibilityTestCases.cs @@ -0,0 +1,228 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using Xunit; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Logging; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public partial class ExtensibilityTesting + { + public static TheoryData GenerateAlgorithmExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames, + string stackFrameFileName) + { + TheoryData theoryData = new(); + CallContext callContext = new CallContext(); + + #region return CustomAlgorithmValidationError + // Test cases where delegate is overridden and return a CustomAlgorithmValidationError + // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: SecurityTokenInvalidAlgorithmException + theoryData.Add(new AlgorithmExtensibilityTheoryData( + "CustomAlgorithmValidatorDelegate", + tokenHandlerType, + CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorDelegate, + extraStackFrames: extraStackFrames) + { + 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", 0), + "algorithm") + }); + + // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: CustomSecurityTokenInvalidAlgorithmException : SecurityTokenInvalidAlgorithmException + theoryData.Add(new AlgorithmExtensibilityTheoryData( + "CustomAlgorithmValidatorCustomExceptionDelegate", + tokenHandlerType, + CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionDelegate, + extraStackFrames: extraStackFrames) + { + 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", 0), + "algorithm"), + }); + + // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: NotSupportedException : SystemException + theoryData.Add(new AlgorithmExtensibilityTheoryData( + "CustomAlgorithmValidatorUnknownExceptionDelegate", + tokenHandlerType, + CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorUnknownExceptionDelegate, + extraStackFrames: extraStackFrames) + { + // 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", 0), + "algorithm"), + }); + + // CustomAlgorithmValidationError : AlgorithmValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomAudienceValidationFailureType + theoryData.Add(new AlgorithmExtensibilityTheoryData( + "CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate", + tokenHandlerType, + CustomAlgorithmValidationDelegates.CustomAlgorithmValidatorCustomExceptionCustomFailureTypeDelegate, + extraStackFrames: extraStackFrames) + { + 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", 0), + "algorithm"), + }); + #endregion + + #region return AlgorithmValidationError + // Test cases where delegate is overridden and return an AlgorithmValidationError + // AlgorithmValidationError : ValidationError, ExceptionType: SecurityTokenInvalidAlgorithmException + theoryData.Add(new AlgorithmExtensibilityTheoryData( + "AlgorithmValidatorDelegate", + tokenHandlerType, + CustomAlgorithmValidationDelegates.AlgorithmValidatorDelegate, + extraStackFrames: extraStackFrames) + { + 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", 0), + "algorithm") + }); + + // AlgorithmValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidAlgorithmException : SecurityTokenInvalidAlgorithmException + theoryData.Add(new AlgorithmExtensibilityTheoryData( + "AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate", + tokenHandlerType, + CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomAlgorithmExceptionTypeDelegate, + extraStackFrames: extraStackFrames) + { + // 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", 0), + "algorithm") + }); + + // AlgorithmValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException + theoryData.Add(new AlgorithmExtensibilityTheoryData( + "AlgorithmValidatorCustomExceptionTypeDelegate", + tokenHandlerType, + CustomAlgorithmValidationDelegates.AlgorithmValidatorCustomExceptionTypeDelegate, + extraStackFrames: extraStackFrames) + { + // 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", 0), + "algorithm") + }); + + // SignatureValidationError : ValidationError, ExceptionType: SecurityTokenInvalidSignatureException, inner: CustomSecurityTokenInvalidAlgorithmException + theoryData.Add(new AlgorithmExtensibilityTheoryData( + "AlgorithmValidatorThrows", + tokenHandlerType, + CustomAlgorithmValidationDelegates.AlgorithmValidatorThrows, + extraStackFrames: extraStackFrames + 1) + { + 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(stackFrameFileName, 0), + null, // no inner validation error + new CustomSecurityTokenInvalidAlgorithmException(nameof(CustomAlgorithmValidationDelegates.AlgorithmValidatorThrows), null) + ) + }); + #endregion + + return theoryData; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/AlgorithmExtensibilityTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/AlgorithmExtensibilityTheoryData.cs new file mode 100644 index 0000000000..90a6888d32 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/AlgorithmExtensibilityTheoryData.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.IdentityModel.Tokens; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public class AlgorithmExtensibilityTheoryData : ExtensibilityTheoryData + { + internal AlgorithmExtensibilityTheoryData( + string testId, + string tokenHandlerType, + AlgorithmValidationDelegate algorithmValidationDelegate, + int extraStackFrames) : base(testId, tokenHandlerType, extraStackFrames) + { + SecurityTokenDescriptor = new() + { + Issuer = Default.Issuer, + SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2, + }; + + ValidationParameters.AlgorithmValidator = algorithmValidationDelegate; + ValidationParameters.SignatureValidator = null; + ValidationParameters.IssuerSigningKeys.Add(KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/AudienceExtensibilityTestCases.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/AudienceExtensibilityTestCases.cs new file mode 100644 index 0000000000..d6d56aa50f --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/AudienceExtensibilityTestCases.cs @@ -0,0 +1,217 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using Xunit; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Logging; +using System.Collections.Generic; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public partial class ExtensibilityTesting + { + public static TheoryData GenerateAudienceExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames, + string stackFrameFileName) + { + var theoryData = new TheoryData(); + CallContext callContext = new CallContext(); + string issuerGuid = Guid.NewGuid().ToString(); + var audience = Default.Audience; + List tokenAudiences = [audience]; + + #region return CustomAudienceValidationError + // Test cases where delegate is overridden and return a CustomAudienceValidationError + // CustomAudienceValidationError : AudienceValidationError, ExceptionType: SecurityTokenInvalidAudienceException + theoryData.Add(new AudienceExtensibilityTheoryData( + "CustomAudienceValidatorDelegate", + tokenHandlerType, + audience, + CustomAudienceValidationDelegates.CustomAudienceValidatorDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidAudienceException), + nameof(CustomAudienceValidationDelegates.CustomAudienceValidatorDelegate)), + ValidationError = new CustomAudienceValidationError( + new MessageDetail( + nameof(CustomAudienceValidationDelegates.CustomAudienceValidatorDelegate), null), + ValidationFailureType.AudienceValidationFailed, + typeof(SecurityTokenInvalidAudienceException), + new StackFrame("CustomAudienceValidationDelegates.cs", 0), + tokenAudiences, + null) + }); + + // CustomAudienceValidationError : AudienceValidationError, ExceptionType: CustomSecurityTokenInvalidAudienceException : SecurityTokenInvalidAudienceException + theoryData.Add(new AudienceExtensibilityTheoryData( + "CustomAudienceValidatorCustomExceptionDelegate", + tokenHandlerType, + audience, + CustomAudienceValidationDelegates.CustomAudienceValidatorCustomExceptionDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenInvalidAudienceException), + nameof(CustomAudienceValidationDelegates.CustomAudienceValidatorCustomExceptionDelegate)), + ValidationError = new CustomAudienceValidationError( + new MessageDetail( + nameof(CustomAudienceValidationDelegates.CustomAudienceValidatorCustomExceptionDelegate), null), + ValidationFailureType.AudienceValidationFailed, + typeof(CustomSecurityTokenInvalidAudienceException), + new StackFrame("CustomAudienceValidationDelegates.cs", 0), + tokenAudiences, + null), + }); + + // CustomAudienceValidationError : AudienceValidationError, ExceptionType: NotSupportedException : SystemException + theoryData.Add(new AudienceExtensibilityTheoryData( + "CustomAudienceValidatorUnknownExceptionDelegate", + tokenHandlerType, + audience, + CustomAudienceValidationDelegates.CustomAudienceValidatorUnknownExceptionDelegate, + extraStackFrames: extraStackFrames) + { + // CustomAudienceValidationError 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(CustomAudienceValidationDelegates.CustomAudienceValidatorUnknownExceptionDelegate))), + ValidationError = new CustomAudienceValidationError( + new MessageDetail( + nameof(CustomAudienceValidationDelegates.CustomAudienceValidatorUnknownExceptionDelegate), null), + ValidationFailureType.AudienceValidationFailed, + typeof(NotSupportedException), + new StackFrame("CustomAudienceValidationDelegates.cs", 0), + tokenAudiences, + null), + }); + + // CustomAudienceValidationError : AudienceValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomAudienceValidationFailureType + theoryData.Add(new AudienceExtensibilityTheoryData( + "CustomAudienceValidatorCustomExceptionCustomFailureTypeDelegate", + tokenHandlerType, + audience, + CustomAudienceValidationDelegates.CustomAudienceValidatorCustomExceptionCustomFailureTypeDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenInvalidAudienceException), + nameof(CustomAudienceValidationDelegates.CustomAudienceValidatorCustomExceptionCustomFailureTypeDelegate)), + ValidationError = new CustomAudienceValidationError( + new MessageDetail( + nameof(CustomAudienceValidationDelegates.CustomAudienceValidatorCustomExceptionCustomFailureTypeDelegate), null), + CustomAudienceValidationError.CustomAudienceValidationFailureType, + typeof(CustomSecurityTokenInvalidAudienceException), + new StackFrame("CustomAudienceValidationDelegates.cs", 0), + tokenAudiences, + null) // validAudiences + }); + #endregion + + #region return AudienceValidationError + // Test cases where delegate is overridden and return an AudienceValidationError + // AudienceValidationError : ValidationError, ExceptionType: SecurityTokenInvalidAudienceException + theoryData.Add(new AudienceExtensibilityTheoryData( + "AudienceValidatorDelegate", + tokenHandlerType, + audience, + CustomAudienceValidationDelegates.AudienceValidatorDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidAudienceException), + nameof(CustomAudienceValidationDelegates.AudienceValidatorDelegate)), + ValidationError = new AudienceValidationError( + new MessageDetail( + nameof(CustomAudienceValidationDelegates.AudienceValidatorDelegate), null), + ValidationFailureType.AudienceValidationFailed, + typeof(SecurityTokenInvalidAudienceException), + new StackFrame("CustomAudienceValidationDelegates.cs", 0), + tokenAudiences, + null) + }); + + // AudienceValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidAudienceException : SecurityTokenInvalidAudienceException + theoryData.Add(new AudienceExtensibilityTheoryData( + "AudienceValidatorCustomAudienceExceptionTypeDelegate", + tokenHandlerType, + audience, + CustomAudienceValidationDelegates.AudienceValidatorCustomAudienceExceptionTypeDelegate, + extraStackFrames: extraStackFrames) + { + // AudienceValidationError does not handle the exception type 'CustomSecurityTokenInvalidAudienceException' + ExpectedException = ExpectedException.SecurityTokenException( + LogHelper.FormatInvariant( + Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; + typeof(CustomSecurityTokenInvalidAudienceException), + nameof(CustomAudienceValidationDelegates.AudienceValidatorCustomAudienceExceptionTypeDelegate))), + ValidationError = new AudienceValidationError( + new MessageDetail( + nameof(CustomAudienceValidationDelegates.AudienceValidatorCustomAudienceExceptionTypeDelegate), null), + ValidationFailureType.AudienceValidationFailed, + typeof(CustomSecurityTokenInvalidAudienceException), + new StackFrame("CustomAudienceValidationDelegates.cs", 0), + tokenAudiences, + null) + }); + + // AudienceValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException + theoryData.Add(new AudienceExtensibilityTheoryData( + "AudienceValidatorCustomExceptionTypeDelegate", + tokenHandlerType, + audience, + CustomAudienceValidationDelegates.AudienceValidatorCustomExceptionTypeDelegate, + extraStackFrames: extraStackFrames) + { + // AudienceValidationError 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(CustomAudienceValidationDelegates.AudienceValidatorCustomExceptionTypeDelegate))), + ValidationError = new AudienceValidationError( + new MessageDetail( + nameof(CustomAudienceValidationDelegates.AudienceValidatorCustomExceptionTypeDelegate), null), + ValidationFailureType.AudienceValidationFailed, + typeof(CustomSecurityTokenException), + new StackFrame("CustomAudienceValidationDelegates.cs", 0), + tokenAudiences, + null) + }); + + // AudienceValidationError : ValidationError, ExceptionType: SecurityTokenInvalidAudienceException, inner: CustomSecurityTokenInvalidAudienceException + theoryData.Add(new AudienceExtensibilityTheoryData( + "AudienceValidatorThrows", + tokenHandlerType, + audience, + CustomAudienceValidationDelegates.AudienceValidatorThrows, + extraStackFrames: extraStackFrames - 1) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidAudienceException), + string.Format(Tokens.LogMessages.IDX10270), + typeof(CustomSecurityTokenInvalidAudienceException)), + ValidationError = new AudienceValidationError( + new MessageDetail( + string.Format(Tokens.LogMessages.IDX10270), null), + ValidationFailureType.AudienceValidatorThrew, + typeof(SecurityTokenInvalidAudienceException), + new StackFrame(stackFrameFileName, 0), + tokenAudiences, + null, + new SecurityTokenInvalidAudienceException(nameof(CustomAudienceValidationDelegates.AudienceValidatorThrows)) + ) + }); + #endregion + + return theoryData; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/AudienceExtensibilityTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/AudienceExtensibilityTheoryData.cs new file mode 100644 index 0000000000..7b1da2d0ad --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/AudienceExtensibilityTheoryData.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.IdentityModel.Tokens; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public class AudienceExtensibilityTheoryData : ExtensibilityTheoryData + { + internal AudienceExtensibilityTheoryData( + string testId, + string tokenHandlerType, + string audience, + AudienceValidationDelegate audienceValidationDelegate, + int extraStackFrames) : base(testId, tokenHandlerType, extraStackFrames) + { + SecurityTokenDescriptor = new() + { + Issuer = Default.Issuer, + Audience = audience, + }; + + ValidationParameters.AudienceValidator = audienceValidationDelegate; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ExtensibilityTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ExtensibilityTheoryData.cs new file mode 100644 index 0000000000..839e636701 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ExtensibilityTheoryData.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Security.Claims; +using System; +using Microsoft.IdentityModel.Tokens; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public class ExtensibilityTheoryData : TheoryDataBase + { + string _tokenHandlerType; + SecurityTokenDescriptor? _securityTokenDescriptor; + + internal ExtensibilityTheoryData( + string testId, + string tokenHandlerType, + int extraStackFrames) : base(testId) + { + ExtraStackFrames = extraStackFrames; + TokenHandler = CreateSecurityTokenHandlerForType(tokenHandlerType); + _tokenHandlerType = tokenHandlerType; + + ValidationParameters = CreateValidationParametersSkippingValidations(); + } + + internal static ITestingTokenHandler CreateSecurityTokenHandlerForType(string tokenHandlerType) + { + return tokenHandlerType switch + { + "JWT" => new JsonWebTokenHandlerWithResult(), + "SAML" => new SamlSecurityTokenHandlerWithResult(), + "SAML2" => new Saml2SecurityTokenHandlerWithResult(), + _ => throw new NotImplementedException(tokenHandlerType) + }; + } + + private SecurityTokenDescriptor PopulateSubjectForSecurityTokenDescriptor( + SecurityTokenDescriptor securityTokenDescriptor, + string tokenHandlerType) + { + ClaimsIdentity subject = tokenHandlerType switch + { + "JWT" => Default.ClaimsIdentity, + "SAML" or "SAML2" => Default.SamlClaimsIdentity, + _ => throw new NotImplementedException(tokenHandlerType) + }; + + securityTokenDescriptor.Subject = subject; + + return securityTokenDescriptor; + } + + private ValidationParameters CreateValidationParametersSkippingValidations() + { + var validationParameters = new ValidationParameters(); + + validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation; + validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; + validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation; + validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation; + validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation; + validationParameters.SignatureValidator = SkipValidationDelegates.SkipSignatureValidation; + validationParameters.TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation; + validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation; + + return validationParameters; + } + + public SecurityTokenDescriptor SecurityTokenDescriptor + { + get => _securityTokenDescriptor!; + set => _securityTokenDescriptor = PopulateSubjectForSecurityTokenDescriptor(value, _tokenHandlerType); + } + + internal ITestingTokenHandler TokenHandler { get; } + + public bool IsValid { get; set; } + + internal ValidationParameters ValidationParameters { get; } + + internal ValidationError? ValidationError { get; set; } + + internal int ExtraStackFrames { get; } + + internal ExpectedException? ExpectedInnerException { get; set; } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ITestingTokenHandler.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ITestingTokenHandler.cs new file mode 100644 index 0000000000..ebfb32d418 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ITestingTokenHandler.cs @@ -0,0 +1,171 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.IdentityModel.JsonWebTokens; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Tokens.Saml; +using Microsoft.IdentityModel.Tokens.Saml2; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + // This interface is used to test the extensibility of the ValidateTokenAsync method + // in the JsonWebTokenHandler, SamlSecurityTokenHandler, and Saml2SecurityTokenHandler classes, + // since the ValidateTokenAsync method with ValidationParameters is not part of any shared interface. + internal interface ITestingTokenHandler + { + Task> ValidateTokenAsync( + string token, + ValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken); + + Task ValidateTokenAsync( + string token, + TokenValidationParameters validationParameters); + + Task> ValidateTokenAsync( + SecurityToken token, + ValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken); + + SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor); + string CreateStringToken(SecurityTokenDescriptor tokenDescriptor); + } + + // Because the ValidateTokenAsync method in the token handler subclasses is internal, we need + // to create classes that implement the IValidateTokenAsyncResult interface to use in tests. + internal class JsonWebTokenHandlerWithResult : ITestingTokenHandler + { + private readonly JsonWebTokenHandler _handler = new JsonWebTokenHandler(); + + public JsonWebTokenHandlerWithResult() + { + } + + public async Task> ValidateTokenAsync( + SecurityToken token, + ValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + return await _handler.ValidateTokenAsync(token, validationParameters, callContext, cancellationToken); + } + + public async Task> ValidateTokenAsync( + string token, + ValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + return await _handler.ValidateTokenAsync(token, validationParameters, callContext, cancellationToken); + } + + public async Task ValidateTokenAsync( + string token, + TokenValidationParameters validationParameters) + { + return await _handler.ValidateTokenAsync(token, validationParameters); + } + + public SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor) + { + return _handler.ReadToken(_handler.CreateToken(tokenDescriptor)); + } + + public string CreateStringToken(SecurityTokenDescriptor tokenDescriptor) + { + return _handler.CreateToken(tokenDescriptor); + } + } + + internal class SamlSecurityTokenHandlerWithResult : ITestingTokenHandler + { + private readonly SamlSecurityTokenHandler _handler = new SamlSecurityTokenHandler(); + + public async Task> ValidateTokenAsync( + SecurityToken token, + ValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + return await _handler.ValidateTokenAsync(token, validationParameters, callContext, cancellationToken); + } + + public async Task> ValidateTokenAsync( + string token, + ValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + return await _handler.ValidateTokenAsync(token, validationParameters, callContext, cancellationToken); + } + + public async Task ValidateTokenAsync( + string token, + TokenValidationParameters validationParameters) + { + return await _handler.ValidateTokenAsync(token, validationParameters); + } + + public SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor) + { + SamlSecurityToken token = (SamlSecurityToken)_handler.CreateToken(tokenDescriptor); + // SamlSecurityTokenHandler.CreateToken does not set correctly the signature on the token. + // Reading the token from the CanonicalString will set the signature correctly. + return _handler.ReadToken(token.Assertion.CanonicalString); + } + + public string CreateStringToken(SecurityTokenDescriptor tokenDescriptor) + { + return ((SamlSecurityToken)_handler.CreateToken(tokenDescriptor)).Assertion.CanonicalString; + } + } + + internal class Saml2SecurityTokenHandlerWithResult : ITestingTokenHandler + { + private readonly Saml2SecurityTokenHandler _handler = new Saml2SecurityTokenHandler(); + + public async Task> ValidateTokenAsync( + SecurityToken token, + ValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + return await _handler.ValidateTokenAsync(token, validationParameters, callContext, cancellationToken); + } + + public async Task> ValidateTokenAsync( + string token, + ValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + return await _handler.ValidateTokenAsync(token, validationParameters, callContext, cancellationToken); + } + + public async Task ValidateTokenAsync( + string token, + TokenValidationParameters validationParameters) + { + return await _handler.ValidateTokenAsync(token, validationParameters); + } + + public SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor) + { + Saml2SecurityToken token = (Saml2SecurityToken)_handler.CreateToken(tokenDescriptor); + // SamlSecurityTokenHandler.CreateToken does not set correctly the signature on the token. + // Reading the token from the CanonicalString will set the signature correctly. + return _handler.ReadToken(token.Assertion.CanonicalString); + } + + public string CreateStringToken(SecurityTokenDescriptor tokenDescriptor) + { + return ((Saml2SecurityToken)_handler.CreateToken(tokenDescriptor)).Assertion.CanonicalString; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerExtensibilityTestCases.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerExtensibilityTestCases.cs new file mode 100644 index 0000000000..296708b70a --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerExtensibilityTestCases.cs @@ -0,0 +1,207 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using Xunit; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Logging; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public partial class ExtensibilityTesting + { + public static TheoryData GenerateIssuerExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames, + string stackFrameFileName) + { + var theoryData = new TheoryData(); + CallContext callContext = new CallContext(); + string issuerGuid = Guid.NewGuid().ToString(); + + #region return CustomIssuerValidationError + // Test cases where delegate is overridden and return an CustomIssuerValidationError + // CustomIssuerValidationError : IssuerValidationError, ExceptionType: SecurityTokenInvalidIssuerException + theoryData.Add(new IssuerExtensibilityTheoryData( + "CustomIssuerValidatorDelegate", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync, + extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidIssuerException), + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync)), + ValidationError = new CustomIssuerValidationError( + new MessageDetail( + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorDelegateAsync), null), + ValidationFailureType.IssuerValidationFailed, + typeof(SecurityTokenInvalidIssuerException), + new StackFrame("CustomIssuerValidationDelegates.cs", 0), + issuerGuid) + }); + + // CustomIssuerValidationError : IssuerValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException + theoryData.Add(new IssuerExtensibilityTheoryData( + "CustomIssuerValidatorCustomExceptionDelegate", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync, + extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenInvalidIssuerException), + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync)), + ValidationError = new CustomIssuerValidationError( + new MessageDetail( + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync), null), + ValidationFailureType.IssuerValidationFailed, + typeof(CustomSecurityTokenInvalidIssuerException), + new StackFrame("CustomIssuerValidationDelegates.cs", 0), + issuerGuid), + }); + + // CustomIssuerValidationError : IssuerValidationError, ExceptionType: NotSupportedException : SystemException + theoryData.Add(new IssuerExtensibilityTheoryData( + "CustomIssuerValidatorUnknownExceptionDelegate", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync, + extraStackFrames) + { + // CustomIssuerValidationError 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(CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync))), + ValidationError = new CustomIssuerValidationError( + new MessageDetail( + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync), null), + ValidationFailureType.IssuerValidationFailed, + typeof(NotSupportedException), + new StackFrame("CustomIssuerValidationDelegates.cs", 0), + issuerGuid), + }); + + // CustomIssuerValidationError : IssuerValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomIssuerValidationFailureType + theoryData.Add(new IssuerExtensibilityTheoryData( + "CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegate", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync, + extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenInvalidIssuerException), + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync)), + ValidationError = new CustomIssuerValidationError( + new MessageDetail( + nameof(CustomIssuerValidationDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync), null), + CustomIssuerValidationError.CustomIssuerValidationFailureType, + typeof(CustomSecurityTokenInvalidIssuerException), + new StackFrame("CustomIssuerValidationDelegates.cs", 0), + issuerGuid, + null), + }); + #endregion + + #region return IssuerValidationError + // Test cases where delegate is overridden and return an IssuerValidationError + // IssuerValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerException + theoryData.Add(new IssuerExtensibilityTheoryData( + "IssuerValidatorDelegate", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync, + extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidIssuerException), + nameof(CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync)), + ValidationError = new IssuerValidationError( + new MessageDetail( + nameof(CustomIssuerValidationDelegates.IssuerValidatorDelegateAsync), null), + ValidationFailureType.IssuerValidationFailed, + typeof(SecurityTokenInvalidIssuerException), + new StackFrame("CustomIssuerValidationDelegates.cs", 0), + issuerGuid) + }); + + // IssuerValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException + theoryData.Add(new IssuerExtensibilityTheoryData( + "IssuerValidatorCustomIssuerExceptionTypeDelegate", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync, + extraStackFrames) + { + // IssuerValidationError does not handle the exception type 'CustomSecurityTokenInvalidIssuerException' + ExpectedException = ExpectedException.SecurityTokenException( + LogHelper.FormatInvariant( + Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; + typeof(CustomSecurityTokenInvalidIssuerException), + nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync))), + ValidationError = new IssuerValidationError( + new MessageDetail( + nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync), null), + ValidationFailureType.IssuerValidationFailed, + typeof(CustomSecurityTokenInvalidIssuerException), + new StackFrame("CustomIssuerValidationDelegates.cs", 0), + issuerGuid) + }); + + // IssuerValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException + theoryData.Add(new IssuerExtensibilityTheoryData( + "IssuerValidatorCustomExceptionTypeDelegate", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync, + extraStackFrames) + { + // IssuerValidationError 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(CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync))), + ValidationError = new IssuerValidationError( + new MessageDetail( + nameof(CustomIssuerValidationDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync), null), + ValidationFailureType.IssuerValidationFailed, + typeof(CustomSecurityTokenException), + new StackFrame("CustomIssuerValidationDelegates.cs", 0), + issuerGuid) + }); + + // IssuerValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerException, inner: CustomSecurityTokenInvalidIssuerException + theoryData.Add(new IssuerExtensibilityTheoryData( + "IssuerValidatorThrows", + tokenHandlerType, + issuerGuid, + CustomIssuerValidationDelegates.IssuerValidatorThrows, + extraStackFrames - 1) // when throwing an exception, the stack trace contains one less frame + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidIssuerException), + string.Format(Tokens.LogMessages.IDX10269), + typeof(CustomSecurityTokenInvalidIssuerException)), + ValidationError = new IssuerValidationError( + new MessageDetail( + string.Format(Tokens.LogMessages.IDX10269), null), + ValidationFailureType.IssuerValidatorThrew, + typeof(SecurityTokenInvalidIssuerException), + new StackFrame(stackFrameFileName, 0), + issuerGuid, + new SecurityTokenInvalidIssuerException(nameof(CustomIssuerValidationDelegates.IssuerValidatorThrows)) + ) + }); + #endregion + + return theoryData; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerExtensibilityTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerExtensibilityTheoryData.cs new file mode 100644 index 0000000000..329658ddde --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerExtensibilityTheoryData.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.IdentityModel.Tokens; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public class IssuerExtensibilityTheoryData : ExtensibilityTheoryData + { + internal IssuerExtensibilityTheoryData( + string testId, + string tokenHandlerType, + string issuer, + IssuerValidationDelegateAsync issuerValidationDelegate, + int extraStackFrames) : base(testId, tokenHandlerType, extraStackFrames) + { + SecurityTokenDescriptor = new() + { + Issuer = issuer, + }; + + ValidationParameters.IssuerValidatorAsync = issuerValidationDelegate; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerSigningKeyExtensibilityTestCases.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerSigningKeyExtensibilityTestCases.cs new file mode 100644 index 0000000000..e504f3c82a --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerSigningKeyExtensibilityTestCases.cs @@ -0,0 +1,198 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using Xunit; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Logging; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public partial class ExtensibilityTesting + { + public static TheoryData GenerateIssuerSigningKeyExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames, + string stackFrameFileName) + { + var theoryData = new TheoryData(); + CallContext callContext = new CallContext(); + string issuerGuid = Guid.NewGuid().ToString(); + + #region return CustomIssuerSigningKeyValidationError + // Test cases where delegate is overridden and return a CustomIssuerSigningKeyValidationError + // CustomIssuerSigningKeyValidationError : IssuerSigningKeyValidationError, ExceptionType: SecurityTokenInvalidIssuerSigningKeyException + theoryData.Add(new IssuerSigningKeyExtensibilityTheoryData( + "CustomIssuerSigningKeyValidatorDelegate", + tokenHandlerType, + CustomIssuerSigningKeyValidationDelegates.CustomIssuerSigningKeyValidatorDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidSigningKeyException), + nameof(CustomIssuerSigningKeyValidationDelegates.CustomIssuerSigningKeyValidatorDelegate)), + ValidationError = new CustomIssuerSigningKeyValidationError( + new MessageDetail( + nameof(CustomIssuerSigningKeyValidationDelegates.CustomIssuerSigningKeyValidatorDelegate), null), + ValidationFailureType.SigningKeyValidationFailed, + typeof(SecurityTokenInvalidSigningKeyException), + new StackFrame("CustomIssuerSigningKeyValidationDelegates.cs", 0), + null) + }); + + // CustomIssuerSigningKeyValidationError : IssuerSigningKeyValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerSigningKeyException : SecurityTokenInvalidIssuerSigningKeyException + theoryData.Add(new IssuerSigningKeyExtensibilityTheoryData( + "CustomIssuerSigningKeyValidatorCustomExceptionDelegate", + tokenHandlerType, + CustomIssuerSigningKeyValidationDelegates.CustomIssuerSigningKeyValidatorCustomExceptionDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenInvalidSigningKeyException), + nameof(CustomIssuerSigningKeyValidationDelegates.CustomIssuerSigningKeyValidatorCustomExceptionDelegate)), + ValidationError = new CustomIssuerSigningKeyValidationError( + new MessageDetail( + nameof(CustomIssuerSigningKeyValidationDelegates.CustomIssuerSigningKeyValidatorCustomExceptionDelegate), null), + ValidationFailureType.SigningKeyValidationFailed, + typeof(CustomSecurityTokenInvalidSigningKeyException), + new StackFrame("CustomIssuerSigningKeyValidationDelegates.cs", 0), + null) + }); + + // CustomIssuerSigningKeyValidationError : IssuerSigningKeyValidationError, ExceptionType: NotSupportedException : SystemException + theoryData.Add(new IssuerSigningKeyExtensibilityTheoryData( + "CustomIssuerSigningKeyValidatorUnknownExceptionDelegate", + tokenHandlerType, + CustomIssuerSigningKeyValidationDelegates.CustomIssuerSigningKeyValidatorUnknownExceptionDelegate, + extraStackFrames: extraStackFrames) + { + // CustomIssuerSigningKeyValidationError 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(CustomIssuerSigningKeyValidationDelegates.CustomIssuerSigningKeyValidatorUnknownExceptionDelegate))), + ValidationError = new CustomIssuerSigningKeyValidationError( + new MessageDetail( + nameof(CustomIssuerSigningKeyValidationDelegates.CustomIssuerSigningKeyValidatorUnknownExceptionDelegate), null), + ValidationFailureType.SigningKeyValidationFailed, + typeof(NotSupportedException), + new StackFrame("CustomIssuerSigningKeyValidationDelegates.cs", 0), + null) + }); + + // CustomIssuerSigningKeyValidationError : IssuerSigningKeyValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomAudienceValidationFailureType + theoryData.Add(new IssuerSigningKeyExtensibilityTheoryData( + "CustomIssuerSigningKeyValidatorCustomExceptionCustomFailureTypeDelegate", + tokenHandlerType, + CustomIssuerSigningKeyValidationDelegates.CustomIssuerSigningKeyValidatorCustomExceptionCustomFailureTypeDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenInvalidSigningKeyException), + nameof(CustomIssuerSigningKeyValidationDelegates.CustomIssuerSigningKeyValidatorCustomExceptionCustomFailureTypeDelegate)), + ValidationError = new CustomIssuerSigningKeyValidationError( + new MessageDetail( + nameof(CustomIssuerSigningKeyValidationDelegates.CustomIssuerSigningKeyValidatorCustomExceptionCustomFailureTypeDelegate), null), + CustomIssuerSigningKeyValidationError.CustomIssuerSigningKeyValidationFailureType, + typeof(CustomSecurityTokenInvalidSigningKeyException), + new StackFrame("CustomIssuerSigningKeyValidationDelegates.cs", 0), + null), + }); + #endregion + + #region return IssuerSigningKeyValidationError + // Test cases where delegate is overridden and return an IssuerSigningKeyValidationError + // IssuerSigningKeyValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerSigningKeyException + theoryData.Add(new IssuerSigningKeyExtensibilityTheoryData( + "IssuerSigningKeyValidatorDelegate", + tokenHandlerType, + CustomIssuerSigningKeyValidationDelegates.IssuerSigningKeyValidatorDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidSigningKeyException), + nameof(CustomIssuerSigningKeyValidationDelegates.IssuerSigningKeyValidatorDelegate)), + ValidationError = new IssuerSigningKeyValidationError( + new MessageDetail( + nameof(CustomIssuerSigningKeyValidationDelegates.IssuerSigningKeyValidatorDelegate), null), + ValidationFailureType.SigningKeyValidationFailed, + typeof(SecurityTokenInvalidSigningKeyException), + new StackFrame("CustomIssuerSigningKeyValidationDelegates.cs", 0), + null) + }); + + // IssuerSigningKeyValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerSigningKeyException : SecurityTokenInvalidIssuerSigningKeyException + theoryData.Add(new IssuerSigningKeyExtensibilityTheoryData( + "IssuerSigningKeyValidatorCustomIssuerSigningKeyExceptionTypeDelegate", + tokenHandlerType, + CustomIssuerSigningKeyValidationDelegates.IssuerSigningKeyValidatorCustomIssuerSigningKeyExceptionTypeDelegate, + extraStackFrames: extraStackFrames) + { + // IssuerSigningKeyValidationError does not handle the exception type 'CustomSecurityTokenInvalidIssuerSigningKeyException' + ExpectedException = ExpectedException.SecurityTokenException( + LogHelper.FormatInvariant( + Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; + typeof(CustomSecurityTokenInvalidSigningKeyException), + nameof(CustomIssuerSigningKeyValidationDelegates.IssuerSigningKeyValidatorCustomIssuerSigningKeyExceptionTypeDelegate))), + ValidationError = new IssuerSigningKeyValidationError( + new MessageDetail( + nameof(CustomIssuerSigningKeyValidationDelegates.IssuerSigningKeyValidatorCustomIssuerSigningKeyExceptionTypeDelegate), null), + ValidationFailureType.SigningKeyValidationFailed, + typeof(CustomSecurityTokenInvalidSigningKeyException), + new StackFrame("CustomIssuerSigningKeyValidationDelegates.cs", 0), + null) + }); + + // IssuerSigningKeyValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException + theoryData.Add(new IssuerSigningKeyExtensibilityTheoryData( + "IssuerSigningKeyValidatorCustomExceptionTypeDelegate", + tokenHandlerType, + CustomIssuerSigningKeyValidationDelegates.IssuerSigningKeyValidatorCustomExceptionTypeDelegate, + extraStackFrames: extraStackFrames) + { + // IssuerSigningKeyValidationError 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(CustomIssuerSigningKeyValidationDelegates.IssuerSigningKeyValidatorCustomExceptionTypeDelegate))), + ValidationError = new IssuerSigningKeyValidationError( + new MessageDetail( + nameof(CustomIssuerSigningKeyValidationDelegates.IssuerSigningKeyValidatorCustomExceptionTypeDelegate), null), + ValidationFailureType.SigningKeyValidationFailed, + typeof(CustomSecurityTokenException), + new StackFrame("CustomIssuerSigningKeyValidationDelegates.cs", 0), + null) + }); + + // IssuerSigningKeyValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerSigningKeyException, inner: CustomSecurityTokenInvalidIssuerSigningKeyException + theoryData.Add(new IssuerSigningKeyExtensibilityTheoryData( + "IssuerSigningKeyValidatorThrows", + tokenHandlerType, + CustomIssuerSigningKeyValidationDelegates.IssuerSigningKeyValidatorThrows, + extraStackFrames: extraStackFrames - 1) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidSigningKeyException), + string.Format(Tokens.LogMessages.IDX10274), + typeof(CustomSecurityTokenInvalidSigningKeyException)), + ValidationError = new IssuerSigningKeyValidationError( + new MessageDetail( + string.Format(Tokens.LogMessages.IDX10274), null), + ValidationFailureType.IssuerSigningKeyValidatorThrew, + typeof(SecurityTokenInvalidSigningKeyException), + new StackFrame(stackFrameFileName, 0), + null, + new SecurityTokenInvalidSigningKeyException(nameof(CustomIssuerSigningKeyValidationDelegates.IssuerSigningKeyValidatorThrows)) + ) + }); + #endregion + + return theoryData; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerSigningKeyExtensibilityTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerSigningKeyExtensibilityTheoryData.cs new file mode 100644 index 0000000000..990ed5152a --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/IssuerSigningKeyExtensibilityTheoryData.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.IdentityModel.Tokens; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public class IssuerSigningKeyExtensibilityTheoryData : ExtensibilityTheoryData + { + internal IssuerSigningKeyExtensibilityTheoryData( + string testId, + string tokenHandlerType, + IssuerSigningKeyValidationDelegate issuerSigningKeyValidationDelegate, + int extraStackFrames) : base(testId, tokenHandlerType, extraStackFrames) + { + var signingCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2; + + SecurityTokenDescriptor = new() + { + Issuer = Default.Issuer, + SigningCredentials = signingCredentials, + }; + + ValidationParameters.IssuerSigningKeyValidator = issuerSigningKeyValidationDelegate; + ValidationParameters.SignatureValidator = (SecurityToken token, ValidationParameters validationParameters, BaseConfiguration? configuration, CallContext callContext) => + { + token.SigningKey = signingCredentials.Key; + + return signingCredentials.Key; + }; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/LifetimeExtensibilityTestCases.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/LifetimeExtensibilityTestCases.cs new file mode 100644 index 0000000000..24c92c20f5 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/LifetimeExtensibilityTestCases.cs @@ -0,0 +1,215 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using Xunit; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Logging; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public partial class ExtensibilityTesting + { + public static TheoryData GenerateLifetimeExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames, + string stackFrameFileName) + { + TheoryData theoryData = new(); + CallContext callContext = new CallContext(); + DateTime utcNow = DateTime.UtcNow; + DateTime utcPlusOneHour = utcNow.AddHours(1); + + #region return CustomLifetimeValidationError + // Test cases where delegate is overridden and return a CustomLifetimeValidationError + // CustomLifetimeValidationError : LifetimeValidationError, ExceptionType: SecurityTokenInvalidLifetimeException + theoryData.Add(new LifetimeExtensibilityTheoryData( + "CustomLifetimeValidatorDelegate", + tokenHandlerType, + utcNow, + CustomLifetimeValidationDelegates.CustomLifetimeValidatorDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidLifetimeException), + nameof(CustomLifetimeValidationDelegates.CustomLifetimeValidatorDelegate)), + ValidationError = new CustomLifetimeValidationError( + new MessageDetail( + nameof(CustomLifetimeValidationDelegates.CustomLifetimeValidatorDelegate), null), + ValidationFailureType.LifetimeValidationFailed, + typeof(SecurityTokenInvalidLifetimeException), + new StackFrame("CustomLifetimeValidationDelegates.cs", 0), + utcNow, + utcPlusOneHour) + }); + + // CustomLifetimeValidationError : LifetimeValidationError, ExceptionType: CustomSecurityTokenInvalidLifetimeException : SecurityTokenInvalidLifetimeException + theoryData.Add(new LifetimeExtensibilityTheoryData( + "CustomLifetimeValidatorCustomExceptionDelegate", + tokenHandlerType, + utcNow, + CustomLifetimeValidationDelegates.CustomLifetimeValidatorCustomExceptionDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenInvalidLifetimeException), + nameof(CustomLifetimeValidationDelegates.CustomLifetimeValidatorCustomExceptionDelegate)), + ValidationError = new CustomLifetimeValidationError( + new MessageDetail( + nameof(CustomLifetimeValidationDelegates.CustomLifetimeValidatorCustomExceptionDelegate), null), + ValidationFailureType.LifetimeValidationFailed, + typeof(CustomSecurityTokenInvalidLifetimeException), + new StackFrame("CustomLifetimeValidationDelegates.cs", 0), + utcNow, + utcPlusOneHour), + }); + + // CustomLifetimeValidationError : LifetimeValidationError, ExceptionType: NotSupportedException : SystemException + theoryData.Add(new LifetimeExtensibilityTheoryData( + "CustomLifetimeValidatorUnknownExceptionDelegate", + tokenHandlerType, + utcNow, + CustomLifetimeValidationDelegates.CustomLifetimeValidatorUnknownExceptionDelegate, + extraStackFrames: extraStackFrames) + { + // CustomLifetimeValidationError 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(CustomLifetimeValidationDelegates.CustomLifetimeValidatorUnknownExceptionDelegate))), + ValidationError = new CustomLifetimeValidationError( + new MessageDetail( + nameof(CustomLifetimeValidationDelegates.CustomLifetimeValidatorUnknownExceptionDelegate), null), + ValidationFailureType.LifetimeValidationFailed, + typeof(NotSupportedException), + new StackFrame("CustomLifetimeValidationDelegates.cs", 0), + utcNow, + utcPlusOneHour), + }); + + // CustomLifetimeValidationError : LifetimeValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomAudienceValidationFailureType + theoryData.Add(new LifetimeExtensibilityTheoryData( + "CustomLifetimeValidatorCustomExceptionCustomFailureTypeDelegate", + tokenHandlerType, + utcNow, + CustomLifetimeValidationDelegates.CustomLifetimeValidatorCustomExceptionCustomFailureTypeDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenInvalidLifetimeException), + nameof(CustomLifetimeValidationDelegates.CustomLifetimeValidatorCustomExceptionCustomFailureTypeDelegate)), + ValidationError = new CustomLifetimeValidationError( + new MessageDetail( + nameof(CustomLifetimeValidationDelegates.CustomLifetimeValidatorCustomExceptionCustomFailureTypeDelegate), null), + CustomLifetimeValidationError.CustomLifetimeValidationFailureType, + typeof(CustomSecurityTokenInvalidLifetimeException), + new StackFrame("CustomLifetimeValidationDelegates.cs", 0), + utcNow, + utcPlusOneHour), + }); + #endregion + + #region return LifetimeValidationError + // Test cases where delegate is overridden and return an LifetimeValidationError + // LifetimeValidationError : ValidationError, ExceptionType: SecurityTokenInvalidLifetimeException + theoryData.Add(new LifetimeExtensibilityTheoryData( + "LifetimeValidatorDelegate", + tokenHandlerType, + utcNow, + CustomLifetimeValidationDelegates.LifetimeValidatorDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidLifetimeException), + nameof(CustomLifetimeValidationDelegates.LifetimeValidatorDelegate)), + ValidationError = new LifetimeValidationError( + new MessageDetail( + nameof(CustomLifetimeValidationDelegates.LifetimeValidatorDelegate), null), + ValidationFailureType.LifetimeValidationFailed, + typeof(SecurityTokenInvalidLifetimeException), + new StackFrame("CustomLifetimeValidationDelegates.cs", 0), + utcNow, + utcPlusOneHour) + }); + + // LifetimeValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidLifetimeException : SecurityTokenInvalidLifetimeException + theoryData.Add(new LifetimeExtensibilityTheoryData( + "LifetimeValidatorCustomLifetimeExceptionTypeDelegate", + tokenHandlerType, + utcNow, + CustomLifetimeValidationDelegates.LifetimeValidatorCustomLifetimeExceptionTypeDelegate, + extraStackFrames: extraStackFrames) + { + // LifetimeValidationError does not handle the exception type 'CustomSecurityTokenInvalidLifetimeException' + ExpectedException = ExpectedException.SecurityTokenException( + LogHelper.FormatInvariant( + Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; + typeof(CustomSecurityTokenInvalidLifetimeException), + nameof(CustomLifetimeValidationDelegates.LifetimeValidatorCustomLifetimeExceptionTypeDelegate))), + ValidationError = new LifetimeValidationError( + new MessageDetail( + nameof(CustomLifetimeValidationDelegates.LifetimeValidatorCustomLifetimeExceptionTypeDelegate), null), + ValidationFailureType.LifetimeValidationFailed, + typeof(CustomSecurityTokenInvalidLifetimeException), + new StackFrame("CustomLifetimeValidationDelegates.cs", 0), + utcNow, + utcPlusOneHour) + }); + + // LifetimeValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException + theoryData.Add(new LifetimeExtensibilityTheoryData( + "LifetimeValidatorCustomExceptionTypeDelegate", + tokenHandlerType, + utcNow, + CustomLifetimeValidationDelegates.LifetimeValidatorCustomExceptionTypeDelegate, + extraStackFrames: extraStackFrames) + { + // LifetimeValidationError 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(CustomLifetimeValidationDelegates.LifetimeValidatorCustomExceptionTypeDelegate))), + ValidationError = new LifetimeValidationError( + new MessageDetail( + nameof(CustomLifetimeValidationDelegates.LifetimeValidatorCustomExceptionTypeDelegate), null), + ValidationFailureType.LifetimeValidationFailed, + typeof(CustomSecurityTokenException), + new StackFrame("CustomLifetimeValidationDelegates.cs", 0), + utcNow, + utcPlusOneHour) + }); + + // LifetimeValidationError : ValidationError, ExceptionType: SecurityTokenInvalidLifetimeException, inner: CustomSecurityTokenInvalidLifetimeException + theoryData.Add(new LifetimeExtensibilityTheoryData( + "LifetimeValidatorThrows", + tokenHandlerType, + utcNow, + CustomLifetimeValidationDelegates.LifetimeValidatorThrows, + extraStackFrames: extraStackFrames - 1) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidLifetimeException), + string.Format(Tokens.LogMessages.IDX10271), + typeof(CustomSecurityTokenInvalidLifetimeException)), + ValidationError = new LifetimeValidationError( + new MessageDetail( + string.Format(Tokens.LogMessages.IDX10271), null), + ValidationFailureType.LifetimeValidatorThrew, + typeof(SecurityTokenInvalidLifetimeException), + new StackFrame(stackFrameFileName, 0), + utcNow, + utcPlusOneHour, + new SecurityTokenInvalidLifetimeException(nameof(CustomLifetimeValidationDelegates.LifetimeValidatorThrows)) + ) + }); + #endregion + + return theoryData; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/LifetimeExtensibilityTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/LifetimeExtensibilityTheoryData.cs new file mode 100644 index 0000000000..f4e209f8e9 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/LifetimeExtensibilityTheoryData.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.IdentityModel.Tokens; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public class LifetimeExtensibilityTheoryData : ExtensibilityTheoryData + { + internal LifetimeExtensibilityTheoryData( + string testId, + string tokenHandlerType, + DateTime utcNow, + LifetimeValidationDelegate lifetimeValidationDelegate, + int extraStackFrames) : base(testId, tokenHandlerType, extraStackFrames) + { + SecurityTokenDescriptor = new() + { + Issuer = Default.Issuer, + IssuedAt = utcNow.AddHours(-1), + NotBefore = utcNow, + Expires = utcNow.AddHours(1), + }; + + ValidationParameters.LifetimeValidator = lifetimeValidationDelegate; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/SignatureExtensibilityTestCases.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/SignatureExtensibilityTestCases.cs new file mode 100644 index 0000000000..d943f6041d --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/SignatureExtensibilityTestCases.cs @@ -0,0 +1,191 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using Xunit; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Logging; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public partial class ExtensibilityTesting + { + public static TheoryData GenerateSignatureExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames, + string stackFrameFileName) + { + var theoryData = new TheoryData(); + CallContext callContext = new CallContext(); + string issuerGuid = Guid.NewGuid().ToString(); + + #region return CustomSignatureValidationError + // Test cases where delegate is overridden and return a CustomSignatureValidationError + // CustomSignatureValidationError : SignatureValidationError, ExceptionType: SecurityTokenInvalidSignatureException + theoryData.Add(new SignatureExtensibilityTheoryData( + "CustomSignatureValidatorDelegate", + tokenHandlerType, + CustomSignatureValidationDelegates.CustomSignatureValidatorDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidSignatureException), + nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorDelegate)), + ValidationError = new CustomSignatureValidationError( + new MessageDetail( + nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorDelegate), null), + ValidationFailureType.SignatureValidationFailed, + typeof(SecurityTokenInvalidSignatureException), + new StackFrame("CustomSignatureValidationDelegates.cs", 0)) + }); + + // CustomSignatureValidationError : SignatureValidationError, ExceptionType: CustomSecurityTokenInvalidSignatureException : SecurityTokenInvalidSignatureException + theoryData.Add(new SignatureExtensibilityTheoryData( + "CustomSignatureValidatorCustomExceptionDelegate", + tokenHandlerType, + CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenInvalidSignatureException), + nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionDelegate)), + ValidationError = new CustomSignatureValidationError( + new MessageDetail( + nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionDelegate), null), + ValidationFailureType.SignatureValidationFailed, + typeof(CustomSecurityTokenInvalidSignatureException), + new StackFrame("CustomSignatureValidationDelegates.cs", 0)), + }); + + // CustomSignatureValidationError : SignatureValidationError, ExceptionType: NotSupportedException : SystemException + theoryData.Add(new SignatureExtensibilityTheoryData( + "CustomSignatureValidatorUnknownExceptionDelegate", + tokenHandlerType, + CustomSignatureValidationDelegates.CustomSignatureValidatorUnknownExceptionDelegate, + extraStackFrames: extraStackFrames) + { + // 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))), + ValidationError = new CustomSignatureValidationError( + new MessageDetail( + nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorUnknownExceptionDelegate), null), + ValidationFailureType.SignatureValidationFailed, + typeof(NotSupportedException), + new StackFrame("CustomSignatureValidationDelegates.cs", 0)), + }); + + // CustomSignatureValidationError : SignatureValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomAudienceValidationFailureType + theoryData.Add(new SignatureExtensibilityTheoryData( + "CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate", + tokenHandlerType, + CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenInvalidSignatureException), + nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate)), + ValidationError = new CustomSignatureValidationError( + new MessageDetail( + nameof(CustomSignatureValidationDelegates.CustomSignatureValidatorCustomExceptionCustomFailureTypeDelegate), null), + CustomSignatureValidationError.CustomSignatureValidationFailureType, + typeof(CustomSecurityTokenInvalidSignatureException), + new StackFrame("CustomSignatureValidationDelegates.cs", 0)), + }); + #endregion + + #region return SignatureValidationError + // Test cases where delegate is overridden and return an SignatureValidationError + // SignatureValidationError : ValidationError, ExceptionType: SecurityTokenInvalidSignatureException + theoryData.Add(new SignatureExtensibilityTheoryData( + "SignatureValidatorDelegate", + tokenHandlerType, + CustomSignatureValidationDelegates.SignatureValidatorDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidSignatureException), + nameof(CustomSignatureValidationDelegates.SignatureValidatorDelegate)), + ValidationError = new SignatureValidationError( + new MessageDetail( + nameof(CustomSignatureValidationDelegates.SignatureValidatorDelegate), null), + ValidationFailureType.SignatureValidationFailed, + typeof(SecurityTokenInvalidSignatureException), + new StackFrame("CustomSignatureValidationDelegates.cs", 0)) + }); + + // SignatureValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidSignatureException : SecurityTokenInvalidSignatureException + theoryData.Add(new SignatureExtensibilityTheoryData( + "SignatureValidatorCustomSignatureExceptionTypeDelegate", + tokenHandlerType, + CustomSignatureValidationDelegates.SignatureValidatorCustomSignatureExceptionTypeDelegate, + extraStackFrames: extraStackFrames) + { + // 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))), + ValidationError = new SignatureValidationError( + new MessageDetail( + nameof(CustomSignatureValidationDelegates.SignatureValidatorCustomSignatureExceptionTypeDelegate), null), + ValidationFailureType.SignatureValidationFailed, + typeof(CustomSecurityTokenInvalidSignatureException), + new StackFrame("CustomSignatureValidationDelegates.cs", 0)) + }); + + // SignatureValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException + theoryData.Add(new SignatureExtensibilityTheoryData( + "SignatureValidatorCustomExceptionTypeDelegate", + tokenHandlerType, + CustomSignatureValidationDelegates.SignatureValidatorCustomExceptionTypeDelegate, + extraStackFrames: extraStackFrames) + { + // 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))), + ValidationError = new SignatureValidationError( + new MessageDetail( + nameof(CustomSignatureValidationDelegates.SignatureValidatorCustomExceptionTypeDelegate), null), + ValidationFailureType.SignatureValidationFailed, + typeof(CustomSecurityTokenException), + new StackFrame("CustomSignatureValidationDelegates.cs", 0)) + }); + + // SignatureValidationError : ValidationError, ExceptionType: SecurityTokenInvalidSignatureException, inner: CustomSecurityTokenInvalidSignatureException + theoryData.Add(new SignatureExtensibilityTheoryData( + "SignatureValidatorThrows", + tokenHandlerType, + CustomSignatureValidationDelegates.SignatureValidatorThrows, + extraStackFrames: extraStackFrames - 1) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidSignatureException), + string.Format(Tokens.LogMessages.IDX10272), + typeof(CustomSecurityTokenInvalidSignatureException)), + ValidationError = new SignatureValidationError( + new MessageDetail( + string.Format(Tokens.LogMessages.IDX10272), null), + ValidationFailureType.SignatureValidatorThrew, + typeof(SecurityTokenInvalidSignatureException), + new StackFrame(stackFrameFileName, 0), + null, // no inner validation error + new SecurityTokenInvalidSignatureException(nameof(CustomSignatureValidationDelegates.SignatureValidatorThrows)) + ) + }); + #endregion + + return theoryData; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/SignatureExtensibilityTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/SignatureExtensibilityTheoryData.cs new file mode 100644 index 0000000000..f3d222bae8 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/SignatureExtensibilityTheoryData.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.IdentityModel.Tokens; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public class SignatureExtensibilityTheoryData : ExtensibilityTheoryData + { + internal SignatureExtensibilityTheoryData( + string testId, + string tokenHandlerType, + SignatureValidationDelegate signatureValidationDelegate, + int extraStackFrames) : base(testId, tokenHandlerType, extraStackFrames) + { + SecurityTokenDescriptor = new() + { + Issuer = Default.Issuer, + }; + + ValidationParameters.SignatureValidator = signatureValidationDelegate; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/TokenReplayExtensibilityTestCases.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/TokenReplayExtensibilityTestCases.cs new file mode 100644 index 0000000000..2828262df9 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/TokenReplayExtensibilityTestCases.cs @@ -0,0 +1,206 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using Xunit; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Logging; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public partial class ExtensibilityTesting + { + public static TheoryData GenerateTokenReplayExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames, + string stackFrameFileName) + { + var theoryData = new TheoryData(); + CallContext callContext = new CallContext(); + DateTime expirationTime = DateTime.UtcNow + TimeSpan.FromHours(1); + + #region return CustomTokenReplayValidationError + // Test cases where delegate is overridden and return a CustomTokenReplayValidationError + // CustomTokenReplayValidationError : TokenReplayValidationError, ExceptionType: SecurityTokenReplayDetectedException + theoryData.Add(new TokenReplayExtensibilityTheoryData( + "CustomTokenReplayValidationDelegate", + tokenHandlerType, + expirationTime, + CustomTokenReplayValidationDelegates.CustomTokenReplayValidationDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenReplayDetectedException), + nameof(CustomTokenReplayValidationDelegates.CustomTokenReplayValidationDelegate)), + ValidationError = new CustomTokenReplayValidationError( + new MessageDetail( + nameof(CustomTokenReplayValidationDelegates.CustomTokenReplayValidationDelegate), null), + ValidationFailureType.TokenReplayValidationFailed, + typeof(SecurityTokenReplayDetectedException), + new StackFrame("CustomTokenReplayValidationDelegates.cs", 0), + expirationTime) + }); + + // CustomTokenReplayValidationError : TokenReplayValidationError, ExceptionType: CustomSecurityTokenReplayDetectedException : SecurityTokenReplayDetectedException + theoryData.Add(new TokenReplayExtensibilityTheoryData( + "CustomTokenReplayValidatorCustomExceptionDelegate", + tokenHandlerType, + expirationTime, + CustomTokenReplayValidationDelegates.CustomTokenReplayValidatorCustomExceptionDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenReplayDetectedException), + nameof(CustomTokenReplayValidationDelegates.CustomTokenReplayValidatorCustomExceptionDelegate)), + ValidationError = new CustomTokenReplayValidationError( + new MessageDetail( + nameof(CustomTokenReplayValidationDelegates.CustomTokenReplayValidatorCustomExceptionDelegate), null), + ValidationFailureType.TokenReplayValidationFailed, + typeof(CustomSecurityTokenReplayDetectedException), + new StackFrame("CustomTokenReplayValidationDelegates.cs", 0), + expirationTime), + }); + + // CustomTokenReplayValidationError : TokenReplayValidationError, ExceptionType: NotSupportedException : SystemException + theoryData.Add(new TokenReplayExtensibilityTheoryData( + "CustomTokenReplayValidatorUnknownExceptionDelegate", + tokenHandlerType, + expirationTime, + CustomTokenReplayValidationDelegates.CustomTokenReplayValidatorUnknownExceptionDelegate, + extraStackFrames: extraStackFrames) + { + // CustomTokenReplayValidationError 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(CustomTokenReplayValidationDelegates.CustomTokenReplayValidatorUnknownExceptionDelegate))), + ValidationError = new CustomTokenReplayValidationError( + new MessageDetail( + nameof(CustomTokenReplayValidationDelegates.CustomTokenReplayValidatorUnknownExceptionDelegate), null), + ValidationFailureType.TokenReplayValidationFailed, + typeof(NotSupportedException), + new StackFrame("CustomTokenReplayValidationDelegates.cs", 0), + expirationTime), + }); + + // CustomTokenReplayValidationError : TokenReplayValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomAudienceValidationFailureType + theoryData.Add(new TokenReplayExtensibilityTheoryData( + "CustomTokenReplayValidatorCustomExceptionCustomFailureTypeDelegate", + tokenHandlerType, + expirationTime, + CustomTokenReplayValidationDelegates.CustomTokenReplayValidatorCustomExceptionCustomFailureTypeDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenReplayDetectedException), + nameof(CustomTokenReplayValidationDelegates.CustomTokenReplayValidatorCustomExceptionCustomFailureTypeDelegate)), + ValidationError = new CustomTokenReplayValidationError( + new MessageDetail( + nameof(CustomTokenReplayValidationDelegates.CustomTokenReplayValidatorCustomExceptionCustomFailureTypeDelegate), null), + CustomTokenReplayValidationError.CustomTokenReplayValidationFailureType, + typeof(CustomSecurityTokenReplayDetectedException), + new StackFrame("CustomTokenReplayValidationDelegates.cs", 0), + expirationTime), + }); + #endregion + + #region return TokenReplayValidationError + // Test cases where delegate is overridden and return an TokenReplayValidationError + // TokenReplayValidationError : ValidationError, ExceptionType: SecurityTokenReplayDetectedException + theoryData.Add(new TokenReplayExtensibilityTheoryData( + "TokenReplayValidationDelegate", + tokenHandlerType, + expirationTime, + CustomTokenReplayValidationDelegates.TokenReplayValidationDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenReplayDetectedException), + nameof(CustomTokenReplayValidationDelegates.TokenReplayValidationDelegate)), + ValidationError = new TokenReplayValidationError( + new MessageDetail( + nameof(CustomTokenReplayValidationDelegates.TokenReplayValidationDelegate), null), + ValidationFailureType.TokenReplayValidationFailed, + typeof(SecurityTokenReplayDetectedException), + new StackFrame("CustomTokenReplayValidationDelegates.cs", 0), + expirationTime) + }); + + // TokenReplayValidationError : ValidationError, ExceptionType: CustomSecurityTokenReplayDetectedException : SecurityTokenReplayDetectedException + theoryData.Add(new TokenReplayExtensibilityTheoryData( + "TokenReplayValidatorCustomTokenReplayDetectedExceptionTypeDelegate", + tokenHandlerType, + expirationTime, + CustomTokenReplayValidationDelegates.TokenReplayValidatorCustomTokenReplayDetectedExceptionTypeDelegate, + extraStackFrames: extraStackFrames) + { + // TokenReplayValidationError does not handle the exception type 'CustomSecurityTokenReplayDetectedException' + ExpectedException = ExpectedException.SecurityTokenException( + LogHelper.FormatInvariant( + Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; + typeof(CustomSecurityTokenReplayDetectedException), + nameof(CustomTokenReplayValidationDelegates.TokenReplayValidatorCustomTokenReplayDetectedExceptionTypeDelegate))), + ValidationError = new TokenReplayValidationError( + new MessageDetail( + nameof(CustomTokenReplayValidationDelegates.TokenReplayValidatorCustomTokenReplayDetectedExceptionTypeDelegate), null), + ValidationFailureType.TokenReplayValidationFailed, + typeof(CustomSecurityTokenReplayDetectedException), + new StackFrame("CustomTokenReplayValidationDelegates.cs", 0), + expirationTime) + }); + + // TokenReplayValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException + theoryData.Add(new TokenReplayExtensibilityTheoryData( + "TokenReplayValidatorCustomExceptionTypeDelegate", + tokenHandlerType, + expirationTime, + CustomTokenReplayValidationDelegates.TokenReplayValidatorCustomExceptionTypeDelegate, + extraStackFrames: extraStackFrames) + { + // TokenReplayValidationError 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(CustomTokenReplayValidationDelegates.TokenReplayValidatorCustomExceptionTypeDelegate))), + ValidationError = new TokenReplayValidationError( + new MessageDetail( + nameof(CustomTokenReplayValidationDelegates.TokenReplayValidatorCustomExceptionTypeDelegate), null), + ValidationFailureType.TokenReplayValidationFailed, + typeof(CustomSecurityTokenException), + new StackFrame("CustomTokenReplayValidationDelegates.cs", 0), + expirationTime) + }); + + // TokenReplayValidationError : ValidationError, ExceptionType: SecurityTokenReplayDetectedException, inner: CustomSecurityTokenReplayDetectedException + theoryData.Add(new TokenReplayExtensibilityTheoryData( + "TokenReplayValidatorThrows", + tokenHandlerType, + expirationTime, + CustomTokenReplayValidationDelegates.TokenReplayValidatorThrows, + extraStackFrames: extraStackFrames - 1) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenReplayDetectedException), + string.Format(Tokens.LogMessages.IDX10276), + typeof(CustomSecurityTokenReplayDetectedException)), + ValidationError = new TokenReplayValidationError( + new MessageDetail( + string.Format(Tokens.LogMessages.IDX10276), null), + ValidationFailureType.TokenReplayValidatorThrew, + typeof(SecurityTokenReplayDetectedException), + new StackFrame(stackFrameFileName, 0), + expirationTime, + new SecurityTokenReplayDetectedException(nameof(CustomTokenReplayValidationDelegates.TokenReplayValidatorThrows)) + ) + }); + #endregion + + return theoryData; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/TokenReplayExtensibilityTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/TokenReplayExtensibilityTheoryData.cs new file mode 100644 index 0000000000..20f2111df6 --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/TokenReplayExtensibilityTheoryData.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.IdentityModel.Tokens; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public class TokenReplayExtensibilityTheoryData : ExtensibilityTheoryData + { + internal TokenReplayExtensibilityTheoryData( + string testId, + string tokenHandlerType, + DateTime expirationTime, + TokenReplayValidationDelegate tokenReplayValidationDelegate, + int extraStackFrames) : base(testId, tokenHandlerType, extraStackFrames) + { + SecurityTokenDescriptor = new() + { + Issuer = Default.Issuer, + IssuedAt = expirationTime.AddMinutes(-10), + NotBefore = expirationTime.AddMinutes(-5), + Expires = expirationTime, + }; + + ValidationParameters.TokenReplayValidator = tokenReplayValidationDelegate; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/TokenTypeExtensibilityTestCases.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/TokenTypeExtensibilityTestCases.cs new file mode 100644 index 0000000000..1afd67a6dd --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/TokenTypeExtensibilityTestCases.cs @@ -0,0 +1,206 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using Xunit; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Logging; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public partial class ExtensibilityTesting + { + public static TheoryData GenerateTokenTypeExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames, + string stackFrameFileName) + { + var theoryData = new TheoryData(); + CallContext callContext = new CallContext(); + string tokenType = "NOTJWT"; + + #region return CustomTokenTypeValidationError + // Test cases where delegate is overridden and return a CustomTokenTypeValidationError + // CustomTokenTypeValidationError : TokenTypeValidationError, ExceptionType: SecurityTokenInvalidTypeException + theoryData.Add(new TokenTypeExtensibilityTheoryData( + "CustomTokenTypeValidatorDelegate", + tokenHandlerType, + tokenType, + CustomTokenTypeValidationDelegates.CustomTokenTypeValidatorDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidTypeException), + nameof(CustomTokenTypeValidationDelegates.CustomTokenTypeValidatorDelegate)), + ValidationError = new CustomTokenTypeValidationError( + new MessageDetail( + nameof(CustomTokenTypeValidationDelegates.CustomTokenTypeValidatorDelegate), null), + ValidationFailureType.TokenTypeValidationFailed, + typeof(SecurityTokenInvalidTypeException), + new StackFrame("CustomTokenTypeValidationDelegates.cs", 0), + tokenType) + }); + + // CustomTokenTypeValidationError : TokenTypeValidationError, ExceptionType: CustomSecurityTokenInvalidTypeException : SecurityTokenInvalidTypeException + theoryData.Add(new TokenTypeExtensibilityTheoryData( + "CustomTokenTypeValidatorCustomExceptionDelegate", + tokenHandlerType, + tokenType, + CustomTokenTypeValidationDelegates.CustomTokenTypeValidatorCustomExceptionDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenInvalidTypeException), + nameof(CustomTokenTypeValidationDelegates.CustomTokenTypeValidatorCustomExceptionDelegate)), + ValidationError = new CustomTokenTypeValidationError( + new MessageDetail( + nameof(CustomTokenTypeValidationDelegates.CustomTokenTypeValidatorCustomExceptionDelegate), null), + ValidationFailureType.TokenTypeValidationFailed, + typeof(CustomSecurityTokenInvalidTypeException), + new StackFrame("CustomTokenTypeValidationDelegates.cs", 0), + tokenType) + }); + + // CustomTokenTypeValidationError : TokenTypeValidationError, ExceptionType: NotSupportedException : SystemException + theoryData.Add(new TokenTypeExtensibilityTheoryData( + "CustomTokenTypeValidatorUnknownExceptionDelegate", + tokenHandlerType, + tokenType, + CustomTokenTypeValidationDelegates.CustomTokenTypeValidatorUnknownExceptionDelegate, + extraStackFrames: extraStackFrames) + { + // CustomTokenTypeValidationError 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(CustomTokenTypeValidationDelegates.CustomTokenTypeValidatorUnknownExceptionDelegate))), + ValidationError = new CustomTokenTypeValidationError( + new MessageDetail( + nameof(CustomTokenTypeValidationDelegates.CustomTokenTypeValidatorUnknownExceptionDelegate), null), + ValidationFailureType.TokenTypeValidationFailed, + typeof(NotSupportedException), + new StackFrame("CustomTokenTypeValidationDelegates.cs", 0), + tokenType) + }); + + // CustomTokenTypeValidationError : TokenTypeValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomAudienceValidationFailureType + theoryData.Add(new TokenTypeExtensibilityTheoryData( + "CustomTokenTypeValidatorCustomExceptionCustomFailureTypeDelegate", + tokenHandlerType, + tokenType, + CustomTokenTypeValidationDelegates.CustomTokenTypeValidatorCustomExceptionCustomFailureTypeDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(CustomSecurityTokenInvalidTypeException), + nameof(CustomTokenTypeValidationDelegates.CustomTokenTypeValidatorCustomExceptionCustomFailureTypeDelegate)), + ValidationError = new CustomTokenTypeValidationError( + new MessageDetail( + nameof(CustomTokenTypeValidationDelegates.CustomTokenTypeValidatorCustomExceptionCustomFailureTypeDelegate), null), + CustomTokenTypeValidationError.CustomTokenTypeValidationFailureType, + typeof(CustomSecurityTokenInvalidTypeException), + new StackFrame("CustomTokenTypeValidationDelegates.cs", 0), + tokenType), + }); + #endregion + + #region return TokenTypeValidationError + // Test cases where delegate is overridden and return an TokenTypeValidationError + // TokenTypeValidationError : ValidationError, ExceptionType: SecurityTokenInvalidTypeException + theoryData.Add(new TokenTypeExtensibilityTheoryData( + "TokenTypeValidatorDelegate", + tokenHandlerType, + tokenType, + CustomTokenTypeValidationDelegates.TokenTypeValidatorDelegate, + extraStackFrames: extraStackFrames) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidTypeException), + nameof(CustomTokenTypeValidationDelegates.TokenTypeValidatorDelegate)), + ValidationError = new TokenTypeValidationError( + new MessageDetail( + nameof(CustomTokenTypeValidationDelegates.TokenTypeValidatorDelegate), null), + ValidationFailureType.TokenTypeValidationFailed, + typeof(SecurityTokenInvalidTypeException), + new StackFrame("CustomTokenTypeValidationDelegates.cs", 0), + tokenType) + }); + + // TokenTypeValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidTypeException : SecurityTokenInvalidTypeException + theoryData.Add(new TokenTypeExtensibilityTheoryData( + "TokenTypeValidatorCustomTokenTypeExceptionTypeDelegate", + tokenHandlerType, + tokenType, + CustomTokenTypeValidationDelegates.TokenTypeValidatorCustomTokenTypeExceptionTypeDelegate, + extraStackFrames: extraStackFrames) + { + // TokenTypeValidationError does not handle the exception type 'CustomSecurityTokenInvalidTypeException' + ExpectedException = ExpectedException.SecurityTokenException( + LogHelper.FormatInvariant( + Tokens.LogMessages.IDX10002, // "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'."; + typeof(CustomSecurityTokenInvalidTypeException), + nameof(CustomTokenTypeValidationDelegates.TokenTypeValidatorCustomTokenTypeExceptionTypeDelegate))), + ValidationError = new TokenTypeValidationError( + new MessageDetail( + nameof(CustomTokenTypeValidationDelegates.TokenTypeValidatorCustomTokenTypeExceptionTypeDelegate), null), + ValidationFailureType.TokenTypeValidationFailed, + typeof(CustomSecurityTokenInvalidTypeException), + new StackFrame("CustomTokenTypeValidationDelegates.cs", 0), + tokenType) + }); + + // TokenTypeValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException + theoryData.Add(new TokenTypeExtensibilityTheoryData( + "TokenTypeValidatorCustomExceptionTypeDelegate", + tokenHandlerType, + tokenType, + CustomTokenTypeValidationDelegates.TokenTypeValidatorCustomExceptionTypeDelegate, + extraStackFrames: extraStackFrames) + { + // TokenTypeValidationError 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(CustomTokenTypeValidationDelegates.TokenTypeValidatorCustomExceptionTypeDelegate))), + ValidationError = new TokenTypeValidationError( + new MessageDetail( + nameof(CustomTokenTypeValidationDelegates.TokenTypeValidatorCustomExceptionTypeDelegate), null), + ValidationFailureType.TokenTypeValidationFailed, + typeof(CustomSecurityTokenException), + new StackFrame("CustomTokenTypeValidationDelegates.cs", 0), + tokenType) + }); + + // TokenTypeValidationError : ValidationError, ExceptionType: SecurityTokenInvalidTypeException, inner: CustomSecurityTokenInvalidTypeException + theoryData.Add(new TokenTypeExtensibilityTheoryData( + "TokenTypeValidatorThrows", + tokenHandlerType, + tokenType, + CustomTokenTypeValidationDelegates.TokenTypeValidatorThrows, + extraStackFrames: extraStackFrames - 1) + { + ExpectedException = new ExpectedException( + typeof(SecurityTokenInvalidTypeException), + string.Format(Tokens.LogMessages.IDX10275), + typeof(CustomSecurityTokenInvalidTypeException)), + ValidationError = new TokenTypeValidationError( + new MessageDetail( + string.Format(Tokens.LogMessages.IDX10275), null), + ValidationFailureType.TokenTypeValidatorThrew, + typeof(SecurityTokenInvalidTypeException), + new StackFrame("JsonWebTokenHandler.ValidateToken.Internal.cs", 0), + tokenType, + new SecurityTokenInvalidTypeException(nameof(CustomTokenTypeValidationDelegates.TokenTypeValidatorThrows)) + ) + }); + #endregion + + return theoryData; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/TokenTypeExtensibilityTheoryData.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/TokenTypeExtensibilityTheoryData.cs new file mode 100644 index 0000000000..c83ccdbd4f --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/TokenTypeExtensibilityTheoryData.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.IdentityModel.Tokens; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public class TokenTypeExtensibilityTheoryData : ExtensibilityTheoryData + { + internal TokenTypeExtensibilityTheoryData( + string testId, + string tokenHandlerType, + string tokenType, + TokenTypeValidationDelegate tokenTypeValidationDelegate, + int extraStackFrames) : base(testId, tokenHandlerType, extraStackFrames) + { + SecurityTokenDescriptor = new() + { + Issuer = Default.Issuer, + TokenType = tokenType, + }; + + ValidationParameters.TokenTypeValidator = tokenTypeValidationDelegate; + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ValidateTokenAsyncExtensibility.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ValidateTokenAsyncExtensibility.cs new file mode 100644 index 0000000000..240fd24e4a --- /dev/null +++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/Tests/ValidateTokenAsyncExtensibility.cs @@ -0,0 +1,75 @@ +// 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.Tokens; + +#nullable enable +namespace Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests +{ + public partial class ExtensibilityTesting + { + public static async Task ValidateTokenAsync_Extensibility(ExtensibilityTheoryData theoryData, object testInstance, string methodName) + { + var context = TestUtilities.WriteHeader($"{testInstance}.{methodName}", theoryData); + context.IgnoreType = false; + for (int i = 0; i < theoryData.ExtraStackFrames; i++) + theoryData.ValidationError!.AddStackFrame(new StackFrame(false)); + + SecurityToken securityToken = theoryData.TokenHandler.CreateToken(theoryData.SecurityTokenDescriptor!); + + try + { + ValidationResult validationResult = await theoryData.TokenHandler.ValidateTokenAsync( + securityToken, + theoryData.ValidationParameters!, + theoryData.CallContext, + CancellationToken.None); + + if (validationResult.IsValid) + { + context.AddDiff("validationResult.IsValid == true, expected false"); + } + else + { + ValidationError validationError = validationResult.UnwrapError(); + + if (validationError is SignatureValidationError signatureValidationError && + signatureValidationError.InnerValidationError is not null) + { + // Algorithm validation errors are wrapped in a signature validation error + // Other validation errors use the else branch. + IdentityComparer.AreValidationErrorsEqual( + signatureValidationError.InnerValidationError, + theoryData.ValidationError, + context); + } + else + { + IdentityComparer.AreValidationErrorsEqual( + validationError, + theoryData.ValidationError, + context); + } + + theoryData.ExpectedException.ProcessException(validationError.GetException(), context); + + // In the algorithm validation case, we want to ensure the inner exception contains + // the expected message and not just assert its type. + if (theoryData.ExpectedInnerException is not null) + theoryData.ExpectedInnerException.ProcessException(validationError.GetException().InnerException, context); + } + } + catch (Exception ex) + { + context.AddDiff($"ValidateTokenAsync threw an unexpected exception: {ex}."); + } + + TestUtilities.AssertFailIfErrors(context); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Comparison.ClaimsIdentity.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Comparison.ClaimsIdentity.cs new file mode 100644 index 0000000000..29ae44e4d2 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Comparison.ClaimsIdentity.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml2.Tests +{ + public partial class Saml2SecurityTokenHandlerTests + { + [Fact] + public async Task ValidateTokenAsync_ClaimsIdentity_Comparison() + { + await SamlClaimsIdentityComparisonTestBase.ValidateTokenAsync_ClaimsIdentity_Comparison( + this, + nameof(ValidateTokenAsync_ClaimsIdentity_Comparison), + "SAML2"); + } + } +} +#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..4a11fc47ac --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Algorithm.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml2.Extensibility.Tests +{ + public partial class Saml2SecurityTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateAlgorithmExtensibilityTestCases), + parameters: ["SAML2", 1], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_AlgorithmValidator_Extensibility( + AlgorithmExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_AlgorithmValidator_Extensibility)); + } + + public static TheoryData GenerateAlgorithmExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateAlgorithmExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "Saml2SecurityTokenHandler.ValidateSignature.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Audience.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Audience.cs new file mode 100644 index 0000000000..254667ea18 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Audience.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml2.Extensibility.Tests +{ + public partial class Saml2SecurityTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateAudienceExtensibilityTestCases), + parameters: ["SAML2", 2], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_AudienceValidator_Extensibility( + AudienceExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_AudienceValidator_Extensibility)); + } + + public static TheoryData GenerateAudienceExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateAudienceExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "Saml2SecurityTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Issuer.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Issuer.cs new file mode 100644 index 0000000000..e5ce7bffc0 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Issuer.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml2.Extensibility.Tests +{ + public partial class Saml2SecurityTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateIssuerExtensibilityTestCases), + parameters: ["SAML2", 1], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_IssuerValidator_Extensibility( + IssuerExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_IssuerValidator_Extensibility)); + } + + public static TheoryData GenerateIssuerExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateIssuerExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "Saml2SecurityTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.IssuerSigningKey.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.IssuerSigningKey.cs new file mode 100644 index 0000000000..9bc70cdbb8 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.IssuerSigningKey.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml2.Extensibility.Tests +{ + public partial class Saml2SecurityTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateIssuerSigningKeyExtensibilityTestCases), + parameters: ["SAML2", 1], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_IssuerSigningKeyValidator_Extensibility( + IssuerSigningKeyExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_IssuerSigningKeyValidator_Extensibility)); + } + + public static TheoryData GenerateIssuerSigningKeyExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateIssuerSigningKeyExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "Saml2SecurityTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Lifetime.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Lifetime.cs new file mode 100644 index 0000000000..09a905e2a5 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Lifetime.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml2.Extensibility.Tests +{ + public partial class Saml2SecurityTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateLifetimeExtensibilityTestCases), + parameters: ["SAML2", 2], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_LifetimeValidator_Extensibility( + LifetimeExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_LifetimeValidator_Extensibility)); + } + + public static TheoryData GenerateLifetimeExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateLifetimeExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "Saml2SecurityTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#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..7f39a236ca --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.Signature.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml2.Extensibility.Tests +{ + public partial class Saml2SecurityTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateSignatureExtensibilityTestCases), + parameters: ["SAML2", 2], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_SignatureValidator_Extensibility( + SignatureExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_SignatureValidator_Extensibility)); + } + + public static TheoryData GenerateSignatureExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateSignatureExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "Saml2SecurityTokenHandler.ValidateSignature.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.TokenReplay.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.TokenReplay.cs new file mode 100644 index 0000000000..8e801b04a9 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandler.Extensibility.TokenReplay.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml2.Extensibility.Tests +{ + public partial class Saml2SecurityTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateTokenReplayExtensibilityTestCases), + parameters: ["SAML2", 1], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_TokenReplayValidator_Extensibility( + TokenReplayExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_TokenReplayValidator_Extensibility)); + } + + public static TheoryData GenerateTokenReplayExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateTokenReplayExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "Saml2SecurityTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#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/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.TokenReplay.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.TokenReplay.cs new file mode 100644 index 0000000000..098dde0650 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.ValidateTokenAsyncTests.TokenReplay.cs @@ -0,0 +1,206 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +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_TokenReplay_TestCases), DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_TokenReplayComparison(ValidateTokenAsyncTokenReplayTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.ValidateTokenAsync_TokenReplayComparison", theoryData); + + Saml2SecurityTokenHandler saml2TokenHandler = new Saml2SecurityTokenHandler(); + + Saml2SecurityToken saml2Token = CreateTokenForTokenReplayValidation(theoryData.TokenHasExpiration); + + // 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 != theoryData.ExpectedIsValid) + context.AddDiff($"tokenValidationResult.IsValid != theoryData.ExpectedIsValid"); + + if (validationResult.IsValid != theoryData.ExpectedIsValid) + context.AddDiff($"validationResult.IsValid != theoryData.ExpectedIsValid"); + + if (!theoryData.ExpectedIsValid) + { + // Verify the exception provided by both paths match. + var tokenValidationResultException = tokenValidationResult.Exception; + var validationResultException = validationResult.UnwrapError().GetException(); + + theoryData.ExpectedException.ProcessException(tokenValidationResultException, context); + theoryData.ExpectedExceptionValidationParameters!.ProcessException(validationResultException, context); + } + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData ValidateTokenAsync_TokenReplay_TestCases + { + get + { + var successfulTokenReplayCache = new TokenReplayCache + { + OnAddReturnValue = true, + OnFindReturnValue = false, + }; + + var failToAddTokenReplayCache = new TokenReplayCache + { + OnAddReturnValue = false, + OnFindReturnValue = false, + }; + + var tokenAlreadySavedTokenReplayCache = new TokenReplayCache + { + OnAddReturnValue = true, + OnFindReturnValue = true, + }; + + var theoryData = new TheoryData(); + + theoryData.Add(new ValidateTokenAsyncTokenReplayTheoryData("Valid_TokenHasNotBeenReplayed") + { + TokenValidationParameters = CreateTokenValidationParameters(successfulTokenReplayCache), + ValidationParameters = CreateValidationParameters(successfulTokenReplayCache), + }); + + theoryData.Add(new ValidateTokenAsyncTokenReplayTheoryData("Valid_TokenHasNoExpiration_TokenReplayCacheIsNull") + { + TokenHasExpiration = false, + TokenValidationParameters = CreateTokenValidationParameters(null), + ValidationParameters = CreateValidationParameters(null), + }); + + theoryData.Add(new ValidateTokenAsyncTokenReplayTheoryData("Invalid_TokenHasNoExpiration_TokenReplayCacheIsNotNull") + { + TokenHasExpiration = false, + TokenValidationParameters = CreateTokenValidationParameters(successfulTokenReplayCache), + ValidationParameters = CreateValidationParameters(successfulTokenReplayCache), + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenNoExpirationException("IDX10227:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenNoExpirationException("IDX10227:"), + }); + + theoryData.Add(new ValidateTokenAsyncTokenReplayTheoryData("Invalid_TokenCouldNotBeAdded") + { + TokenValidationParameters = CreateTokenValidationParameters(failToAddTokenReplayCache), + ValidationParameters = CreateValidationParameters(failToAddTokenReplayCache), + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenReplayAddFailedException("IDX10229:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenReplayAddFailedException("IDX10229:"), + }); + + theoryData.Add(new ValidateTokenAsyncTokenReplayTheoryData("Invalid_TokenHasBeenReplayed") + { + TokenValidationParameters = CreateTokenValidationParameters(tokenAlreadySavedTokenReplayCache), + ValidationParameters = CreateValidationParameters(tokenAlreadySavedTokenReplayCache), + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenReplayDetectedException("IDX10228:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenReplayDetectedException("IDX10228:"), + }); + + return theoryData; + + static TokenValidationParameters CreateTokenValidationParameters(ITokenReplayCache? tokenReplayCache) + { + var tokenValidationParameters = new TokenValidationParameters + { + ValidateAudience = false, + ValidateIssuer = false, + ValidateLifetime = false, + ValidateTokenReplay = true, + ValidateIssuerSigningKey = false, + RequireSignedTokens = false, + TokenReplayCache = tokenReplayCache + }; + + return tokenValidationParameters; + } + + static ValidationParameters CreateValidationParameters(ITokenReplayCache? tokenReplayCache) + { + ValidationParameters validationParameters = new ValidationParameters(); + validationParameters.TokenReplayCache = tokenReplayCache; + + validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation; + validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; + validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation; + validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation; + validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation; + validationParameters.SignatureValidator = SkipValidationDelegates.SkipSignatureValidation; + validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation; + + return validationParameters; + } + } + } + + public class ValidateTokenAsyncTokenReplayTheoryData : TheoryDataBase + { + public ValidateTokenAsyncTokenReplayTheoryData(string testId) : base(testId) { } + + internal ExpectedException? ExpectedExceptionValidationParameters { get; set; } = ExpectedException.NoExceptionExpected; + + internal bool TokenHasExpiration { get; set; } = true; + + internal bool ExpectedIsValid { get; set; } = true; + + internal ValidationParameters? ValidationParameters { get; set; } + + internal TokenValidationParameters? TokenValidationParameters { get; set; } + } + + private static Saml2SecurityToken CreateTokenForTokenReplayValidation(bool hasExpiration = true) + { + Saml2SecurityTokenHandler saml2SecurityTokenHandler = new Saml2SecurityTokenHandler(); + // If the token has expiration, we use the default times. + saml2SecurityTokenHandler.SetDefaultTimesOnTokenCreation = hasExpiration; + + SecurityTokenDescriptor securityTokenDescriptor; + + if (!hasExpiration) + { + securityTokenDescriptor = new SecurityTokenDescriptor + { + Subject = Default.ClaimsIdentity, + Issuer = Default.Issuer, + Audience = Default.Audience, + Expires = null, + NotBefore = null, + IssuedAt = null, + }; + } + else + { + securityTokenDescriptor = new SecurityTokenDescriptor + { + Subject = Default.ClaimsIdentity, + Issuer = Default.Issuer, + Audience = Default.Audience, + }; + } + + return (Saml2SecurityToken)saml2SecurityTokenHandler.CreateToken(securityTokenDescriptor); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Comparison.ClaimsIdentity.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Comparison.ClaimsIdentity.cs new file mode 100644 index 0000000000..72cd31d127 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Comparison.ClaimsIdentity.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml.Tests +{ + public partial class SamlSecurityTokenHandlerTests + { + [Fact] + public async Task ValidateTokenAsync_ClaimsIdentity_Comparison() + { + await SamlClaimsIdentityComparisonTestBase.ValidateTokenAsync_ClaimsIdentity_Comparison( + this, + nameof(ValidateTokenAsync_ClaimsIdentity_Comparison), + "SAML"); + } + } +} +#nullable restore 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..bdf117bece --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Algorithm.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml.Extensibility.Tests +{ + public partial class SamlSecurityTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateAlgorithmExtensibilityTestCases), + parameters: ["SAML", 1], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_AlgorithmValidator_Extensibility( + AlgorithmExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_AlgorithmValidator_Extensibility)); + } + + public static TheoryData GenerateAlgorithmExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateAlgorithmExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "SamlSecurityTokenHandler.ValidateSignature.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Audience.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Audience.cs new file mode 100644 index 0000000000..9e6285389f --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Audience.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml.Extensibility.Tests +{ + public partial class SamlSecurityTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateAudienceExtensibilityTestCases), + parameters: ["SAML", 2], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_AudienceValidator_Extensibility( + AudienceExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_AudienceValidator_Extensibility)); + } + + public static TheoryData GenerateAudienceExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateAudienceExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "SamlSecurityTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Issuer.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Issuer.cs new file mode 100644 index 0000000000..b0c95b0952 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Issuer.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml.Extensibility.Tests +{ + public partial class SamlSecurityTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateIssuerExtensibilityTestCases), + parameters: ["SAML", 1], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_IssuerValidator_Extensibility( + IssuerExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_IssuerValidator_Extensibility)); + } + + public static TheoryData GenerateIssuerExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateIssuerExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "SamlSecurityTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.IssuerSigningKey.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.IssuerSigningKey.cs new file mode 100644 index 0000000000..2c0bf99497 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.IssuerSigningKey.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml.Extensibility.Tests +{ + public partial class SamlSecurityTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateIssuerSigningKeyExtensibilityTestCases), + parameters: ["SAML", 1], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_IssuerSigningKeyValidator_Extensibility( + IssuerSigningKeyExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_IssuerSigningKeyValidator_Extensibility)); + } + + public static TheoryData GenerateIssuerSigningKeyExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateIssuerSigningKeyExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "SamlSecurityTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Lifetime.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Lifetime.cs new file mode 100644 index 0000000000..8dbe848283 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Lifetime.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml.Extensibility.Tests +{ + public partial class SamlSecurityTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateLifetimeExtensibilityTestCases), + parameters: ["SAML", 2], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_LifetimeValidator_Extensibility( + LifetimeExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_LifetimeValidator_Extensibility)); + } + + public static TheoryData GenerateLifetimeExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateLifetimeExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "SamlSecurityTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#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..85f57bb942 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.Signature.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml.Extensibility.Tests +{ + public partial class SamlSecurityTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateSignatureExtensibilityTestCases), + parameters: ["SAML", 2], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_SignatureValidator_Extensibility( + SignatureExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_SignatureValidator_Extensibility)); + } + + public static TheoryData GenerateSignatureExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateSignatureExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "SamlSecurityTokenHandler.ValidateSignature.cs"); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.TokenReplay.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.TokenReplay.cs new file mode 100644 index 0000000000..ca52b2eb49 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandler.Extensibility.TokenReplay.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils.TokenValidationExtensibility.Tests; +using Xunit; + +#nullable enable +namespace Microsoft.IdentityModel.Tokens.Saml.Extensibility.Tests +{ + public partial class SamlSecurityTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData( + nameof(GenerateTokenReplayExtensibilityTestCases), + parameters: ["SAML", 1], + DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_TokenReplayValidator_Extensibility( + TokenReplayExtensibilityTheoryData theoryData) + { + await ExtensibilityTesting.ValidateTokenAsync_Extensibility( + theoryData, + this, + nameof(ValidateTokenAsync_TokenReplayValidator_Extensibility)); + } + + public static TheoryData GenerateTokenReplayExtensibilityTestCases( + string tokenHandlerType, + int extraStackFrames) + { + return ExtensibilityTesting.GenerateTokenReplayExtensibilityTestCases( + tokenHandlerType, + extraStackFrames, + "SamlSecurityTokenHandler.ValidateToken.Internal.cs"); + } + } +} +#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; diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.ValidateTokenAsyncTests.TokenReplay.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.ValidateTokenAsyncTests.TokenReplay.cs new file mode 100644 index 0000000000..0ae2a2ed76 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.ValidateTokenAsyncTests.TokenReplay.cs @@ -0,0 +1,188 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils; +using Xunit; + +namespace Microsoft.IdentityModel.Tokens.Saml.Tests +{ +#nullable enable + public partial class SamlSecurityTokenHandlerTests + { + [Theory, MemberData(nameof(ValidateTokenAsync_TokenReplay_TestCases), DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_TokenReplayComparison(ValidateTokenAsyncTokenReplayTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.ValidateTokenAsync_TokenReplayComparison", theoryData); + + SamlSecurityTokenHandler samlTokenHandler = new SamlSecurityTokenHandler(); + + SamlSecurityToken samlToken = CreateTokenForTokenReplayValidation(theoryData.TokenHasExpiration); + + // 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 != theoryData.ExpectedIsValid) + context.AddDiff($"tokenValidationResult.IsValid != theoryData.ExpectedIsValid"); + + if (validationResult.IsValid != theoryData.ExpectedIsValid) + context.AddDiff($"validationResult.IsValid != theoryData.ExpectedIsValid"); + + if (!theoryData.ExpectedIsValid) + { + // Verify the exception provided by both paths match. + var tokenValidationResultException = tokenValidationResult.Exception; + var validationResultException = validationResult.UnwrapError().GetException(); + + theoryData.ExpectedException.ProcessException(tokenValidationResultException, context); + theoryData.ExpectedExceptionValidationParameters!.ProcessException(validationResultException, context); + } + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData ValidateTokenAsync_TokenReplay_TestCases + { + get + { + var successfulTokenReplayCache = new TokenReplayCache + { + OnAddReturnValue = true, + OnFindReturnValue = false, + }; + + var failToAddTokenReplayCache = new TokenReplayCache + { + OnAddReturnValue = false, + OnFindReturnValue = false, + }; + + var tokenAlreadySavedTokenReplayCache = new TokenReplayCache + { + OnAddReturnValue = true, + OnFindReturnValue = true, + }; + + var theoryData = new TheoryData(); + + theoryData.Add(new ValidateTokenAsyncTokenReplayTheoryData("Valid_TokenHasNotBeenReplayed") + { + TokenValidationParameters = CreateTokenValidationParameters(successfulTokenReplayCache), + ValidationParameters = CreateValidationParameters(successfulTokenReplayCache), + }); + + theoryData.Add(new ValidateTokenAsyncTokenReplayTheoryData("Invalid_TokenCouldNotBeAdded") + { + TokenValidationParameters = CreateTokenValidationParameters(failToAddTokenReplayCache), + ValidationParameters = CreateValidationParameters(failToAddTokenReplayCache), + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenReplayAddFailedException("IDX10229:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenReplayAddFailedException("IDX10229:"), + }); + + theoryData.Add(new ValidateTokenAsyncTokenReplayTheoryData("Invalid_TokenHasBeenReplayed") + { + TokenValidationParameters = CreateTokenValidationParameters(tokenAlreadySavedTokenReplayCache), + ValidationParameters = CreateValidationParameters(tokenAlreadySavedTokenReplayCache), + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenReplayDetectedException("IDX10228:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenReplayDetectedException("IDX10228:"), + }); + + return theoryData; + + static TokenValidationParameters CreateTokenValidationParameters(ITokenReplayCache? tokenReplayCache) + { + var tokenValidationParameters = new TokenValidationParameters + { + ValidateAudience = false, + ValidateIssuer = false, + ValidateLifetime = false, + ValidateTokenReplay = true, + ValidateIssuerSigningKey = false, + RequireSignedTokens = false, + TokenReplayCache = tokenReplayCache + }; + + return tokenValidationParameters; + } + + static ValidationParameters CreateValidationParameters(ITokenReplayCache? tokenReplayCache) + { + ValidationParameters validationParameters = new ValidationParameters(); + validationParameters.TokenReplayCache = tokenReplayCache; + + validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation; + validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; + validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation; + validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation; + validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation; + validationParameters.SignatureValidator = SkipValidationDelegates.SkipSignatureValidation; + validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation; + + return validationParameters; + } + } + } + + public class ValidateTokenAsyncTokenReplayTheoryData : TheoryDataBase + { + public ValidateTokenAsyncTokenReplayTheoryData(string testId) : base(testId) { } + + internal ExpectedException? ExpectedExceptionValidationParameters { get; set; } = ExpectedException.NoExceptionExpected; + + internal bool TokenHasExpiration { get; set; } = true; + + internal bool ExpectedIsValid { get; set; } = true; + + internal ValidationParameters? ValidationParameters { get; set; } + + internal TokenValidationParameters? TokenValidationParameters { get; set; } + } + + private static SamlSecurityToken CreateTokenForTokenReplayValidation(bool hasExpiration = true) + { + SamlSecurityTokenHandler samlSecurityTokenHandler = new SamlSecurityTokenHandler(); + // If the token has expiration, we use the default times. + samlSecurityTokenHandler.SetDefaultTimesOnTokenCreation = hasExpiration; + + SecurityTokenDescriptor securityTokenDescriptor; + + if (!hasExpiration) + { + securityTokenDescriptor = new SecurityTokenDescriptor + { + Subject = Default.SamlClaimsIdentity, + Issuer = Default.Issuer, + Audience = Default.Audience, + Expires = null, + NotBefore = null, + IssuedAt = null, + }; + } + else + { + securityTokenDescriptor = new SecurityTokenDescriptor + { + Subject = Default.SamlClaimsIdentity, + Issuer = Default.Issuer, + Audience = Default.Audience, + }; + } + + return (SamlSecurityToken)samlSecurityTokenHandler.CreateToken(securityTokenDescriptor); + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.Validators.Tests/AadIssuerValidatorTests.cs b/test/Microsoft.IdentityModel.Validators.Tests/AadIssuerValidatorTests.cs index 10d4575a33..0fa85ed1ce 100644 --- a/test/Microsoft.IdentityModel.Validators.Tests/AadIssuerValidatorTests.cs +++ b/test/Microsoft.IdentityModel.Validators.Tests/AadIssuerValidatorTests.cs @@ -26,6 +26,13 @@ public static TheoryData AadIssuerValidationTestCa var theoryData = new TheoryData { // Success cases + new AadIssuerValidatorTheoryData("V1_TemplateWithoutTrailingSlash_Matches_V1_IssuerWithoutTrailingSlash_Success") + { + TemplatedIssuer = ValidatorConstants.AadIssuerV1CommonAuthorityWithoutTrailingSlash, + TokenIssuer = ValidatorConstants.V1IssuerWithoutTrailingSlash, + TenantIdClaim = ValidatorConstants.TenantIdAsGuid, + ExpectedResult = true, + }, new AadIssuerValidatorTheoryData("V1_Template_Matches_V1_Issuer_Success") { TemplatedIssuer = ValidatorConstants.AadIssuerV1CommonAuthority, @@ -106,7 +113,7 @@ public static TheoryData AadIssuerValidationTestCa public class AadIssuerValidatorTheoryData : TheoryDataBase { - public AadIssuerValidatorTheoryData() {} + public AadIssuerValidatorTheoryData() { } public AadIssuerValidatorTheoryData(string testId) : base(testId) { } diff --git a/test/Microsoft.IdentityModel.Validators.Tests/ValidatorConstants.cs b/test/Microsoft.IdentityModel.Validators.Tests/ValidatorConstants.cs index 920b6c628f..4cca5d8e88 100644 --- a/test/Microsoft.IdentityModel.Validators.Tests/ValidatorConstants.cs +++ b/test/Microsoft.IdentityModel.Validators.Tests/ValidatorConstants.cs @@ -40,8 +40,10 @@ internal class ValidatorConstants public const string UsGovIssuer = "https://login.microsoftonline.us/" + UsGovTenantId + "/v2.0"; public const string UsGovTenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47"; public const string V1Issuer = "https://sts.windows.net/f645ad92-e38d-4d1a-b510-d1b09a74a8ca/"; + public const string V1IssuerWithoutTrailingSlash = "https://sts.windows.net/f645ad92-e38d-4d1a-b510-d1b09a74a8ca"; public const string V1IssuerPPE = "https://sts.windows-ppe.net/f645ad92-e38d-4d1a-b510-d1b09a74a8ca/"; public const string AadIssuerV1CommonAuthority = "https://sts.windows.net/{tenantid}/"; + public const string AadIssuerV1CommonAuthorityWithoutTrailingSlash = "https://sts.windows.net/{tenantid}"; public const string AadIssuerV11CommonAuthority = AadInstance + "/{tenantid}/v1.1"; public const string AadIssuerV2CommonAuthority = AadInstance + "/{tenantid}/v2.0";