diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs index 2deb10bafd..cfa1e5c057 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.Internal.cs @@ -267,13 +267,30 @@ private async ValueTask> ValidateJWSAsync( 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; 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 23b4444503..ec438248be 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.ValidateToken.Internal.cs @@ -211,18 +211,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(); - - var audienceValidationResult = validationParameters.AudienceValidator( - audiencesAsList, - samlToken, - validationParameters, - callContext); - - if (!audienceValidationResult.IsValid) - return audienceValidationResult.UnwrapError(); + 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 + { + 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/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs index 81ae5762f8..b2358e68ec 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.ValidateToken.Internal.cs @@ -235,15 +235,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 { - StackFrames.AudienceValidationFailed ??= new StackFrame(true); - return audienceValidationResult.UnwrapError().AddStackFrame(StackFrames.AudienceValidationFailed); + 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 + { + 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. diff --git a/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt index c81cb4212e..cc4d32a13a 100644 --- a/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt @@ -1,6 +1,7 @@ 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.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 @@ -61,6 +62,7 @@ static Microsoft.IdentityModel.Tokens.TokenTypeValidationError.NullParameter(str static Microsoft.IdentityModel.Tokens.Utility.SerializeAsSingleCommaDelimitedString(System.Collections.Generic.IList strings) -> string static Microsoft.IdentityModel.Tokens.ValidationError.GetCurrentStackFrame(string filePath = "", int lineNumber = 0, int skipFrames = 1) -> System.Diagnostics.StackFrame static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.AlgorithmValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType +static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.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.NoTokenAudiencesProvided -> Microsoft.IdentityModel.Tokens.ValidationFailureType diff --git a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs index 02062ddad4..d87ce1e01c 100644 --- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs @@ -88,6 +88,7 @@ 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 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."; diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs index f20011641c..5fc1fa5cc6 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs @@ -140,6 +140,11 @@ private class XmlValidationFailure : ValidationFailureType { internal XmlValidat 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 audience validation delegate threw an exception. + /// + public static readonly ValidationFailureType AudienceValidatorThrew = new AudienceValidationFailure("AudienceValidatorThrew"); + /// /// Defines a type that represents the fact that the issuer signing key validation delegate threw an exception. /// 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.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/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.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/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