Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/Microsoft.IdentityModel.Tokens/LogMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ internal static class LogMessages
public const string IDX10256 = "IDX10256: Unable to validate the token type. TokenValidationParameters.ValidTypes is set, but the 'typ' header claim is null or empty.";
public const string IDX10257 = "IDX10257: Token type validation failed. Type: '{0}'. Did not match: validationParameters.TokenTypes: '{1}'.";
public const string IDX10258 = "IDX10258: Token type validated. Type: '{0}'.";
public const string IDX10259 = "IDX10259: Unable to validate the token type, delegate threw an exception.";
// public const string IDX10260 = "IDX10260:";
public const string IDX10261 = "IDX10261: Unable to retrieve configuration from authority: '{0}'. \nProceeding with token validation in case the relevant properties have been set manually on the TokenValidationParameters. Exception caught: \n {1}. See https://aka.ms/validate-using-configuration-manager for additional information.";
public const string IDX10262 = "IDX10262: One of the issuers in TokenValidationParameters.ValidIssuers was null or an empty string. See https://aka.ms/wilson/tokenvalidation for details.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,16 +440,16 @@ public string RoleClaimType
public TokenReplayValidator TokenReplayValidator { get; set; }

/// <summary>
/// Gets or sets a delegate that will be used to validate the type of the token.
/// If the token type cannot be validated, an exception MUST be thrown by the delegate.
/// Allows overriding the delegate that will be used to validate the type of the token.
/// If the token type cannot be validated, a <see cref="TokenTypeValidationResult"/> MUST be returned by the delegate.
/// Note: the 'type' parameter may be null if it couldn't be extracted from its usual location.
/// Implementations that need to resolve it from a different location can use the 'token' parameter.
/// </summary>
/// <remarks>
/// If set, this delegate will be called to validate the 'type' of the token, instead of default processing.
/// This means that no default 'type' validation will occur.
/// If no delegate is set, the default implementation will be used. The default checks the type
/// against the <see cref="ValidTypes"/> property, if it's present then, it will succeed.
/// </remarks>
public TypeValidator TypeValidator { get; set; }
public TypeValidatorDelegate TypeValidator { get; set; } = Validators.ValidateTokenType;

/// <summary>
/// Gets or sets a boolean to control if the LKG configuration will be used for token validation.
Expand Down Expand Up @@ -491,12 +491,12 @@ public string RoleClaimType
public IList<string> ValidIssuers { get; }

/// <summary>
/// Gets the <see cref="IList{String}"/> that contains valid types that will be used to check against the JWT header's 'typ' claim.
/// Gets or sets the <see cref="IList{String}"/> that contains valid types that will be used to check against the JWT header's 'typ' claim.
/// If this property is not set, the 'typ' header claim will not be validated and all types will be accepted.
/// In the case of a JWE, this property will ONLY apply to the inner token header.
/// The default is <c>null</c>.
/// </summary>
public IList<string> ValidTypes { get; }
public IList<string> ValidTypes { get; set; }

public bool ValidateActor { get; set; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public static partial class Validators
/// <param name="algorithm">The algorithm to be validated.</param>
/// <param name="securityKey">The <see cref="SecurityKey"/> that signed the <see cref="SecurityToken"/>.</param>
/// <param name="securityToken">The <see cref="SecurityToken"/> being validated.</param>
/// <param name="validationParameters"><see cref="TokenValidationParameters"/> required for validation.</param>
/// <param name="validationParameters"><see cref="ValidationParameters"/> required for validation.</param>
/// <param name="callContext"></param>
#pragma warning disable CA1801 // TODO: remove pragma disable once callContext is used for logging
internal static AlgorithmValidationResult ValidateAlgorithm(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,38 @@
#nullable enable
namespace Microsoft.IdentityModel.Tokens
{
/// <summary>
/// Definition for delegate that will validate the token type of a token.
/// </summary>
/// <param name="type">The token type or <c>null</c> if it couldn't be resolved (e.g from the 'typ' header for a JWT).</param>
/// <param name="securityToken">The <see cref="SecurityToken"/> that is being validated.</param>
/// <param name="validationParameters"><see cref="ValidationParameters"/> required for validation.</param>
/// <param name="callContext"></param>
/// <returns> A <see cref="TokenTypeValidationResult"/>that contains the results of validating the token type.</returns>
/// <remarks>An EXACT match is required. <see cref="StringComparison.Ordinal"/> (case sensitive) is used for comparing <paramref name="type"/> against <see cref="TokenValidationParameters.ValidTypes"/>.</remarks>
internal delegate TokenTypeValidationResult TypeValidatorDelegate(
string? type,
SecurityToken? securityToken,
ValidationParameters validationParameters,
CallContext callContext);

public static partial class Validators
{
/// <summary>
/// Validates the type of the token.
/// </summary>
/// <param name="type">The token type or <c>null</c> if it couldn't be resolved (e.g from the 'typ' header for a JWT).</param>
/// <param name="securityToken">The <see cref="SecurityToken"/> that is being validated.</param>
/// <param name="validationParameters"><see cref="TokenValidationParameters"/> required for validation.</param>
/// <param name="validationParameters"><see cref="ValidationParameters"/> required for validation.</param>
/// <param name="callContext"></param>
/// <exception cref="ArgumentNullException">If <paramref name="validationParameters"/> is null.</exception>
/// <exception cref="ArgumentNullException">If <paramref name="securityToken"/> is null.</exception>
/// <exception cref="SecurityTokenInvalidTypeException">If <paramref name="type"/> is null or whitespace and <see cref="TokenValidationParameters.ValidTypes"/> is not null.</exception>
/// <exception cref="SecurityTokenInvalidTypeException">If <paramref name="type"/> failed to match <see cref="TokenValidationParameters.ValidTypes"/>.</exception>
/// <returns> A <see cref="TokenTypeValidationResult"/>that contains the results of validating the token type.</returns>
/// <remarks>An EXACT match is required. <see cref="StringComparison.Ordinal"/> (case sensitive) is used for comparing <paramref name="type"/> against <see cref="TokenValidationParameters.ValidTypes"/>.</remarks>
#pragma warning disable CA1801 // TODO: remove pragma disable once callContext is used for logging
internal static TokenTypeValidationResult ValidateTokenType(string? type, SecurityToken? securityToken, TokenValidationParameters validationParameters, CallContext callContext)
internal static TokenTypeValidationResult ValidateTokenType(
string? type,
SecurityToken? securityToken,
ValidationParameters validationParameters,
CallContext callContext)
#pragma warning restore CA1801 // TODO: remove pragma disable once callContext is used for logging
{
if (securityToken == null)
Expand Down Expand Up @@ -54,19 +70,12 @@ internal static TokenTypeValidationResult ValidateTokenType(string? type, Securi
new StackFrame(true)));
}

if (validationParameters.TypeValidator == null && (validationParameters.ValidTypes == null || !validationParameters.ValidTypes.Any()))
if (validationParameters.ValidTypes == null || validationParameters.ValidTypes.Count == 0)
{
LogHelper.LogVerbose(LogMessages.IDX10255);
return new TokenTypeValidationResult(type);
}

if (validationParameters.TypeValidator != null)
{
return ValidateTokenTypeUsingDelegate(type, securityToken, validationParameters);
}

// Note: don't return an invalid TokenTypeValidationResult for a null or empty token type when a user-defined delegate is set
// to allow it to extract the actual token type from a different location (e.g from the claims).
if (string.IsNullOrEmpty(type))
{
return new TokenTypeValidationResult(
Expand Down Expand Up @@ -101,31 +110,6 @@ internal static TokenTypeValidationResult ValidateTokenType(string? type, Securi

return new TokenTypeValidationResult(type);
}

private static TokenTypeValidationResult ValidateTokenTypeUsingDelegate(string? type, SecurityToken securityToken, TokenValidationParameters validationParameters)
{
try
{
var validatedType = validationParameters.TypeValidator(type, securityToken, validationParameters);
return new TokenTypeValidationResult(validatedType);
}
#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 TokenTypeValidationResult(
type,
ValidationFailureType.TokenTypeValidationFailed,
new ExceptionDetail(
new MessageDetail(
LogMessages.IDX10259,
LogHelper.MarkAsNonPII(nameof(validationParameters.TypeValidator)),
LogHelper.MarkAsNonPII(ex.Message)),
ex.GetType(),
new StackFrame(true),
ex));
}
}
}
}
#nullable restore
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static TheoryData<TokenTypeTheoryData> TokenTypeValidationTestCases
TestId = "Valid_DefaultTokenTypeValidation",
Type = "JWT",
SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, "JWT"),
ValidationParameters = new TokenValidationParameters
ValidationParameters = new ValidationParameters
{
ValidTypes = validTypesWithJwt
},
Expand Down Expand Up @@ -94,45 +94,11 @@ public static TheoryData<TokenTypeTheoryData> TokenTypeValidationTestCases
},
new TokenTypeTheoryData
{
TestId = "Valid_ValidateTokenTypeUsingDelegate",
TestId = "Valid_ValidationParametersTypeValidTypesAreNull",
Type = "JWT",
SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, "JWT"),
ValidationParameters = new TokenValidationParameters
ValidationParameters = new ValidationParameters
{
TypeValidator = (Type, SecurityToken, TokenValidationParameters) => "JWT"
},
TokenTypeValidationResult = new TokenTypeValidationResult("JWT")
},
new TokenTypeTheoryData
{
TestId = "Invalid_ValidateTokenTypeUsingDelegate",
ExpectedException = ExpectedException.SecurityTokenInvalidTypeException(substringExpected: "IDX10259:", innerTypeExpected: typeof(SecurityTokenInvalidTypeException)),
Type = "JWT",
SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, "JWT"),
ValidationParameters = new TokenValidationParameters
{
TypeValidator = (Type, SecurityToken, TokenValidationParameters) => throw new SecurityTokenInvalidTypeException()
},
TokenTypeValidationResult = new TokenTypeValidationResult(
"JWT",
ValidationFailureType.TokenTypeValidationFailed,
new ExceptionDetail(
new MessageDetail(
LogMessages.IDX10259,
LogHelper.MarkAsNonPII("TypeValidator"),
LogHelper.MarkAsNonPII("Delegate message")),
typeof(SecurityTokenInvalidTypeException),
new StackFrame(true),
new SecurityTokenInvalidTypeException()))
},
new TokenTypeTheoryData
{
TestId = "Valid_TokenValidationParametersTypeValidatorAndValidTypesAreNull",
Type = "JWT",
SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, "JWT"),
ValidationParameters = new TokenValidationParameters
{
TypeValidator = null,
ValidTypes = null
},
TokenTypeValidationResult = new TokenTypeValidationResult("JWT")
Expand All @@ -143,7 +109,7 @@ public static TheoryData<TokenTypeTheoryData> TokenTypeValidationTestCases
ExpectedException = ExpectedException.SecurityTokenInvalidTypeException("IDX10256:"),
Type = String.Empty,
SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, String.Empty),
ValidationParameters = new TokenValidationParameters
ValidationParameters = new ValidationParameters
{
ValidTypes = validTypesNoJwt
},
Expand All @@ -163,7 +129,7 @@ public static TheoryData<TokenTypeTheoryData> TokenTypeValidationTestCases
ExpectedException = ExpectedException.SecurityTokenInvalidTypeException("IDX10256:"),
Type = null,
SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, null),
ValidationParameters = new TokenValidationParameters
ValidationParameters = new ValidationParameters
{
ValidTypes = validTypesNoJwt
},
Expand All @@ -179,11 +145,11 @@ public static TheoryData<TokenTypeTheoryData> TokenTypeValidationTestCases
},
new TokenTypeTheoryData
{
TestId = "Invalid_TokenValidationParametersValidTypesDoesNotSupportType",
TestId = "Invalid_ValidationParametersValidTypesDoesNotSupportType",
ExpectedException = ExpectedException.SecurityTokenInvalidTypeException("IDX10257:"),
Type = "JWT",
SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Typ, "JWT"),
ValidationParameters = new TokenValidationParameters
ValidationParameters = new ValidationParameters
{
ValidTypes = validTypesNoJwt
},
Expand All @@ -208,7 +174,7 @@ public class TokenTypeTheoryData : TheoryDataBase

public SecurityToken SecurityToken { get; set; }

public TokenValidationParameters ValidationParameters { get; set; }
internal ValidationParameters ValidationParameters { get; set; }

internal TokenTypeValidationResult TokenTypeValidationResult { get; set; }
}
Expand Down