diff --git a/.github/workflows/aot-check.yml b/.github/workflows/aot-check.yml
index 911da7f712..a9d20a0eba 100644
--- a/.github/workflows/aot-check.yml
+++ b/.github/workflows/aot-check.yml
@@ -23,7 +23,7 @@ jobs:
fetch-depth: 1
- name: Setup .NET 9.0.x
- uses: actions/setup-dotnet@v4.1.0
+ uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 85b9755976..345bf87488 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -26,6 +26,11 @@ jobs:
with:
fetch-depth: 2
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 9.0.x
+
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml
index cfef45dc72..8807853e1c 100644
--- a/.github/workflows/dotnetcore.yml
+++ b/.github/workflows/dotnetcore.yml
@@ -24,19 +24,24 @@ 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
- name: Setup .NET 9.x
- uses: actions/setup-dotnet@v4.1.0
+ uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.x
- 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
@@ -44,7 +49,7 @@ jobs:
- name: Create code coverage report
run: |
dotnet tool install -g dotnet-reportgenerator-globaltool --version 5.4.1
- reportgenerator -reports:./**/coverage.cobertura.xml -targetdir:CodeCoverage -reporttypes:'MarkdownSummaryGithub;Cobertura'
+ reportgenerator -reports:./**/coverage.cobertura.xml -targetdir:CodeCoverage -reporttypes:'MarkdownSummaryGithub;Cobertura' -filefilters:'+src/**/*.cs'
- name: Write coverage to job summary
shell: bash
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8a7f619240..b994ce1945 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,55 @@
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
+- Update to use .NET 9 GA. See [2990](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/2990).
+
+### Bug fixes
+- Remove dependency on Microsoft.Bcl.TimeProvider for .NET 8+ targets. See [2935](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/2935).
+- Update cgmanifest to align with the JSON schema. See [2969](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/2969).
+
+### Fundamentals
+- Streamline token creation by using `SecurityTokenDescriptor`. See [2993](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2993).
+- Prevent inlining to guarantee stack frames in test. See [2999](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2999).
+
+### Work related to redesign of IdentityModel's token validation logic [#2711](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2711)
+- Simplify stack frame caching. See [2976](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/2976).
+- Implement new model for reading SAML and SAML2 tokens. See [2980](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/2980).
+- Implement new model for validating SAML signature. See [2950](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2950).
+- Add tests for `IssuerExtensibility`. See [2987](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/2987).
+- Switch to new validation model for SAML and SAML2 issuer signing key. See [2965](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/2965).
+- Switch to new validation model for SAML and SAML2 algorithm. See [2984](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/2984).
+
8.2.0
=====
### Fundamentals
diff --git a/PerfAndStress.slnf b/PerfAndStress.slnf
new file mode 100644
index 0000000000..ccca0c04ce
--- /dev/null
+++ b/PerfAndStress.slnf
@@ -0,0 +1,20 @@
+{
+ "solution": {
+ "path": "Wilson.sln",
+ "projects": [
+ "src\\Microsoft.IdentityModel.Protocols\\Microsoft.IdentityModel.Protocols.csproj",
+ "src\\Microsoft.IdentityModel.Tokens\\Microsoft.IdentityModel.Tokens.csproj",
+ "src\\System.IdentityModel.Tokens.Jwt\\System.IdentityModel.Tokens.Jwt.csproj",
+ "src\\Microsoft.IdentityModel.Protocols.WsFederation\\Microsoft.IdentityModel.Protocols.WsFederation.csproj",
+ "src\\Microsoft.IdentityModel.Protocols.OpenIdConnect\\Microsoft.IdentityModel.Protocols.OpenIdConnect.csproj",
+ "src\\Microsoft.IdentityModel.Tokens.Saml\\Microsoft.IdentityModel.Tokens.Saml.csproj",
+ "src\\Microsoft.IdentityModel.Xml\\Microsoft.IdentityModel.Xml.csproj",
+ "src\\Microsoft.IdentityModel.Logging\\Microsoft.IdentityModel.Logging.csproj",
+ "src\\Microsoft.IdentityModel.JsonWebTokens\\Microsoft.IdentityModel.JsonWebTokens.csproj",
+ "src\\Microsoft.IdentityModel.Protocols.SignedHttpRequest\\Microsoft.IdentityModel.Protocols.SignedHttpRequest.csproj",
+ "src\\Microsoft.IdentityModel.Validators\\Microsoft.IdentityModel.Validators.csproj",
+ "src\\Microsoft.IdentityModel.Abstractions\\Microsoft.IdentityModel.Abstractions.csproj",
+ "src\\Microsoft.IdentityModel.LoggingExtensions\\Microsoft.IdentityModel.LoggingExtensions.csproj"
+ ]
+ }
+}
\ No newline at end of file
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 3acadea8e2..d09d31ffcb 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -13,4 +13,29 @@
8.0.5
+
+
+ 9.0.0
+
+
+
+ 8.0.0
+
+
+
+ 7.0.0
+
+
+
+ 6.0.0
+
+
+
+ 6.0.0
+
+
+
+ 2.1.0
+
+
diff --git a/build/template-Build-run-tests-sign.yml b/build/template-Build-run-tests-sign.yml
index 4c79d9baa9..f8d7047729 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
@@ -209,6 +212,13 @@ steps:
PathtoPublish: '$(Build.SourcesDirectory)\artifacts'
ArtifactName: '$(Build.BuildNumber)-nuget-package'
+- task: BuildQualityChecks@9
+ displayName: 'Check Warnings'
+ inputs:
+ checkWarnings: true
+ warningFailOption: 'build'
+ showStatistics: true
+
- task: mspremier.PostBuildCleanup.PostBuildCleanup-task.PostBuildCleanup@3
displayName: 'Clean Agent Directories'
condition: and(succeeded(), eq(variables['PipelineType'], 'legacy'))
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/JsonWebToken.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs
index 48af759d86..985836f1c4 100644
--- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs
+++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs
@@ -19,9 +19,6 @@ public partial class JsonWebToken : SecurityToken
{
internal const string ClassName = "Microsoft.IdentityModel.JsonWebTokens.JsonWebToken";
- private ClaimsIdentity _claimsIdentity;
- private bool _wasClaimsIdentitySet;
-
private string _act;
private string _authenticationTag;
private string _ciphertext;
@@ -629,61 +626,6 @@ public Claim GetClaim(string key)
///
internal IReadOnlyCollection PayloadClaimNames => Payload._jsonClaims.Keys;
- internal ClaimsIdentity ClaimsIdentity
- {
- get
- {
- if (!_wasClaimsIdentitySet)
- {
- _wasClaimsIdentitySet = true;
- string actualIssuer = ActualIssuer ?? Issuer;
-
- foreach (Claim claim in Claims)
- {
- string claimType = claim.Type;
- if (claimType == ClaimTypes.Actor)
- {
- if (_claimsIdentity.Actor != null)
- throw LogHelper.LogExceptionMessage(new InvalidOperationException(LogHelper.FormatInvariant(LogMessages.IDX14112, LogHelper.MarkAsNonPII(JwtRegisteredClaimNames.Actort), claim.Value)));
-
-#pragma warning disable CA1031 // Do not catch general exception types
- try
- {
- JsonWebToken actorToken = new JsonWebToken(claim.Value);
- _claimsIdentity.Actor = ActorClaimsIdentity;
- }
- catch
- {
-
- }
-#pragma warning restore CA1031 // Do not catch general exception types
- }
-
- if (claim.Properties.Count == 0)
- {
- _claimsIdentity.AddClaim(new Claim(claimType, claim.Value, claim.ValueType, actualIssuer, actualIssuer, _claimsIdentity));
- }
- else
- {
- Claim newClaim = new Claim(claimType, claim.Value, claim.ValueType, actualIssuer, actualIssuer, _claimsIdentity);
-
- foreach (var kv in claim.Properties)
- newClaim.Properties[kv.Key] = kv.Value;
-
- _claimsIdentity.AddClaim(newClaim);
- }
- }
- }
-
- return _claimsIdentity;
- }
-
- set
- {
- _claimsIdentity = value;
- }
- }
-
///
/// Try to get a representing the { key, 'value' } pair corresponding to the provided .
/// The value is obtained from the Payload.
diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateSignature.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateSignature.cs
index 1340c61781..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,60 +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),
- 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,
@@ -291,7 +316,7 @@ private static ValidationResult ValidateSignatureWithKey(
if (valid)
return key;
else
- return new ValidationError(
+ return new SignatureValidationError(
new MessageDetail(
TokenLogMessages.IDX10504,
LogHelper.MarkAsSecurityArtifact(
@@ -299,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(
@@ -313,8 +338,8 @@ private static ValidationResult ValidateSignatureWithKey(
JwtTokenUtilities.SafeLogJwtToken)),
ValidationFailureType.SignatureValidationFailed,
typeof(SecurityTokenInvalidSignatureException),
- new StackFrame(true),
- ex);
+ ValidationError.GetCurrentStackFrame(),
+ innerException: ex);
}
finally
{
@@ -322,7 +347,7 @@ private static ValidationResult ValidateSignatureWithKey(
}
}
- private static ValidationError GetSignatureValidationError(
+ private static SignatureValidationError GetSignatureValidationError(
JsonWebToken jwtToken,
ValidationParameters validationParameters,
BaseConfiguration? configuration,
@@ -342,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()),
@@ -354,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),
@@ -369,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()),
@@ -381,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 cd06188f0f..1f38022f33 100644
--- a/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs
+++ b/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs
@@ -235,7 +235,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 1fc631663c..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,20 +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 failureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame) -> void
-Microsoft.IdentityModel.Tokens.Saml.SamlValidationError.SamlValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType failureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Exception innerException) -> void
+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 failureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame) -> void
-Microsoft.IdentityModel.Tokens.Saml2.Saml2ValidationError.Saml2ValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType failureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Exception innerException) -> void
+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
@@ -47,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/Exceptions/SamlValidationError.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Exceptions/SamlValidationError.cs
index 48aff2a17d..d7f39f8cc4 100644
--- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Exceptions/SamlValidationError.cs
+++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/Exceptions/SamlValidationError.cs
@@ -4,11 +4,18 @@
using System;
using System.Diagnostics;
+#nullable enable
namespace Microsoft.IdentityModel.Tokens.Saml
{
internal class SamlValidationError : ValidationError
{
- internal SamlValidationError(MessageDetail messageDetail, ValidationFailureType failureType, Type exceptionType, StackFrame stackFrame, Exception innerException) : base(messageDetail, failureType, exceptionType, stackFrame, innerException)
+ internal SamlValidationError(
+ MessageDetail messageDetail,
+ ValidationFailureType validationFailureType,
+ Type exceptionType,
+ StackFrame stackFrame,
+ Exception? innerException = null)
+ : base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException)
{
}
@@ -24,3 +31,4 @@ internal override Exception GetException()
}
}
}
+#nullable restore
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/Exceptions/Saml2ValidationError.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Exceptions/Saml2ValidationError.cs
index b8f5803e23..7425f50679 100644
--- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Exceptions/Saml2ValidationError.cs
+++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Exceptions/Saml2ValidationError.cs
@@ -4,11 +4,18 @@
using System;
using System.Diagnostics;
+#nullable enable
namespace Microsoft.IdentityModel.Tokens.Saml2
{
internal class Saml2ValidationError : ValidationError
{
- internal Saml2ValidationError(MessageDetail messageDetail, ValidationFailureType failureType, Type exceptionType, StackFrame stackFrame, Exception innerException) : base(messageDetail, failureType, exceptionType, stackFrame, innerException)
+ internal Saml2ValidationError(
+ MessageDetail messageDetail,
+ ValidationFailureType validationFailureType,
+ Type exceptionType,
+ StackFrame stackFrame,
+ Exception? innerException = null)
+ : base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException)
{
}
@@ -24,3 +31,4 @@ internal override Exception GetException()
}
}
}
+#nullable restore
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 fa2f518ab7..6e9eef5fa4 100644
--- a/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt
+++ b/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt
@@ -1,44 +1,85 @@
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.LogMessages.IDX10277 = "IDX10277: RequireAudience property on ValidationParameters is set to false. Exiting without validating the audience." -> string
Microsoft.IdentityModel.Tokens.AlgorithmValidationError
-Microsoft.IdentityModel.Tokens.AlgorithmValidationError.AlgorithmValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidAlgorithm) -> void
+Microsoft.IdentityModel.Tokens.AlgorithmValidationError.AlgorithmValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidAlgorithm, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.AlgorithmValidationError.InvalidAlgorithm.get -> string
Microsoft.IdentityModel.Tokens.AlgorithmValidationError._invalidAlgorithm -> string
-Microsoft.IdentityModel.Tokens.AudienceValidationError.AudienceValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType failureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Collections.Generic.IList tokenAudiences, System.Collections.Generic.IList validAudiences) -> void
+Microsoft.IdentityModel.Tokens.AudienceValidationError.AudienceValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Collections.Generic.IList tokenAudiences, System.Collections.Generic.IList validAudiences, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.AudienceValidationError.TokenAudiences.get -> System.Collections.Generic.IList
+Microsoft.IdentityModel.Tokens.AudienceValidationError.TokenAudiences.set -> void
+Microsoft.IdentityModel.Tokens.AudienceValidationError.ValidAudiences.get -> System.Collections.Generic.IList
+Microsoft.IdentityModel.Tokens.AudienceValidationError.ValidAudiences.set -> void
+Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError
+Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError.InvalidSigningKey.get -> Microsoft.IdentityModel.Tokens.SecurityKey
+Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError.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) -> void
-Microsoft.IdentityModel.Tokens.IssuerValidationError.IssuerValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidIssuer, System.Exception innerException) -> void
+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
Microsoft.IdentityModel.Tokens.IssuerValidationSource.IssuerMatchedValidationParameters = 2 -> Microsoft.IdentityModel.Tokens.IssuerValidationSource
-Microsoft.IdentityModel.Tokens.LifetimeValidationError._expires -> System.DateTime
-Microsoft.IdentityModel.Tokens.LifetimeValidationError._notBefore -> System.DateTime
+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.TokenTypeValidationError
-Microsoft.IdentityModel.Tokens.TokenTypeValidationError.TokenTypeValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidTokenType) -> void
-Microsoft.IdentityModel.Tokens.TokenTypeValidationError._invalidTokenType -> string
+Microsoft.IdentityModel.Tokens.TokenTypeValidationError.InvalidTokenType.get -> string
+Microsoft.IdentityModel.Tokens.TokenTypeValidationError.TokenTypeValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidTokenType, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.TokenValidationParameters.TimeProvider.get -> System.TimeProvider
Microsoft.IdentityModel.Tokens.TokenValidationParameters.TimeProvider.set -> void
+Microsoft.IdentityModel.Tokens.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.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.AudienceValidationError.AudiencesCountZero -> System.Diagnostics.StackFrame
-static Microsoft.IdentityModel.Tokens.AudienceValidationError.AudiencesNull -> System.Diagnostics.StackFrame
-static Microsoft.IdentityModel.Tokens.AudienceValidationError.ValidateAudienceFailed -> System.Diagnostics.StackFrame
-static Microsoft.IdentityModel.Tokens.AudienceValidationError.ValidationParametersAudiencesCountZero -> System.Diagnostics.StackFrame
-static Microsoft.IdentityModel.Tokens.AudienceValidationError.ValidationParametersNull -> System.Diagnostics.StackFrame
+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.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.ValidationFailureType.IssuerValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType
+static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.LifetimeValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.NoTokenAudiencesProvided -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.NoValidationParameterAudiencesProvided -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.SignatureAlgorithmValidationFailed -> Microsoft.IdentityModel.Tokens.ValidationFailureType
+static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.SignatureValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.TokenExceedsMaximumSize -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.TokenIsNotSigned -> Microsoft.IdentityModel.Tokens.ValidationFailureType
+static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.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..a07f46494d 100644
--- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs
+++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs
@@ -88,7 +88,14 @@ 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.";
+ public const string IDX10277 = "IDX10277: RequireAudience property on ValidationParameters is set to false. Exiting without validating the audience.";
// 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/TokenValidationParameters.cs b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs
index 7ea251d8dc..5ba076d00b 100644
--- a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs
+++ b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs
@@ -459,9 +459,13 @@ public string NameClaimType
public bool RefreshBeforeValidation { get; set; }
///
- /// Gets or sets a value indicating whether SAML tokens must have at least one AudienceRestriction.
+ /// Gets or sets a value indicating whether SAML or JWT tokens must have at least one AudienceRestriction.
/// The default is true.
///
+ ///
+ /// If set to false and the Audience is null, Audience validation will be skipped.
+ /// If set to false and the Audience is not null, the Audience will still be validated.
+ ///
[DefaultValue(true)]
public bool RequireAudience { get; set; }
diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AlgorithmValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AlgorithmValidationError.cs
index c867fdb193..de0dd174d5 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AlgorithmValidationError.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AlgorithmValidationError.cs
@@ -9,16 +9,16 @@ namespace Microsoft.IdentityModel.Tokens
{
internal class AlgorithmValidationError : ValidationError
{
- protected string? _invalidAlgorithm;
-
public AlgorithmValidationError(
MessageDetail messageDetail,
+ ValidationFailureType validationFailureType,
Type exceptionType,
StackFrame stackFrame,
- string? invalidAlgorithm) :
- base(messageDetail, ValidationFailureType.AlgorithmValidationFailed, exceptionType, stackFrame)
+ string? invalidAlgorithm,
+ Exception? innerException = null) :
+ base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException)
{
- _invalidAlgorithm = invalidAlgorithm;
+ InvalidAlgorithm = invalidAlgorithm;
}
internal override Exception GetException()
@@ -27,8 +27,9 @@ internal override Exception GetException()
{
SecurityTokenInvalidAlgorithmException exception = new(MessageDetail.Message, InnerException)
{
- InvalidAlgorithm = _invalidAlgorithm
+ InvalidAlgorithm = InvalidAlgorithm
};
+ exception.SetValidationError(this);
return exception;
}
@@ -36,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/AudienceValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AudienceValidationError.cs
index c38b3a7bb2..4c6dbd42c3 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AudienceValidationError.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/AudienceValidationError.cs
@@ -10,27 +10,18 @@ namespace Microsoft.IdentityModel.Tokens
{
internal class AudienceValidationError : ValidationError
{
- private IList? _tokenAudiences;
- private IList? _validAudiences;
-
- // stack frames associated with AudienceValidationErrors
- internal static StackFrame? ValidationParametersNull;
- internal static StackFrame? AudiencesNull;
- internal static StackFrame? AudiencesCountZero;
- internal static StackFrame? ValidationParametersAudiencesCountZero;
- internal static StackFrame? ValidateAudienceFailed;
-
public AudienceValidationError(
MessageDetail messageDetail,
- ValidationFailureType failureType,
+ ValidationFailureType validationFailureType,
Type exceptionType,
StackFrame stackFrame,
IList? tokenAudiences,
- IList? validAudiences)
- : base(messageDetail, failureType, exceptionType, stackFrame)
+ IList? validAudiences,
+ Exception? innerException = null)
+ : base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException)
{
- _tokenAudiences = tokenAudiences;
- _validAudiences = validAudiences;
+ TokenAudiences = tokenAudiences;
+ ValidAudiences = validAudiences;
}
///
@@ -40,12 +31,18 @@ public AudienceValidationError(
internal override Exception GetException()
{
if (ExceptionType == typeof(SecurityTokenInvalidAudienceException))
- return new SecurityTokenInvalidAudienceException(MessageDetail.Message) { InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(_tokenAudiences) };
+ {
+ var exception = new SecurityTokenInvalidAudienceException(MessageDetail.Message, InnerException) { InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(TokenAudiences) };
+ exception.SetValidationError(this);
+
+ return exception;
+ }
return base.GetException(ExceptionType, null);
}
- internal IList? TokenAudiences => _tokenAudiences;
+ protected IList? TokenAudiences { get; }
+ protected IList? ValidAudiences { 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/IssuerValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/IssuerValidationError.cs
index cc26ebda9a..438681b973 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/IssuerValidationError.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/IssuerValidationError.cs
@@ -9,22 +9,13 @@ namespace Microsoft.IdentityModel.Tokens
{
internal class IssuerValidationError : ValidationError
{
- internal IssuerValidationError(
- MessageDetail messageDetail,
- Type exceptionType,
- StackFrame stackFrame,
- string? invalidIssuer)
- : this(messageDetail, ValidationFailureType.IssuerValidationFailed, exceptionType, stackFrame, invalidIssuer, null)
- {
- }
-
internal IssuerValidationError(
MessageDetail messageDetail,
ValidationFailureType validationFailureType,
Type exceptionType,
StackFrame stackFrame,
string? invalidIssuer,
- Exception? innerException)
+ Exception? innerException = null)
: base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException)
{
InvalidIssuer = invalidIssuer;
@@ -40,6 +31,7 @@ internal override Exception GetException()
{
InvalidIssuer = InvalidIssuer
};
+ exception.SetValidationError(this);
return exception;
}
diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/LifetimeValidationError.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/LifetimeValidationError.cs
index 215296439f..f193985d81 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/LifetimeValidationError.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/LifetimeValidationError.cs
@@ -9,37 +9,19 @@ namespace Microsoft.IdentityModel.Tokens
{
internal class LifetimeValidationError : ValidationError
{
- protected DateTime _notBefore;
- protected DateTime _expires;
-
- public LifetimeValidationError(
- MessageDetail messageDetail,
- Type exceptionType,
- StackFrame stackFrame)
- : base(messageDetail, ValidationFailureType.LifetimeValidationFailed, exceptionType, stackFrame)
- {
- }
-
public LifetimeValidationError(
MessageDetail messageDetail,
+ ValidationFailureType validationFailureType,
Type exceptionType,
StackFrame stackFrame,
- DateTime notBefore,
- DateTime expires)
- : base(messageDetail, ValidationFailureType.LifetimeValidationFailed, exceptionType, stackFrame)
- {
- _notBefore = notBefore;
- _expires = expires;
- }
+ DateTime? notBefore,
+ DateTime? expires,
+ Exception? innerException = null)
- public LifetimeValidationError(
- MessageDetail messageDetail,
- Type exceptionType,
- StackFrame stackFrame,
- DateTime expires)
- : base(messageDetail, ValidationFailureType.LifetimeValidationFailed, exceptionType, stackFrame)
+ : base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException)
{
- _expires = expires;
+ NotBefore = notBefore;
+ Expires = expires;
}
///
@@ -50,33 +32,45 @@ internal override Exception GetException()
{
if (ExceptionType == typeof(SecurityTokenNoExpirationException))
{
- return new SecurityTokenNoExpirationException(MessageDetail.Message);
+ var exception = new SecurityTokenNoExpirationException(MessageDetail.Message, InnerException);
+ exception.SetValidationError(this);
+ return exception;
}
else if (ExceptionType == typeof(SecurityTokenInvalidLifetimeException))
{
- return new SecurityTokenInvalidLifetimeException(MessageDetail.Message)
+ var exception = new SecurityTokenInvalidLifetimeException(MessageDetail.Message, InnerException)
{
- NotBefore = _notBefore,
- Expires = _expires
+ NotBefore = NotBefore,
+ Expires = Expires
};
+ exception.SetValidationError(this);
+ return exception;
}
else if (ExceptionType == typeof(SecurityTokenNotYetValidException))
{
- return new SecurityTokenNotYetValidException(MessageDetail.Message)
+ var exception = new SecurityTokenNotYetValidException(MessageDetail.Message, InnerException)
{
- NotBefore = _notBefore
+ NotBefore = (DateTime)NotBefore!
};
+ exception.SetValidationError(this);
+ return exception;
}
else if (ExceptionType == typeof(SecurityTokenExpiredException))
{
- return new SecurityTokenExpiredException(MessageDetail.Message)
+ var exception = new SecurityTokenExpiredException(MessageDetail.Message, InnerException)
{
- Expires = _expires
+ Expires = (DateTime)Expires!
};
+ exception.SetValidationError(this);
+ return exception;
}
else
return base.GetException(ExceptionType, null);
}
+
+ protected DateTime? NotBefore { get; }
+
+ protected DateTime? Expires { 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 030d510485..298abeefce 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenTypeValidationError.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/TokenTypeValidationError.cs
@@ -9,16 +9,16 @@ namespace Microsoft.IdentityModel.Tokens
{
internal class TokenTypeValidationError : ValidationError
{
- protected string? _invalidTokenType;
-
internal TokenTypeValidationError(
MessageDetail messageDetail,
+ ValidationFailureType validationFailureType,
Type exceptionType,
StackFrame stackFrame,
- string? invalidTokenType)
- : base(messageDetail, ValidationFailureType.TokenTypeValidationFailed, exceptionType, stackFrame)
+ string? invalidTokenType,
+ Exception? innerException = null)
+ : base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException)
{
- _invalidTokenType = invalidTokenType;
+ InvalidTokenType = invalidTokenType;
}
internal override Exception GetException()
@@ -27,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 37bb29c092..b4e0a6714f 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ValidationError.cs
@@ -6,8 +6,10 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
+using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Logging;
+#nullable enable
namespace Microsoft.IdentityModel.Tokens
{
///
@@ -17,42 +19,25 @@ internal class ValidationError
{
private Type _exceptionType;
- ///
- /// Creates an instance of
- ///
- /// contains information about the exception that is used to generate the exception message.
- /// is the type of exception that occurred.
- /// is the type of validation failure that occurred.
- /// is the stack frame where the exception occurred.
- internal ValidationError(
- MessageDetail MessageDetail,
- ValidationFailureType failureType,
- Type exceptionType,
- StackFrame stackFrame)
- : this(MessageDetail, failureType, exceptionType, stackFrame, null)
- {
- // TODO: need to include CallContext.
- }
-
///
/// 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 exception that occurred.
- /// is the type of validation failure 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)
+ Exception? innerException = null)
{
InnerException = innerException;
MessageDetail = messageDetail;
_exceptionType = exceptionType;
- FailureType = failureType;
+ FailureType = validationFailureType;
StackFrames = new List(4)
{
stackFrame
@@ -68,11 +53,11 @@ internal virtual Exception GetException()
return GetException(ExceptionType, InnerException);
}
- internal Exception GetException(Type exceptionType, Exception innerException)
+ internal Exception GetException(Type exceptionType, Exception? innerException)
{
- Exception exception = null;
+ Exception? exception = null;
- if (innerException == null && InnerValidationError == null)
+ if (innerException is null)
{
if (exceptionType == typeof(SecurityTokenArgumentNullException))
exception = new SecurityTokenArgumentNullException(MessageDetail.Message);
@@ -82,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))
@@ -131,61 +118,61 @@ internal Exception GetException(Type exceptionType, Exception innerException)
}
else
{
- Exception actualException = innerException ?? InnerValidationError.GetException();
-
if (exceptionType == typeof(SecurityTokenArgumentNullException))
- return new SecurityTokenArgumentNullException(MessageDetail.Message, innerException);
+ exception = new SecurityTokenArgumentNullException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenInvalidAudienceException))
- exception = new SecurityTokenInvalidAudienceException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenInvalidAudienceException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenInvalidIssuerException))
- exception = new SecurityTokenInvalidIssuerException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenInvalidIssuerException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenInvalidLifetimeException))
- exception = new SecurityTokenInvalidLifetimeException(MessageDetail.Message, actualException);
+ 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, actualException);
+ exception = new SecurityTokenReplayDetectedException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenReplayAddFailedException))
- exception = new SecurityTokenReplayAddFailedException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenReplayAddFailedException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenInvalidSigningKeyException))
- exception = new SecurityTokenInvalidSigningKeyException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenInvalidSigningKeyException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenInvalidTypeException))
- exception = new SecurityTokenInvalidTypeException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenInvalidTypeException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenReplayDetectedException))
- exception = new SecurityTokenReplayDetectedException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenReplayDetectedException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenExpiredException))
- exception = new SecurityTokenExpiredException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenExpiredException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenNotYetValidException))
- exception = new SecurityTokenNotYetValidException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenNotYetValidException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenInvalidLifetimeException))
- exception = new SecurityTokenInvalidLifetimeException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenInvalidLifetimeException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenNoExpirationException))
- exception = new SecurityTokenNoExpirationException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenNoExpirationException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenInvalidIssuerException))
- exception = new SecurityTokenInvalidIssuerException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenInvalidIssuerException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenSignatureKeyNotFoundException))
- exception = new SecurityTokenSignatureKeyNotFoundException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenSignatureKeyNotFoundException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenDecryptionFailedException))
- exception = new SecurityTokenDecryptionFailedException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenDecryptionFailedException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenMalformedException))
- exception = new SecurityTokenMalformedException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenMalformedException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenInvalidSignatureException))
- exception = new SecurityTokenInvalidSignatureException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenInvalidSignatureException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenArgumentNullException))
- exception = new SecurityTokenArgumentNullException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenArgumentNullException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenInvalidAlgorithmException))
- exception = new SecurityTokenInvalidAlgorithmException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenInvalidAlgorithmException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenInvalidAlgorithmException))
- exception = new SecurityTokenInvalidAlgorithmException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenInvalidAlgorithmException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenException))
- exception = new SecurityTokenException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenKeyWrapException))
- exception = new SecurityTokenKeyWrapException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenKeyWrapException(MessageDetail.Message, innerException);
else if (exceptionType == typeof(SecurityTokenValidationException))
- exception = new SecurityTokenValidationException(MessageDetail.Message, actualException);
+ exception = new SecurityTokenValidationException(MessageDetail.Message, innerException);
else
{
// Exception type is unknown
var message = LogHelper.FormatInvariant(LogMessages.IDX10002, exceptionType, MessageDetail.Message);
- exception = new SecurityTokenException(message, actualException);
+ exception = new SecurityTokenException(message, innerException);
}
}
@@ -197,11 +184,17 @@ 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,
typeof(SecurityTokenArgumentNullException),
- stackFrame);
+ stackFrame,
+ null);
///
/// Gets the type of validation failure that occurred.
@@ -216,12 +209,7 @@ internal Exception GetException(Type exceptionType, Exception innerException)
///
/// Gets the inner exception that occurred.
///
- public Exception InnerException { get; }
-
- ///
- /// Gets the details for the inner exception that occurred.
- ///
- public ValidationError InnerValidationError { get; }
+ public Exception? InnerException { get; }
///
/// Gets the message details that are used to generate the exception message.
@@ -279,5 +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.Algorithm.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs
index 1651cd960b..4950042e26 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Algorithm.cs
@@ -57,6 +57,7 @@ internal static ValidationResult ValidateAlgorithm(
new MessageDetail(
LogMessages.IDX10696,
LogHelper.MarkAsNonPII(algorithm)),
+ ValidationFailureType.AlgorithmValidationFailed,
typeof(SecurityTokenInvalidAlgorithmException),
new StackFrame(true),
algorithm);
diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs
index ff97e1b405..8b0903d3dc 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using Microsoft.IdentityModel.Abstractions;
using Microsoft.IdentityModel.Logging;
@@ -48,48 +47,36 @@ internal static ValidationResult ValidateAudience(IList tokenAud
{
if (validationParameters == null)
{
- AudienceValidationError.ValidationParametersNull ??= new StackFrame(true);
- return new AudienceValidationError(
- MessageDetail.NullParameter(nameof(validationParameters)),
- ValidationFailureType.NullArgument,
- typeof(SecurityTokenArgumentNullException),
- AudienceValidationError.ValidationParametersNull,
- tokenAudiences,
- null);
+ return AudienceValidationError.NullParameter(
+ nameof(validationParameters),
+ ValidationError.GetCurrentStackFrame());
}
if (tokenAudiences == null)
{
- AudienceValidationError.AudiencesNull ??= new StackFrame(true);
- return new AudienceValidationError(
- MessageDetail.NullParameter(nameof(tokenAudiences)),
- ValidationFailureType.NullArgument,
- typeof(SecurityTokenArgumentNullException),
- AudienceValidationError.AudiencesNull,
- tokenAudiences,
- validationParameters.ValidAudiences);
+ return AudienceValidationError.NullParameter(
+ nameof(tokenAudiences),
+ ValidationError.GetCurrentStackFrame());
}
if (tokenAudiences.Count == 0)
{
- AudienceValidationError.AudiencesCountZero ??= new StackFrame(true);
return new AudienceValidationError(
new MessageDetail(LogMessages.IDX10206),
ValidationFailureType.NoTokenAudiencesProvided,
typeof(SecurityTokenInvalidAudienceException),
- AudienceValidationError.AudiencesCountZero,
+ ValidationError.GetCurrentStackFrame(),
tokenAudiences,
validationParameters.ValidAudiences);
}
if (validationParameters.ValidAudiences.Count == 0)
{
- AudienceValidationError.ValidationParametersAudiencesCountZero ??= new StackFrame(true);
return new AudienceValidationError(
new MessageDetail(LogMessages.IDX10268),
ValidationFailureType.NoValidationParameterAudiencesProvided,
typeof(SecurityTokenInvalidAudienceException),
- AudienceValidationError.ValidationParametersAudiencesCountZero,
+ ValidationError.GetCurrentStackFrame(),
tokenAudiences,
validationParameters.ValidAudiences);
}
@@ -99,7 +86,6 @@ internal static ValidationResult ValidateAudience(IList tokenAud
return validAudience;
// TODO we shouldn't be serializing here.
- AudienceValidationError.ValidateAudienceFailed ??= new StackFrame(true);
return new AudienceValidationError(
new MessageDetail(
LogMessages.IDX10215,
@@ -107,7 +93,7 @@ internal static ValidationResult ValidateAudience(IList tokenAud
LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidAudiences))),
ValidationFailureType.AudienceValidationFailed,
typeof(SecurityTokenInvalidAudienceException),
- AudienceValidationError.ValidateAudienceFailed,
+ ValidationError.GetCurrentStackFrame(),
tokenAudiences,
validationParameters.ValidAudiences);
}
diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs
index 8a2cd297b1..3bae752f35 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs
@@ -64,6 +64,7 @@ internal static async Task> ValidateIssuerAsyn
{
return new IssuerValidationError(
new MessageDetail(LogMessages.IDX10211),
+ ValidationFailureType.IssuerValidationFailed,
typeof(SecurityTokenInvalidIssuerException),
new StackFrame(true),
issuer);
@@ -87,6 +88,7 @@ internal static async Task> ValidateIssuerAsyn
if (validationParameters.ValidIssuers.Count == 0 && string.IsNullOrWhiteSpace(configuration?.Issuer))
return new IssuerValidationError(
new MessageDetail(LogMessages.IDX10211),
+ ValidationFailureType.IssuerValidationFailed,
typeof(SecurityTokenInvalidIssuerException),
new StackFrame(true),
issuer);
@@ -137,6 +139,7 @@ internal static async Task> ValidateIssuerAsyn
LogHelper.MarkAsNonPII(issuer),
LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidIssuers)),
LogHelper.MarkAsNonPII(configuration?.Issuer)),
+ ValidationFailureType.IssuerValidationFailed,
typeof(SecurityTokenInvalidIssuerException),
new StackFrame(true),
issuer);
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.Lifetime.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs
index a1d7c24153..6f7262e40f 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT License.
using System;
-using System.Diagnostics;
using Microsoft.IdentityModel.Logging;
#nullable enable
@@ -59,15 +58,18 @@ internal static ValidationResult ValidateLifetime(
if (validationParameters == null)
return ValidationError.NullParameter(
nameof(validationParameters),
- new StackFrame(true));
+ ValidationError.GetCurrentStackFrame());
if (!expires.HasValue)
return new LifetimeValidationError(
new MessageDetail(
LogMessages.IDX10225,
LogHelper.MarkAsNonPII(securityToken == null ? "null" : securityToken.GetType().ToString())),
+ ValidationFailureType.LifetimeValidationFailed,
typeof(SecurityTokenNoExpirationException),
- new StackFrame(true));
+ ValidationError.GetCurrentStackFrame(),
+ notBefore,
+ expires);
if (notBefore.HasValue && expires.HasValue && (notBefore.Value > expires.Value))
return new LifetimeValidationError(
@@ -75,10 +77,11 @@ internal static ValidationResult ValidateLifetime(
LogMessages.IDX10224,
LogHelper.MarkAsNonPII(notBefore.Value),
LogHelper.MarkAsNonPII(expires.Value)),
+ ValidationFailureType.LifetimeValidationFailed,
typeof(SecurityTokenInvalidLifetimeException),
- new StackFrame(true),
- notBefore.Value,
- expires.Value);
+ ValidationError.GetCurrentStackFrame(),
+ notBefore,
+ expires);
DateTime utcNow = validationParameters.TimeProvider.GetUtcNow().UtcDateTime;
if (notBefore.HasValue && (notBefore.Value > DateTimeUtil.Add(utcNow, validationParameters.ClockSkew)))
@@ -87,10 +90,11 @@ internal static ValidationResult ValidateLifetime(
LogMessages.IDX10222,
LogHelper.MarkAsNonPII(notBefore.Value),
LogHelper.MarkAsNonPII(utcNow)),
+ ValidationFailureType.LifetimeValidationFailed,
typeof(SecurityTokenNotYetValidException),
- new StackFrame(true),
- notBefore.Value,
- expires.Value);
+ ValidationError.GetCurrentStackFrame(),
+ notBefore,
+ expires);
if (expires.HasValue && (expires.Value < DateTimeUtil.Add(utcNow, validationParameters.ClockSkew.Negate())))
return new LifetimeValidationError(
@@ -98,9 +102,11 @@ internal static ValidationResult ValidateLifetime(
LogMessages.IDX10223,
LogHelper.MarkAsNonPII(expires.Value),
LogHelper.MarkAsNonPII(utcNow)),
+ ValidationFailureType.LifetimeValidationFailed,
typeof(SecurityTokenExpiredException),
- new StackFrame(true),
- expires.Value);
+ ValidationError.GetCurrentStackFrame(),
+ notBefore,
+ expires);
// if it reaches here, that means lifetime of the token is valid
return new ValidatedLifetime(notBefore, expires);
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 175cc190f7..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)
{
@@ -64,8 +63,9 @@ internal static ValidationResult ValidateTokenType(
if (string.IsNullOrEmpty(type))
return new TokenTypeValidationError(
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))
@@ -75,8 +75,9 @@ internal static ValidationResult ValidateTokenType(
LogMessages.IDX10257,
LogHelper.MarkAsNonPII(type),
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..a75bd5bfa4 100644
--- a/src/Microsoft.IdentityModel.Tokens/Validators.cs
+++ b/src/Microsoft.IdentityModel.Tokens/Validators.cs
@@ -87,6 +87,12 @@ public static void ValidateAudience(IEnumerable audiences, SecurityToken
return;
}
+ if (!validationParameters.RequireAudience && !audiences.Any())
+ {
+ LogHelper.LogWarning(LogMessages.IDX10277);
+ return;
+ }
+
if (audiences == null)
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAudienceException(LogMessages.IDX10207) { InvalidAudience = null });
@@ -409,13 +415,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 6374d3edb8..0000000000
--- a/src/Microsoft.IdentityModel.Xml/Exceptions/XmlValidationError.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-using System;
-using System.Diagnostics;
-using Microsoft.IdentityModel.Tokens;
-
-namespace Microsoft.IdentityModel.Xml
-{
- internal class XmlValidationError : ValidationError
- {
- public XmlValidationError(
- MessageDetail messageDetail,
- ValidationFailureType validationFailureType,
- Type exceptionType,
- StackFrame stackFrame) :
- base(messageDetail, validationFailureType, exceptionType, stackFrame)
- {
-
- }
-
- internal override Exception GetException()
- {
- if (ExceptionType == typeof(XmlValidationException))
- {
- XmlValidationException exception = new(MessageDetail.Message, InnerException);
- exception.SetValidationError(this);
- return exception;
- }
-
- return base.GetException();
- }
- }
-}
diff --git a/src/Microsoft.IdentityModel.Xml/InternalAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Xml/InternalAPI.Unshipped.txt
index fb30836a8b..7541dd5624 100644
--- a/src/Microsoft.IdentityModel.Xml/InternalAPI.Unshipped.txt
+++ b/src/Microsoft.IdentityModel.Xml/InternalAPI.Unshipped.txt
@@ -1,7 +1,7 @@
-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) -> void
+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
override Microsoft.IdentityModel.Xml.XmlValidationError.GetException() -> System.Exception
\ No newline at end of file
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/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs b/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs
index e9c10ef213..f108b9dbf5 100644
--- a/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs
+++ b/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs
@@ -1360,7 +1360,7 @@ private JwtSecurityToken ValidateSignature(string token, JwtSecurityToken jwtTok
if (key != null)
{
- (keysAttempted ??= new StringBuilder()).Append(key.ToString()).Append(" , KeyId: ").AppendLine(key.KeyId);
+ (keysAttempted ??= new StringBuilder()).Append(key.ToString());
if (kidExists && !kidMatched && key.KeyId != null)
kidMatched = jwtToken.Header.Kid.Equals(key.KeyId, key is X509SecurityKey ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
}
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 f18599b9c5..0000000000
--- a/test/Microsoft.IdentityModel.AotCompatibility.Tests/Microsoft.IdentityModel.AotCompatibility.Tests.csproj
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
- net8.0;net9.0
- $(TargetFrameworks);
- 13
- 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 b66d40b0f6..0000000000
--- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.Issuer.Extensibility.cs
+++ /dev/null
@@ -1,290 +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,
- CustomIssuerValidatorDelegates.CustomIssuerValidatorDelegateAsync,
- [
- new StackFrame("CustomValidationDelegates.cs", 88),
- new StackFrame(false),
- new StackFrame(false)
- ])
- {
- ExpectedException = new ExpectedException(
- typeof(SecurityTokenInvalidIssuerException),
- nameof(CustomIssuerValidatorDelegates.CustomIssuerValidatorDelegateAsync)),
- IssuerValidationError = new CustomIssuerValidationError(
- new MessageDetail(
- nameof(CustomIssuerValidatorDelegates.CustomIssuerValidatorDelegateAsync), null),
- typeof(SecurityTokenInvalidIssuerException),
- new StackFrame("CustomValidationDelegates.cs", 88),
- issuerGuid)
- });
-
- // CustomIssuerValidationError : IssuerValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException
- theoryData.Add(new IssuerExtensibilityTheoryData(
- "CustomIssuerValidatorCustomExceptionDelegate",
- issuerGuid,
- CustomIssuerValidatorDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync,
- [
- new StackFrame("CustomValidationDelegates.cs", 107),
- new StackFrame(false),
- new StackFrame(false)
- ])
- {
- ExpectedException = new ExpectedException(
- typeof(CustomSecurityTokenInvalidIssuerException),
- nameof(CustomIssuerValidatorDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync)),
- IssuerValidationError = new CustomIssuerValidationError(
- new MessageDetail(
- nameof(CustomIssuerValidatorDelegates.CustomIssuerValidatorCustomExceptionDelegateAsync), null),
- typeof(CustomSecurityTokenInvalidIssuerException),
- new StackFrame("CustomValidationDelegates.cs", 107),
- issuerGuid),
- });
-
- // CustomIssuerValidationError : IssuerValidationError, ExceptionType: NotSupportedException : SystemException
- theoryData.Add(new IssuerExtensibilityTheoryData(
- "CustomIssuerValidatorUnknownExceptionDelegate",
- issuerGuid,
- CustomIssuerValidatorDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync,
- [
- new StackFrame("CustomValidationDelegates.cs", 139),
- new StackFrame(false),
- new StackFrame(false)
- ])
- {
- ExpectedException = new ExpectedException(
- typeof(SecurityTokenException),
- nameof(CustomIssuerValidatorDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync)),
- IssuerValidationError = new CustomIssuerValidationError(
- new MessageDetail(
- nameof(CustomIssuerValidatorDelegates.CustomIssuerValidatorUnknownExceptionDelegateAsync), null),
- typeof(NotSupportedException),
- new StackFrame("CustomValidationDelegates.cs", 139),
- issuerGuid),
- });
-
- // CustomIssuerValidationError : IssuerValidationError, ExceptionType: NotSupportedException : SystemException, ValidationFailureType: CustomIssuerValidationFailureType
- theoryData.Add(new IssuerExtensibilityTheoryData(
- "CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegate",
- issuerGuid,
- CustomIssuerValidatorDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync,
- [
- new StackFrame("CustomValidationDelegates.cs", 123),
- new StackFrame(false),
- new StackFrame(false)
- ])
- {
- ExpectedException = new ExpectedException(
- typeof(CustomSecurityTokenInvalidIssuerException),
- nameof(CustomIssuerValidatorDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync)),
- IssuerValidationError = new CustomIssuerValidationError(
- new MessageDetail(
- nameof(CustomIssuerValidatorDelegates.CustomIssuerValidatorCustomExceptionCustomFailureTypeDelegateAsync), null),
- CustomIssuerValidationError.CustomIssuerValidationFailureType,
- typeof(CustomSecurityTokenInvalidIssuerException),
- new StackFrame("CustomValidationDelegates.cs", 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,
- CustomIssuerValidatorDelegates.IssuerValidatorDelegateAsync,
- [
- new StackFrame("CustomValidationDelegates.cs", 169),
- new StackFrame(false),
- new StackFrame(false)
- ])
- {
- ExpectedException = new ExpectedException(
- typeof(SecurityTokenInvalidIssuerException),
- nameof(CustomIssuerValidatorDelegates.IssuerValidatorDelegateAsync)),
- IssuerValidationError = new IssuerValidationError(
- new MessageDetail(
- nameof(CustomIssuerValidatorDelegates.IssuerValidatorDelegateAsync), null),
- typeof(SecurityTokenInvalidIssuerException),
- new StackFrame("CustomValidationDelegates.cs", 169),
- issuerGuid)
- });
-
- // IssuerValidationError : ValidationError, ExceptionType: CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException
- theoryData.Add(new IssuerExtensibilityTheoryData(
- "IssuerValidatorCustomIssuerExceptionTypeDelegate",
- issuerGuid,
- CustomIssuerValidatorDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync,
- [
- new StackFrame("CustomValidationDelegates.cs", 196),
- new StackFrame(false),
- new StackFrame(false)
- ])
- {
- ExpectedException = new ExpectedException(
- typeof(SecurityTokenException),
- nameof(CustomIssuerValidatorDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync)),
- IssuerValidationError = new IssuerValidationError(
- new MessageDetail(
- nameof(CustomIssuerValidatorDelegates.IssuerValidatorCustomIssuerExceptionTypeDelegateAsync), null),
- typeof(CustomSecurityTokenInvalidIssuerException),
- new StackFrame("CustomValidationDelegates.cs", 196),
- issuerGuid)
- });
-
- // IssuerValidationError : ValidationError, ExceptionType: CustomSecurityTokenException : SystemException
- theoryData.Add(new IssuerExtensibilityTheoryData(
- "IssuerValidatorCustomExceptionTypeDelegate",
- issuerGuid,
- CustomIssuerValidatorDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync,
- [
- new StackFrame("CustomValidationDelegates.cs", 210),
- new StackFrame(false),
- new StackFrame(false)
- ])
- {
- ExpectedException = new ExpectedException(
- typeof(SecurityTokenException),
- nameof(CustomIssuerValidatorDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync)),
- IssuerValidationError = new IssuerValidationError(
- new MessageDetail(
- nameof(CustomIssuerValidatorDelegates.IssuerValidatorCustomExceptionTypeDelegateAsync), null),
- typeof(CustomSecurityTokenException),
- new StackFrame("CustomValidationDelegates.cs", 210),
- issuerGuid)
- });
-
- // IssuerValidationError : ValidationError, ExceptionType: SecurityTokenInvalidIssuerException, inner: CustomSecurityTokenInvalidIssuerException
- theoryData.Add(new IssuerExtensibilityTheoryData(
- "IssuerValidatorThrows",
- issuerGuid,
- CustomIssuerValidatorDelegates.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(CustomIssuerValidatorDelegates.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.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Audience.Extensibility.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Audience.Extensibility.cs
deleted file mode 100644
index 9b2321e348..0000000000
--- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Audience.Extensibility.cs
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-#nullable enable
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.IdentityModel.TestUtils;
-using Microsoft.IdentityModel.Tokens;
-using Xunit;
-
-namespace Microsoft.IdentityModel.JsonWebTokens.Tests
-{
- public partial class JsonWebTokenHandlerValidateTokenAsyncTests
- {
- [Theory, MemberData(nameof(ValidateTokenAsync_Audience_ExtensibilityTestCases), DisableDiscoveryEnumeration = true)]
- public async Task ValidateTokenAsync_Audience_Extensibility(ValidateTokenAsyncAudienceExtensibilityTheoryData theoryData)
- {
- var context = TestUtilities.WriteHeader($"{this}.{nameof(ValidateTokenAsync_Audience_Extensibility)}", theoryData);
-
- string jwtString = CreateTokenWithAudience(theoryData.Audience);
- var handler = new JsonWebTokenHandler();
-
- ValidationResult validationResult;
-
- if (theoryData.ThrownException is null)
- {
- validationResult = await handler.ValidateTokenAsync(
- jwtString, theoryData.ValidationParameters!, theoryData.CallContext, CancellationToken.None);
- }
- else
- {
- // The exception is thrown by the delegate, so we catch it here.
- // Outside of testing, this could be a catch block in the calling code.
- var exception = await Assert.ThrowsAsync(async () =>
- {
- validationResult = await handler.ValidateTokenAsync(
- jwtString, theoryData.ValidationParameters!, theoryData.CallContext, CancellationToken.None);
- });
-
- theoryData.ThrownException.ProcessException(exception, context);
- return;
- }
-
- if (validationResult.IsValid != theoryData.ExpectedIsValid)
- context.AddDiff($"validationResult.IsValid != theoryData.ExpectedIsValid");
-
- if (validationResult.IsValid)
- {
- theoryData.ExpectedException.ProcessNoException(context);
-
- IdentityComparer.AreStringsEqual(validationResult.UnwrapResult().ValidatedAudience, theoryData.Audience, context);
- }
- else
- {
- theoryData.ExpectedException.ProcessException(validationResult.UnwrapError().GetException(), context);
-
- if (validationResult.UnwrapError().GetException() is SecurityTokenInvalidAudienceException audienceException)
- {
- if (theoryData.ExpectedInvalidAudience is not null)
- IdentityComparer.AreStringsEqual(audienceException.InvalidAudience, theoryData.ExpectedInvalidAudience, context);
- }
-
- TestUtilities.AssertFailIfErrors(context);
- }
- }
-
- public static TheoryData ValidateTokenAsync_Audience_ExtensibilityTestCases
- {
- get
- {
- var theoryData = new TheoryData();
- theoryData.Add(new ValidateTokenAsyncAudienceExtensibilityTheoryData("DefaultDelegate_Valid_AudiencesMatch")
- {
- ValidationParameters = CreateValidationParameters(audienceValidationDelegate: null),
- });
-
- theoryData.Add(new ValidateTokenAsyncAudienceExtensibilityTheoryData("DefaultDelegate_Invalid_AudiencesDontMatch")
- {
- ValidationParameters = CreateValidationParameters(audienceValidationDelegate: null),
- Audience = "CustomAudience",
- ExpectedIsValid = false,
- ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"),
- });
-
- theoryData.Add(new ValidateTokenAsyncAudienceExtensibilityTheoryData("CustomDelegate_Valid_DelegateReturnsAudience")
- {
- ValidationParameters = CreateValidationParameters(audienceValidationDelegate: delegate
- (IList audiences,
- SecurityToken? securityToken,
- ValidationParameters validationParameters,
- CallContext callContext)
- {
- return "CustomAudience";
- }),
- });
-
- theoryData.Add(new ValidateTokenAsyncAudienceExtensibilityTheoryData(
- "CustomDelegate_Invalid_DelegateReturnsValidationErrorWithDefaultExceptionType")
- {
- ValidationParameters = CreateValidationParameters(audienceValidationDelegate: delegate
- (IList audiences,
- SecurityToken? securityToken,
- ValidationParameters validationParameters,
- CallContext callContext)
- {
- return new AudienceValidationError(
- new MessageDetail("Custom message from the delegate."),
- ValidationFailureType.AudienceValidationFailed,
- typeof(SecurityTokenInvalidAudienceException),
- new StackFrame(true),
- [Default.Audience],
- null);
- }),
- ExpectedIsValid = false,
- ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidAudienceException), "Custom message from the delegate."),
- ExpectedInvalidAudience = Default.Audience,
- });
-
- theoryData.Add(new ValidateTokenAsyncAudienceExtensibilityTheoryData(
- "CustomDelegate_Invalid_DelegateReturnsValidationErrorWithCustomExceptionType_NoCustomValidationError")
- {
- ValidationParameters = CreateValidationParameters(audienceValidationDelegate: delegate
- (IList audiences,
- SecurityToken? securityToken,
- ValidationParameters validationParameters,
- CallContext callContext)
- {
- return new AudienceValidationError(
- new MessageDetail("Custom message from the delegate."),
- ValidationFailureType.AudienceValidationFailed,
- typeof(CustomInvalidAudienceException),
- new StackFrame(true),
- [Default.Audience],
- null);
- }),
- ExpectedIsValid = false,
- // The delegate returns a custom exception but does not implement a custom ValidationError.
- ExpectedException = ExpectedException.SecurityTokenException("IDX10002:"),
- ExpectedInvalidAudience = Default.Audience,
- });
-
- theoryData.Add(new ValidateTokenAsyncAudienceExtensibilityTheoryData(
- "CustomDelegate_Invalid_DelegateReturnsValidationErrorWithCustomExceptionType_CustomValidationErrorUsed")
- {
- ValidationParameters = CreateValidationParameters(audienceValidationDelegate: delegate
- (IList audiences,
- SecurityToken? securityToken,
- ValidationParameters validationParameters,
- CallContext callContext)
- {
- return new CustomAudienceValidationError(
- new MessageDetail("Custom message from the delegate."),
- typeof(CustomInvalidAudienceException),
- new StackFrame(true),
- [Default.Audience]);
- }),
- ExpectedIsValid = false,
- // The delegate uses a custom validation error that implements GetException to return the custom exception.
- ExpectedException = new ExpectedException(typeof(CustomInvalidAudienceException), "Custom message from the delegate."),
- ExpectedInvalidAudience = Default.Audience,
- });
-
- theoryData.Add(new ValidateTokenAsyncAudienceExtensibilityTheoryData("CustomDelegate_Invalid_DelegateThrows")
- {
- ValidationParameters = CreateValidationParameters(audienceValidationDelegate: delegate
- (IList audiences,
- SecurityToken? securityToken,
- ValidationParameters validationParameters,
- CallContext callContext)
- {
- throw new CustomInvalidAudienceException("Custom exception from the delegate.");
- }),
- ExpectedIsValid = false,
- ThrownException = new ExpectedException(typeof(CustomInvalidAudienceException), "Custom exception from the delegate."),
- });
-
- return theoryData;
-
- static ValidationParameters CreateValidationParameters(
- AudienceValidationDelegate? audienceValidationDelegate)
- {
- ValidationParameters validationParameters = new ValidationParameters();
- validationParameters.ValidAudiences.Add(Default.Audience);
-
- if (audienceValidationDelegate is not null)
- validationParameters.AudienceValidator = audienceValidationDelegate;
-
- // Skip all validations except audience
- validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation;
- validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation;
- validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation;
- validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation;
- validationParameters.SignatureValidator = SkipValidationDelegates.SkipSignatureValidation;
- validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation;
-
- return validationParameters;
- }
- }
- }
-
- public class ValidateTokenAsyncAudienceExtensibilityTheoryData : ValidateTokenAsyncBaseTheoryData
- {
- public ValidateTokenAsyncAudienceExtensibilityTheoryData(string testId) : base(testId) { }
-
- public string? Audience { get; internal set; } = Default.Audience;
-
- public string? ExpectedInvalidAudience { get; internal set; } = null;
-
- internal AudienceValidationDelegate? AudienceValidationDelegate { get; set; }
-
- public ExpectedException? ThrownException { get; internal set; } = null;
- }
-
- private class CustomInvalidAudienceException : SecurityTokenInvalidAudienceException
- {
- public CustomInvalidAudienceException(string message)
- : base(message)
- {
- }
- }
-
- private class CustomAudienceValidationError : AudienceValidationError
- {
- public CustomAudienceValidationError(MessageDetail messageDetail,
- Type exceptionType,
- StackFrame stackFrame,
- IList? tokenAudiences) :
- base(messageDetail, ValidationFailureType.AudienceValidationFailed, exceptionType, stackFrame, tokenAudiences, null)
- {
- }
-
- internal override Exception GetException()
- {
- if (ExceptionType == typeof(CustomInvalidAudienceException))
- return new CustomInvalidAudienceException(MessageDetail.Message) { InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(TokenAudiences) };
-
- return base.GetException();
- }
- }
- }
-}
-#nullable restore
diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Lifetime.Extensibility.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Lifetime.Extensibility.cs
deleted file mode 100644
index b7c09dab2f..0000000000
--- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Lifetime.Extensibility.cs
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-#nullable enable
-using System;
-using System.Diagnostics;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.IdentityModel.TestUtils;
-using Microsoft.IdentityModel.Tokens;
-using Xunit;
-
-namespace Microsoft.IdentityModel.JsonWebTokens.Tests
-{
- public partial class JsonWebTokenHandlerValidateTokenAsyncTests
- {
- [Theory, MemberData(nameof(ValidateTokenAsync_Lifetime_ExtensibilityTestCases), DisableDiscoveryEnumeration = true)]
- public async Task ValidateTokenAsync_Lifetime_Extensibility(ValidateTokenAsyncLifetimeExtensibilityTheoryData theoryData)
- {
- var context = TestUtilities.WriteHeader($"{this}.{nameof(ValidateTokenAsync_Lifetime_Extensibility)}", theoryData);
-
- string jwtString = CreateTokenWithLifetime(theoryData.IssuedAt, theoryData.NotBefore, theoryData.Expires);
- var handler = new JsonWebTokenHandler();
-
- ValidationResult validationResult;
-
- if (theoryData.ThrownException is null)
- {
- validationResult = await handler.ValidateTokenAsync(
- jwtString, theoryData.ValidationParameters!, theoryData.CallContext, CancellationToken.None);
- }
- else
- {
- // The exception is thrown by the delegate, so we catch it here.
- // Outside of testing, this could be a catch block in the calling code.
- var exception = await Assert.ThrowsAsync(async () =>
- {
- validationResult = await handler.ValidateTokenAsync(
- jwtString, theoryData.ValidationParameters!, theoryData.CallContext, CancellationToken.None);
- });
-
- theoryData.ThrownException.ProcessException(exception, context);
- return;
- }
-
- if (validationResult.IsValid != theoryData.ExpectedIsValid)
- context.AddDiff($"validationResult.IsValid != theoryData.ExpectedIsValid");
-
- if (validationResult.IsValid)
- {
- theoryData.ExpectedException.ProcessNoException(context);
-
- ValidatedLifetime? validatedLifetime = validationResult.UnwrapResult().ValidatedLifetime;
-
- if (validatedLifetime is not null)
- {
- IdentityComparer.AreDateTimesEqualWithEpsilon(validatedLifetime.Value.NotBefore, theoryData.ValidatedLifetime.NotBefore, 3, context);
- IdentityComparer.AreDateTimesEqualWithEpsilon(validatedLifetime.Value.Expires, theoryData.ValidatedLifetime.Expires, 3, context);
- }
- }
- else
- {
- theoryData.ExpectedException.ProcessException(validationResult.UnwrapError().GetException(), context);
-
- if (validationResult.UnwrapError().GetException() is SecurityTokenInvalidLifetimeException lifetimeException)
- {
- if (theoryData.ExpectedInvalidNotBefore is not null)
- IdentityComparer.AreDateTimesEqualWithEpsilon(lifetimeException.NotBefore, theoryData.ExpectedInvalidNotBefore, 3, context);
-
- if (theoryData.ExpectedInvalidExpires is not null)
- IdentityComparer.AreDateTimesEqualWithEpsilon(lifetimeException.Expires, theoryData.ExpectedInvalidExpires, 3, context);
- }
-
- TestUtilities.AssertFailIfErrors(context);
- }
- }
-
- public static TheoryData ValidateTokenAsync_Lifetime_ExtensibilityTestCases
- {
- get
- {
- DateTime now = DateTime.UtcNow;
- DateTime nowPlus1Hour = now.AddHours(1);
- DateTime nowMinus1Hour = now.AddHours(-1);
-
- var theoryData = new TheoryData();
-
- theoryData.Add(new ValidateTokenAsyncLifetimeExtensibilityTheoryData("DefaultDelegate_Valid_LifetimeIsValid")
- {
- IssuedAt = now,
- NotBefore = nowMinus1Hour,
- Expires = nowPlus1Hour,
- ValidationParameters = CreateValidationParameters(lifetimeValidationDelegate: null),
- });
-
- theoryData.Add(new ValidateTokenAsyncLifetimeExtensibilityTheoryData("DefaultDelegate_Invalid_TokenHasExpired")
- {
- ValidationParameters = CreateValidationParameters(lifetimeValidationDelegate: null),
- IssuedAt = nowMinus1Hour,
- NotBefore = nowMinus1Hour,
- Expires = nowMinus1Hour,
- ExpectedIsValid = false,
- ExpectedException = ExpectedException.SecurityTokenExpiredException("IDX10223:"),
- });
-
- theoryData.Add(new ValidateTokenAsyncLifetimeExtensibilityTheoryData("CustomDelegate_Valid_DelegateReturnsValidatedLifetime")
- {
- IssuedAt = nowMinus1Hour,
- NotBefore = nowMinus1Hour,
- Expires = nowMinus1Hour,
- ValidationParameters = CreateValidationParameters(lifetimeValidationDelegate: delegate
- (DateTime? notBefore,
- DateTime? expires,
- SecurityToken? securityToken,
- ValidationParameters validationParameters,
- CallContext callContext)
- {
- return new ValidatedLifetime(notBefore, expires);
- }),
- });
-
- theoryData.Add(new ValidateTokenAsyncLifetimeExtensibilityTheoryData("CustomDelegate_Valid_DelegateReturnsEmptyValidatedLifetime")
- {
- IssuedAt = nowMinus1Hour,
- NotBefore = nowMinus1Hour,
- Expires = nowMinus1Hour,
- ValidationParameters = CreateValidationParameters(lifetimeValidationDelegate: delegate
- (DateTime? notBefore,
- DateTime? expires,
- SecurityToken? securityToken,
- ValidationParameters validationParameters,
- CallContext callContext)
- {
- return new ValidatedLifetime();
- }),
- });
-
- theoryData.Add(new ValidateTokenAsyncLifetimeExtensibilityTheoryData(
- "CustomDelegate_Invalid_DelegateReturnsValidationErrorWithDefaultExceptionType")
- {
- IssuedAt = nowMinus1Hour,
- NotBefore = nowMinus1Hour,
- Expires = nowMinus1Hour,
- ValidationParameters = CreateValidationParameters(lifetimeValidationDelegate: delegate
- (DateTime? notBefore,
- DateTime? expires,
- SecurityToken? securityToken,
- ValidationParameters validationParameters,
- CallContext callContext)
- {
- return new LifetimeValidationError(new MessageDetail("Custom message from the delegate."),
- typeof(SecurityTokenInvalidLifetimeException),
- new System.Diagnostics.StackFrame(true),
- (DateTime)notBefore!,
- (DateTime)expires!);
- }),
- ExpectedIsValid = false,
- ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidLifetimeException), "Custom message from the delegate."),
- ExpectedInvalidNotBefore = nowMinus1Hour,
- ExpectedInvalidExpires = nowMinus1Hour,
- });
-
- theoryData.Add(new ValidateTokenAsyncLifetimeExtensibilityTheoryData(
- "CustomDelegate_Invalid_DelegateReturnsValidationErrorWithCustomExceptionType_NoCustomValidationError")
- {
- IssuedAt = nowMinus1Hour,
- NotBefore = nowMinus1Hour,
- Expires = nowMinus1Hour,
- ValidationParameters = CreateValidationParameters(lifetimeValidationDelegate: delegate
- (DateTime? notBefore,
- DateTime? expires,
- SecurityToken? securityToken,
- ValidationParameters validationParameters,
- CallContext callContext)
- {
- return new LifetimeValidationError(
- new MessageDetail("Custom message from the delegate."),
- typeof(CustomInvalidLifetimeException),
- new System.Diagnostics.StackFrame(true),
- (DateTime)notBefore!,
- (DateTime)expires!);
- }),
- ExpectedIsValid = false,
- // The delegate returns a custom exception but does not implement a custom ValidationError.
- ExpectedException = ExpectedException.SecurityTokenException("IDX10002:"),
- ExpectedInvalidNotBefore = nowMinus1Hour,
- ExpectedInvalidExpires = nowMinus1Hour,
- });
-
- theoryData.Add(new ValidateTokenAsyncLifetimeExtensibilityTheoryData(
- "CustomDelegate_Invalid_DelegateReturnsValidationErrorWithCustomExceptionType_CustomValidationErrorUsed")
- {
- IssuedAt = nowMinus1Hour,
- NotBefore = nowMinus1Hour,
- Expires = nowMinus1Hour,
- ValidationParameters = CreateValidationParameters(lifetimeValidationDelegate: delegate
- (DateTime? notBefore,
- DateTime? expires,
- SecurityToken? securityToken,
- ValidationParameters validationParameters,
- CallContext callContext)
- {
- return new CustomLifetimeValidationError(
- new MessageDetail("Custom message from the delegate."),
- typeof(CustomInvalidLifetimeException),
- new System.Diagnostics.StackFrame(true),
- (DateTime)notBefore!,
- (DateTime)expires!);
- }),
- ExpectedIsValid = false,
- // The delegate uses a custom validation error that implements GetException to return the custom exception.
- ExpectedException = new ExpectedException(typeof(CustomInvalidLifetimeException), "Custom message from the delegate."),
- ExpectedInvalidNotBefore = nowMinus1Hour,
- ExpectedInvalidExpires = nowMinus1Hour,
- });
-
- theoryData.Add(new ValidateTokenAsyncLifetimeExtensibilityTheoryData("CustomDelegate_Invalid_DelegateThrows")
- {
- IssuedAt = nowMinus1Hour,
- NotBefore = nowMinus1Hour,
- Expires = nowMinus1Hour,
- ValidationParameters = CreateValidationParameters(lifetimeValidationDelegate: delegate
- (DateTime? notBefore,
- DateTime? expires,
- SecurityToken? securityToken,
- ValidationParameters validationParameters,
- CallContext callContext)
- {
- throw new CustomInvalidLifetimeException("Custom exception from the delegate.");
- }),
- ExpectedIsValid = false,
- ThrownException = new ExpectedException(typeof(CustomInvalidLifetimeException), "Custom exception from the delegate."),
- });
-
- return theoryData;
-
- static ValidationParameters CreateValidationParameters(LifetimeValidationDelegate? lifetimeValidationDelegate)
- {
- ValidationParameters validationParameters = new ValidationParameters();
-
- if (lifetimeValidationDelegate is not null)
- validationParameters.LifetimeValidator = lifetimeValidationDelegate;
-
- // Skip all validations except lifetime
- validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation;
- validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation;
- validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation;
- validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation;
- validationParameters.SignatureValidator = SkipValidationDelegates.SkipSignatureValidation;
- validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation;
-
- return validationParameters;
- }
- }
- }
-
- public class ValidateTokenAsyncLifetimeExtensibilityTheoryData : ValidateTokenAsyncBaseTheoryData
- {
- public ValidateTokenAsyncLifetimeExtensibilityTheoryData(string testId) : base(testId) { }
-
- public DateTime? IssuedAt { get; internal set; } = null;
-
- public DateTime? NotBefore { get; internal set; } = null;
-
- public DateTime? Expires { get; internal set; } = null;
-
- internal ValidatedLifetime ValidatedLifetime { get; set; } = default;
-
- public DateTime? ExpectedInvalidNotBefore { get; internal set; } = null;
-
- public DateTime? ExpectedInvalidExpires { get; internal set; } = null;
-
- public ExpectedException? ThrownException { get; internal set; } = null;
- }
-
- private class CustomInvalidLifetimeException : SecurityTokenInvalidLifetimeException
- {
- public CustomInvalidLifetimeException(string message)
- : base(message)
- {
- }
- }
-
- private class CustomLifetimeValidationError : LifetimeValidationError
- {
- public CustomLifetimeValidationError(MessageDetail messageDetail,
- Type exceptionType,
- StackFrame stackFrame,
- DateTime notBefore,
- DateTime expires) :
- base(messageDetail, exceptionType, stackFrame, notBefore, expires)
- {
- }
-
- internal override Exception GetException()
- {
- if (ExceptionType == typeof(CustomInvalidLifetimeException))
- return new CustomInvalidLifetimeException(MessageDetail.Message) { NotBefore = _notBefore, Expires = _expires };
-
- return base.GetException();
- }
- }
- }
-}
-#nullable restore
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/CustomExceptions.cs b/test/Microsoft.IdentityModel.TestUtils/CustomExceptions.cs
deleted file mode 100644
index d1bfc42e68..0000000000
--- a/test/Microsoft.IdentityModel.TestUtils/CustomExceptions.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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 CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException
- {
- public CustomSecurityTokenInvalidIssuerException(string message)
- : base(message)
- {
- }
- }
-
- internal class CustomSecurityTokenException : SystemException
- {
- public CustomSecurityTokenException(string message)
- : base(message)
- {
- }
- }
-}
-#nullable restore
diff --git a/test/Microsoft.IdentityModel.TestUtils/CustomValidationErrors.cs b/test/Microsoft.IdentityModel.TestUtils/CustomValidationErrors.cs
deleted file mode 100644
index b8ecee5438..0000000000
--- a/test/Microsoft.IdentityModel.TestUtils/CustomValidationErrors.cs
+++ /dev/null
@@ -1,59 +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.TestUtils
-{
- internal class CustomIssuerValidationError : IssuerValidationError
- {
- ///
- /// A custom validation failure type.
- ///
- public static readonly ValidationFailureType CustomIssuerValidationFailureType = new IssuerValidatorFailure("CustomIssuerValidationFailureType");
- private class IssuerValidatorFailure : ValidationFailureType { internal IssuerValidatorFailure(string name) : base(name) { } }
-
- public CustomIssuerValidationError(
- MessageDetail messageDetail,
- Type exceptionType,
- StackFrame stackFrame,
- string? invalidIssuer)
- : base(messageDetail, exceptionType, stackFrame, invalidIssuer)
- {
- }
-
- public CustomIssuerValidationError(
- MessageDetail messageDetail,
- ValidationFailureType validationFailureType,
- Type exceptionType,
- StackFrame stackFrame,
- string? invalidIssuer,
- Exception? innerException)
- : base(messageDetail, validationFailureType, exceptionType, stackFrame, invalidIssuer, innerException)
- {
- }
-
- internal override Exception GetException()
- {
- if (ExceptionType == typeof(CustomSecurityTokenInvalidIssuerException))
- return new CustomSecurityTokenInvalidIssuerException(MessageDetail.Message) { InvalidIssuer = InvalidIssuer };
-
- return base.GetException();
- }
- }
-
- internal class CustomIssuerWithoutGetExceptionValidationOverrideError : IssuerValidationError
- {
- public CustomIssuerWithoutGetExceptionValidationOverrideError(MessageDetail messageDetail,
- Type exceptionType,
- StackFrame stackFrame,
- string? invalidIssuer) :
- base(messageDetail, exceptionType, stackFrame, invalidIssuer)
- {
- }
- }
-}
-#nullable restore
diff --git a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs
index 1ce815b2ea..f3152b4d34 100644
--- a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs
+++ b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs
@@ -1429,7 +1429,7 @@ internal static bool AreValidationErrorsEqual(ValidationError validationError1,
localContext.Diffs.Add($"(validationError1.StackFrames[0].GetFileName(): " +
$"'{validationError1.StackFrames[0].GetFileName()}', " +
$"does not contain validationError2.StackFrames[0].GetFileName():" +
- $"'{validationError1.StackFrames[0].GetFileName()}'.");
+ $"'{validationError2.StackFrames[0].GetFileName()}'.");
}
}
@@ -1468,6 +1468,12 @@ internal static bool AreValidationErrorsEqual(ValidationError validationError1,
validationError2.MessageDetail,
localContext);
+ // compare the actual exception's stack trace against the expected stack trace.
+ if (exception1.StackTrace == null)
+ {
+ localContext.Diffs.Add($"exception1.StackTrace is null. Exception type: {exception1.GetType().Name}");
+ }
+
return context.Merge(localContext);
}
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/CustomExceptions.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomExceptions.cs
new file mode 100644
index 0000000000..30a751e075
--- /dev/null
+++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomExceptions.cs
@@ -0,0 +1,90 @@
+// 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 CustomSecurityTokenException : SecurityTokenException
+ {
+ public CustomSecurityTokenException(string message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+
+ internal class CustomSecurityTokenInvalidIssuerException : SecurityTokenInvalidIssuerException
+ {
+ public CustomSecurityTokenInvalidIssuerException(string message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+
+ internal class CustomSecurityTokenInvalidAudienceException : SecurityTokenInvalidAudienceException
+ {
+ public CustomSecurityTokenInvalidAudienceException(string message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+
+ internal class CustomSecurityTokenInvalidLifetimeException : SecurityTokenInvalidLifetimeException
+ {
+ public CustomSecurityTokenInvalidLifetimeException(string message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+
+ internal class CustomSecurityTokenInvalidSignatureException : SecurityTokenInvalidSignatureException
+ {
+ public CustomSecurityTokenInvalidSignatureException(string message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+
+ internal class CustomSecurityTokenInvalidAlgorithmException : SecurityTokenInvalidAlgorithmException
+ {
+ public CustomSecurityTokenInvalidAlgorithmException(string message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+
+ internal class CustomSecurityTokenInvalidTypeException : SecurityTokenInvalidTypeException
+ {
+ public CustomSecurityTokenInvalidTypeException(string message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+
+ internal class CustomSecurityTokenInvalidSigningKeyException : SecurityTokenInvalidSigningKeyException
+ {
+ public CustomSecurityTokenInvalidSigningKeyException(string message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+
+ internal class CustomSecurityTokenReplayDetectedException : SecurityTokenReplayDetectedException
+ {
+ public CustomSecurityTokenReplayDetectedException(string message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+
+ internal class CustomSecurityTokenDecryptionFailedException : SecurityTokenDecryptionFailedException
+ {
+ public CustomSecurityTokenDecryptionFailedException(string message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+}
+#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/CustomValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerValidationDelegates.cs
similarity index 93%
rename from test/Microsoft.IdentityModel.TestUtils/CustomValidationDelegates.cs
rename to test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerValidationDelegates.cs
index 70f2082265..f5f1e73537 100644
--- a/test/Microsoft.IdentityModel.TestUtils/CustomValidationDelegates.cs
+++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomIssuerValidationDelegates.cs
@@ -9,7 +9,7 @@
#nullable enable
namespace Microsoft.IdentityModel.TestUtils
{
- internal class CustomIssuerValidatorDelegates
+ internal class CustomIssuerValidationDelegates
{
internal async static Task> CustomIssuerValidatorDelegateAsync(
string issuer,
@@ -22,6 +22,7 @@ internal async static Task> CustomIssuerValida
return await Task.FromResult(new ValidationResult(
new CustomIssuerValidationError(
new MessageDetail(nameof(CustomIssuerValidatorDelegateAsync), null),
+ ValidationFailureType.IssuerValidationFailed,
typeof(SecurityTokenInvalidIssuerException),
ValidationError.GetCurrentStackFrame(),
issuer)));
@@ -37,6 +38,7 @@ internal async static Task> CustomIssuerValida
return await Task.FromResult(new ValidationResult(
new CustomIssuerValidationError(
new MessageDetail(nameof(CustomIssuerValidatorCustomExceptionDelegateAsync), null),
+ ValidationFailureType.IssuerValidationFailed,
typeof(CustomSecurityTokenInvalidIssuerException),
ValidationError.GetCurrentStackFrame(),
issuer)));
@@ -69,6 +71,7 @@ internal async static Task> CustomIssuerValida
return await Task.FromResult(new ValidationResult(
new CustomIssuerValidationError(
new MessageDetail(nameof(CustomIssuerValidatorUnknownExceptionDelegateAsync), null),
+ ValidationFailureType.IssuerValidationFailed,
typeof(NotSupportedException),
ValidationError.GetCurrentStackFrame(),
issuer)));
@@ -99,6 +102,7 @@ internal async static Task> IssuerValidatorDel
return await Task.FromResult(new ValidationResult(
new IssuerValidationError(
new MessageDetail(nameof(IssuerValidatorDelegateAsync), null),
+ ValidationFailureType.IssuerValidationFailed,
typeof(SecurityTokenInvalidIssuerException),
ValidationError.GetCurrentStackFrame(),
issuer)));
@@ -111,7 +115,7 @@ internal static Task> IssuerValidatorThrows(
CallContext callContext,
CancellationToken cancellationToken)
{
- throw new CustomSecurityTokenInvalidIssuerException(nameof(IssuerValidatorThrows));
+ throw new CustomSecurityTokenInvalidIssuerException(nameof(IssuerValidatorThrows), null);
}
internal async static Task> IssuerValidatorCustomIssuerExceptionTypeDelegateAsync(
@@ -124,6 +128,7 @@ internal async static Task> IssuerValidatorCus
return await Task.FromResult(new ValidationResult(
new IssuerValidationError(
new MessageDetail(nameof(IssuerValidatorCustomIssuerExceptionTypeDelegateAsync), null),
+ ValidationFailureType.IssuerValidationFailed,
typeof(CustomSecurityTokenInvalidIssuerException),
ValidationError.GetCurrentStackFrame(),
issuer)));
@@ -138,6 +143,7 @@ internal async static Task> IssuerValidatorCus
return await Task.FromResult(new ValidationResult(
new IssuerValidationError(
new MessageDetail(nameof(IssuerValidatorCustomExceptionTypeDelegateAsync), null),
+ ValidationFailureType.IssuerValidationFailed,
typeof(CustomSecurityTokenException),
ValidationError.GetCurrentStackFrame(),
issuer)));
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
new file mode 100644
index 0000000000..7571d4f8aa
--- /dev/null
+++ b/test/Microsoft.IdentityModel.TestUtils/TokenValidationExtensibility/CustomValidationErrors.cs
@@ -0,0 +1,392 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Microsoft.IdentityModel.Tokens;
+
+#nullable enable
+namespace Microsoft.IdentityModel.TestUtils
+{
+ #region IssuerValidationErrors
+ internal class CustomIssuerValidationError : IssuerValidationError
+ {
+ ///
+ /// A custom validation failure type.
+ ///
+ public static readonly ValidationFailureType CustomIssuerValidationFailureType = new IssuerValidatorFailure("CustomIssuerValidationFailureType");
+ private class IssuerValidatorFailure : ValidationFailureType { internal IssuerValidatorFailure(string name) : base(name) { } }
+
+ public CustomIssuerValidationError(
+ MessageDetail messageDetail,
+ ValidationFailureType validationFailureType,
+ Type exceptionType,
+ StackFrame stackFrame,
+ string? invalidIssuer,
+ Exception? innerException = null)
+ : base(messageDetail, validationFailureType, exceptionType, stackFrame, invalidIssuer, innerException)
+ {
+ }
+
+ internal override Exception GetException()
+ {
+ if (ExceptionType == typeof(CustomSecurityTokenInvalidIssuerException))
+ {
+ var exception = new CustomSecurityTokenInvalidIssuerException(MessageDetail.Message, InnerException) { InvalidIssuer = InvalidIssuer };
+ exception.SetValidationError(this);
+
+ return exception;
+ }
+
+ return base.GetException();
+ }
+ }
+
+ internal class CustomIssuerWithoutGetExceptionValidationOverrideError : IssuerValidationError
+ {
+ public CustomIssuerWithoutGetExceptionValidationOverrideError(MessageDetail messageDetail,
+ Type exceptionType,
+ StackFrame stackFrame,
+ string? invalidIssuer) :
+ base(messageDetail, ValidationFailureType.IssuerValidationFailed, exceptionType, stackFrame, invalidIssuer)
+ {
+ }
+ }
+ #endregion
+
+ #region AudienceValidationErrors
+ internal class CustomAudienceValidationError : AudienceValidationError
+ {
+ ///
+ /// A custom validation failure type.
+ ///
+ public static readonly ValidationFailureType CustomAudienceValidationFailureType = new AudienceValidatorFailure("CustomAudienceValidationFailureType");
+ private class AudienceValidatorFailure : ValidationFailureType { internal AudienceValidatorFailure(string name) : base(name) { } }
+
+ public CustomAudienceValidationError(
+ MessageDetail messageDetail,
+ ValidationFailureType validationFailureType,
+ Type exceptionType,
+ StackFrame stackFrame,
+ IList? tokenAudiences,
+ IList? validAudiences,
+ Exception? innerException = null)
+ : base(messageDetail, validationFailureType, exceptionType, stackFrame, tokenAudiences, validAudiences, innerException)
+ {
+ }
+
+ internal override Exception GetException()
+ {
+ if (ExceptionType == typeof(CustomSecurityTokenInvalidAudienceException))
+ {
+ var exception = new CustomSecurityTokenInvalidAudienceException(MessageDetail.Message, InnerException) { InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(TokenAudiences) };
+ exception.SetValidationError(this);
+
+ return exception;
+ }
+
+ return base.GetException();
+ }
+ }
+
+ internal class CustomAudienceWithoutGetExceptionValidationOverrideError : AudienceValidationError
+ {
+ public CustomAudienceWithoutGetExceptionValidationOverrideError(
+ MessageDetail messageDetail,
+ Type exceptionType,
+ StackFrame stackFrame,
+ IList? tokenAudiences,
+ IList? validAudiences,
+ Exception? innerException = null) :
+ base(messageDetail, ValidationFailureType.AudienceValidationFailed, exceptionType, stackFrame, tokenAudiences, validAudiences, innerException)
+ {
+ }
+ }
+ #endregion
+
+ #region LifetimeValidationErrors
+ internal class CustomLifetimeValidationError : LifetimeValidationError
+ {
+ ///
+ /// A custom validation failure type.
+ ///
+ public static readonly ValidationFailureType CustomLifetimeValidationFailureType = new LifetimeValidationFailure("CustomLifetimeValidationFailureType");
+ private class LifetimeValidationFailure : ValidationFailureType { internal LifetimeValidationFailure(string name) : base(name) { } }
+
+ public CustomLifetimeValidationError(
+ MessageDetail messageDetail,
+ ValidationFailureType validationFailureType,
+ Type exceptionType,
+ StackFrame stackFrame,
+ DateTime? notBefore,
+ DateTime? expires,
+ Exception? innerException = null)
+ : base(messageDetail, validationFailureType, exceptionType, stackFrame, notBefore, expires)
+ {
+ }
+
+ internal override Exception GetException()
+ {
+ if (ExceptionType == typeof(CustomSecurityTokenInvalidLifetimeException))
+ {
+ var exception = new CustomSecurityTokenInvalidLifetimeException(MessageDetail.Message, InnerException) { NotBefore = NotBefore, Expires = Expires };
+ exception.SetValidationError(this);
+
+ return exception;
+ }
+
+ return base.GetException();
+ }
+ }
+
+ internal class CustomLifetimeWithoutGetExceptionValidationOverrideError : LifetimeValidationError
+ {
+ public CustomLifetimeWithoutGetExceptionValidationOverrideError(
+ MessageDetail messageDetail,
+ ValidationFailureType validationFailureType,
+ Type exceptionType,
+ StackFrame stackFrame,
+ DateTime? notBefore,
+ DateTime? expires,
+ Exception? innerException = null)
+ : base(messageDetail, validationFailureType, exceptionType, stackFrame, notBefore, expires, innerException)
+ {
+ }
+ }
+ #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.Tokens.Tests/Validation/AlgorithmValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AlgorithmValidationResultTests.cs
index 8b55c64778..3c55debd4b 100644
--- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AlgorithmValidationResultTests.cs
+++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AlgorithmValidationResultTests.cs
@@ -68,7 +68,7 @@ public static TheoryData AlgorithmValidationTestCases
LogHelper.MarkAsNonPII("validationParameters")),
ValidationFailureType.NullArgument,
typeof(SecurityTokenArgumentNullException),
- null) // StackFrame
+ null), // StackFrame
},
new AlgorithmTheoryData
{
@@ -87,7 +87,7 @@ public static TheoryData AlgorithmValidationTestCases
LogHelper.MarkAsNonPII(SecurityAlgorithms.Sha256)),
ValidationFailureType.AlgorithmValidationFailed,
typeof(SecurityTokenInvalidAlgorithmException),
- null),// StackFrame
+ null), // StackFrame
},
new AlgorithmTheoryData
{
diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs
index 788a662384..9d3ab30aad 100644
--- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs
+++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs
@@ -28,91 +28,105 @@ public void ValidateAudienceParameters(AudienceValidationTheoryData theoryData)
TestUtilities.AssertFailIfErrors(context);
}
+
public static TheoryData ValidateAudienceParametersTheoryData
{
get
{
return new TheoryData
{
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("TokenValidationParametersNull")
{
Audiences = new List { "audience1" },
ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"),
- TestId = "TokenValidationParametersNull",
TokenValidationParameters = null
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("AudiencesEmptyString")
{
Audiences = new List { "" },
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "AudiencesEmptyString",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = "audience"}
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("AudiencesWhiteSpace")
{
Audiences = new List { " " },
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "AudiencesWhiteSpace",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = "audience"}
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("AudiencesNull")
{
Audiences = null,
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10207:"),
- TestId = "AudiencesNull"
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("AudiencesEmptyList")
{
Audiences = new List{ },
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10206:"),
- TestId = "AudiencesEmptyList",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = "audience"}
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("ValidateAudienceFalseAudiencesEmptyList")
{
Audiences = new List{ },
- TestId = "ValidateAudienceFalseAudiencesEmptyList",
TokenValidationParameters = new TokenValidationParameters{ ValidateAudience = false }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("ValidateAudienceFalseAudiencesNull")
{
Audiences = null,
- TestId = "ValidateAudienceFalseAudiencesNull",
TokenValidationParameters = new TokenValidationParameters{ ValidateAudience = false }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("ValidAudienceEmptyString")
{
Audiences = new List { "audience1" },
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10208:"),
- TestId = "ValidAudienceEmptyString",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = "" }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("ValidAudienceWhiteSpace")
{
Audiences = new List { "audience1" },
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10208:"),
- TestId = "ValidAudienceWhiteSpace",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = " " }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("ValidAudiencesEmptyString")
{
Audiences = new List { "audience1" },
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "ValidAudiencesEmptyString",
TokenValidationParameters = new TokenValidationParameters{ ValidAudiences = new List{ "" } }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("ValidAudiencesWhiteSpace")
{
Audiences = new List { "audience1" },
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "ValidAudiencesWhiteSpace",
TokenValidationParameters = new TokenValidationParameters{ ValidAudiences = new List{ " " } }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("ValidateAudienceTrueValidAudienceAndValidAudiencesNull")
{
Audiences = new List { "audience1" },
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10208:"),
- TestId = "ValidateAudienceTrueValidAudienceAndValidAudiencesNull"
+ },
+ new AudienceValidationTheoryData("AudiencesEmpty_RequireAudienceFalse_NoException")
+ {
+ Audiences = new List { },
+ TokenValidationParameters = new TokenValidationParameters{
+ RequireAudience = false
+ // default value of TVP.RequireAudience is true.
+ }
+ },
+ new AudienceValidationTheoryData("ValidAudience_RequireAudienceFalse_NoException")
+ {
+ Audiences = new List { "audience" },
+ TokenValidationParameters = new TokenValidationParameters{
+ ValidAudience = "audience",
+ RequireAudience = false
+ }
+ },
+ new AudienceValidationTheoryData("InvalidAudience_RequireAudienceFalse_ExceptionThrown")
+ {
+ Audiences = new List { "audience1" },
+ ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
+ TokenValidationParameters = new TokenValidationParameters{
+ ValidAudience = "audience",
+ RequireAudience = false
+ }
}
};
}
@@ -149,131 +163,112 @@ public static TheoryData ValidateAudienceTheoryDat
return new TheoryData
{
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("SameLengthMatched")
{
Audiences = audiences1,
- TestId = "SameLengthMatched",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = audience1 }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("SameLengthNotMatched")
{
Audiences = audiences1,
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "SameLengthNotMatched",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = audience2 }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("NoMatchTVPValidateFalse")
{
Audiences = audiences1,
- TestId = "NoMatchTVPValidateFalse",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = audience2, ValidateAudience = false }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("AudiencesValidAudienceWithSlashNotMatched")
{
Audiences = audiences1,
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "AudiencesValidAudienceWithSlashNotMatched",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = audience2 + "/" }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("AudiencesWithSlashValidAudienceSameLengthNotMatched")
{
Audiences = audiences2WithSlash,
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "AudiencesWithSlashValidAudienceSameLengthNotMatched",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = audience1 }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("ValidAudienceWithSlashTVPFalse")
{
Audiences = audiences1,
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "ValidAudienceWithSlashTVPFalse",
TokenValidationParameters = new TokenValidationParameters{ IgnoreTrailingSlashWhenValidatingAudience = false, ValidAudience = audience1 + "/" }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("ValidAudienceWithSlashTVPTrue")
{
Audiences = audiences1,
- TestId = "ValidAudienceWithSlashTVPTrue",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = audience1 + "/" }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("ValidAudiencesWithSlashTVPFalse")
{
Audiences = audiences1,
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "ValidAudiencesWithSlashTVPFalse",
TokenValidationParameters = new TokenValidationParameters{ IgnoreTrailingSlashWhenValidatingAudience = false, ValidAudiences = audiences1WithSlash }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("ValidAudiencesWithSlashTVPTrue")
{
Audiences = audiences1,
- TestId = "ValidAudiencesWithSlashTVPTrue",
TokenValidationParameters = new TokenValidationParameters{ ValidAudiences = audiences1WithSlash }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("ValidAudienceWithExtraChar")
{
Audiences = audiences1,
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "ValidAudienceWithExtraChar",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = audience1 + "A" }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("ValidAudienceWithDoubleSlashTVPTrue")
{
Audiences = audiences1,
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "ValidAudienceWithDoubleSlashTVPTrue",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = audience1 + "//" }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("ValidAudiencesWithDoubleSlashTVPTrue")
{
Audiences = audiences1,
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "ValidAudiencesWithDoubleSlashTVPTrue",
TokenValidationParameters = new TokenValidationParameters{ ValidAudiences = audiences1WithTwoSlashes }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("TokenAudienceWithSlashTVPFalse")
{
Audiences = audiences1WithSlash,
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "TokenAudienceWithSlashTVPFalse",
TokenValidationParameters = new TokenValidationParameters{ IgnoreTrailingSlashWhenValidatingAudience = false, ValidAudience = audience1 }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("TokenAudienceWithSlashTVPTrue")
{
Audiences = audiences1WithSlash,
- TestId = "TokenAudienceWithSlashTVPTrue",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = audience1 }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("TokenAudienceWithSlashNotEqual")
{
Audiences = audiences2WithSlash,
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "TokenAudienceWithSlashNotEqual",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = audience1 },
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("TokenAudiencesWithSlashTVPFalse")
{
Audiences = audiences1WithSlash,
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "TokenAudiencesWithSlashTVPFalse",
TokenValidationParameters = new TokenValidationParameters{ IgnoreTrailingSlashWhenValidatingAudience = false, ValidAudience = audience1 }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("TokenAudiencesWithSlashTVPTrue")
{
Audiences = audiences1WithSlash,
- TestId = "TokenAudiencesWithSlashTVPTrue",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = audience1 }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("TokenAudiencesWithSlashValidAudiencesNotMatchedTVPTrue")
{
Audiences = audiences1WithSlash,
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "TokenAudiencesWithSlashValidAudiencesNotMatchedTVPTrue",
TokenValidationParameters = new TokenValidationParameters{ ValidAudiences = audiences2 }
},
- new AudienceValidationTheoryData
+ new AudienceValidationTheoryData("TokenAudienceWithTwoSlashesTVPTrue")
{
Audiences = audiences1WithTwoSlashes,
ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"),
- TestId = "TokenAudienceWithTwoSlashesTVPTrue",
TokenValidationParameters = new TokenValidationParameters{ ValidAudience = audience1 }
}
};
@@ -500,6 +495,9 @@ public bool TryFind(string securityToken)
public class AudienceValidationTheoryData : TheoryDataBase
{
+ public AudienceValidationTheoryData(string testId) : base(testId)
+ { }
+
public List Audiences { get; set; }
public SecurityToken SecurityToken { get; set; }
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";