Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regression tests: Token Type #2932

Merged
merged 11 commits into from
Oct 24, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ await ValidateJWSAsync(actorToken, actorParameters, configuration, callContext,
actorValidationResult = innerActorValidationResult;
}

ValidationResult<ValidatedTokenType> typeValidationResult = validationParameters.TypeValidator(
ValidationResult<ValidatedTokenType> typeValidationResult = validationParameters.TokenTypeValidator(
jsonWebToken.Typ, jsonWebToken, validationParameters, callContext);
if (!typeValidationResult.IsValid)
{
Expand Down
2 changes: 0 additions & 2 deletions src/Microsoft.IdentityModel.Tokens/InternalAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -550,8 +550,6 @@ Microsoft.IdentityModel.Tokens.ValidationParameters.TokenReplayValidator.get ->
Microsoft.IdentityModel.Tokens.ValidationParameters.TokenReplayValidator.set -> void
Microsoft.IdentityModel.Tokens.ValidationParameters.TryAllIssuerSigningKeys.get -> bool
Microsoft.IdentityModel.Tokens.ValidationParameters.TryAllIssuerSigningKeys.set -> void
Microsoft.IdentityModel.Tokens.ValidationParameters.TypeValidator.get -> Microsoft.IdentityModel.Tokens.TokenTypeValidationDelegate
Microsoft.IdentityModel.Tokens.ValidationParameters.TypeValidator.set -> void
Microsoft.IdentityModel.Tokens.ValidationParameters.ValidAlgorithms.get -> System.Collections.Generic.IList<string>
Microsoft.IdentityModel.Tokens.ValidationParameters.ValidAlgorithms.set -> void
Microsoft.IdentityModel.Tokens.ValidationParameters.ValidateActor.get -> bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ Microsoft.IdentityModel.Tokens.IssuerValidationSource.IssuerMatchedConfiguration
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.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.TokenValidationParameters.TimeProvider.get -> System.TimeProvider
Microsoft.IdentityModel.Tokens.TokenValidationParameters.TimeProvider.set -> void
Microsoft.IdentityModel.Tokens.ValidationError.GetException(System.Type exceptionType, System.Exception innerException) -> System.Exception
Microsoft.IdentityModel.Tokens.ValidationParameters.TokenTypeValidator.get -> Microsoft.IdentityModel.Tokens.TokenTypeValidationDelegate
Microsoft.IdentityModel.Tokens.ValidationParameters.TokenTypeValidator.set -> void
Microsoft.IdentityModel.Tokens.ValidationResult<TResult>.Error.get -> Microsoft.IdentityModel.Tokens.ValidationError
Microsoft.IdentityModel.Tokens.ValidationResult<TResult>.IsValid.get -> bool
Microsoft.IdentityModel.Tokens.ValidationResult<TResult>.Result.get -> TResult
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
Expand All @@ -20,4 +26,4 @@ static Microsoft.IdentityModel.Tokens.AudienceValidationError.ValidationParamete
static Microsoft.IdentityModel.Tokens.Utility.SerializeAsSingleCommaDelimitedString(System.Collections.Generic.IList<string> strings) -> string
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.SignatureAlgorithmValidationFailed -> Microsoft.IdentityModel.Tokens.ValidationFailureType
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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 TokenTypeValidationError : ValidationError
{
protected string? _invalidTokenType;

internal TokenTypeValidationError(
MessageDetail messageDetail,
Type exceptionType,
StackFrame stackFrame,
string? invalidTokenType)
: base(messageDetail, ValidationFailureType.TokenTypeValidationFailed, exceptionType, stackFrame)
{
_invalidTokenType = invalidTokenType;
}

internal override Exception GetException()
{
if (ExceptionType == typeof(SecurityTokenInvalidTypeException))
{
SecurityTokenInvalidTypeException exception = new(MessageDetail.Message, InnerException)
{
InvalidType = _invalidTokenType
};

return exception;
}

return base.GetException();
}
}
}
#nullable restore
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ protected ValidationParameters(ValidationParameters other)
TokenDecryptionKeys = other.TokenDecryptionKeys;
TokenReplayCache = other.TokenReplayCache;
TokenReplayValidator = other.TokenReplayValidator;
TypeValidator = other.TypeValidator;
TokenTypeValidator = other.TokenTypeValidator;
ValidateActor = other.ValidateActor;
ValidateSignatureLast = other.ValidateSignatureLast;
ValidateWithLKG = other.ValidateWithLKG;
Expand Down Expand Up @@ -499,7 +499,7 @@ public TokenReplayValidationDelegate TokenReplayValidator
/// </remarks>
/// <exception cref="ArgumentNullException">Thrown when the value is set as null.</exception>
/// <returns>The <see cref="TokenTypeValidationDelegate"/> used to validate the token type of a token</returns>
public TokenTypeValidationDelegate TypeValidator
public TokenTypeValidationDelegate TokenTypeValidator
kellyyangsong marked this conversation as resolved.
Show resolved Hide resolved
{
get { return _tokenTypeValidator; }
set { _tokenTypeValidator = value ?? throw new ArgumentNullException(nameof(value), "TypeValidator cannot be set as null."); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,27 +56,28 @@ internal static ValidationResult<ValidatedTokenType> ValidateTokenType(

if (validationParameters.ValidTypes.Count == 0)
{
LogHelper.LogVerbose(LogMessages.IDX10255);
//TODO: Move to CallContext?
//LogHelper.LogVerbose(LogMessages.IDX10255);
return new ValidatedTokenType(type ?? "null", validationParameters.ValidTypes.Count);
}

if (string.IsNullOrEmpty(type))
return new ValidationError(
return new TokenTypeValidationError(
new MessageDetail(LogMessages.IDX10256),
ValidationFailureType.TokenTypeValidationFailed,
typeof(SecurityTokenInvalidTypeException),
new StackFrame(true));
new StackFrame(true),
null); // even if it is empty, we report null to match the original behaviour.

if (!validationParameters.ValidTypes.Contains(type, StringComparer.Ordinal))
{
return new ValidationError(
return new TokenTypeValidationError(
new MessageDetail(
LogMessages.IDX10257,
LogHelper.MarkAsNonPII(type),
LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidTypes))),
ValidationFailureType.TokenTypeValidationFailed,
typeof(SecurityTokenInvalidTypeException),
new StackFrame(true));
new StackFrame(true),
type);
}

// TODO: Move to CallContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ static ValidationParameters CreateValidationParameters(
validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation;
validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation;
validationParameters.SignatureValidator = SkipValidationDelegates.SkipSignatureValidation;
validationParameters.TypeValidator = SkipValidationDelegates.SkipTokenTypeValidation;
validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation;

return validationParameters;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ static ValidationParameters CreateValidationParameters(
validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation;
validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation;
validationParameters.TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation;
validationParameters.TypeValidator = SkipValidationDelegates.SkipTokenTypeValidation;
validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation;

return validationParameters;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ static ValidationParameters CreateValidationParameters(LifetimeValidationDelegat
validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation;
validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation;
validationParameters.SignatureValidator = SkipValidationDelegates.SkipSignatureValidation;
validationParameters.TypeValidator = SkipValidationDelegates.SkipTokenTypeValidation;
validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation;

return validationParameters;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ static ValidationParameters CreateValidationParameters(
validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation;
validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation;
validationParameters.TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation;
validationParameters.TypeValidator = SkipValidationDelegates.SkipTokenTypeValidation;
validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation;
validationParameters.TryAllIssuerSigningKeys = tryAllKeys;

return validationParameters;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#nullable enable
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_TokenTypeTestCases), DisableDiscoveryEnumeration = true)]
public async Task ValidateTokenAsync_TokenType(ValidateTokenAsyncTokenTypeTheoryData theoryData)
{
var context = TestUtilities.WriteHeader($"{this}.ValidateTokenAsync_TokenType", theoryData);

string jwtString = CreateTokenForTokenTypeValidation(theoryData.UseEmptyType, theoryData.CustomTokenType);

await ValidateAndCompareResults(jwtString, theoryData, context);

TestUtilities.AssertFailIfErrors(context);
}

public static TheoryData<ValidateTokenAsyncTokenTypeTheoryData> ValidateTokenAsync_TokenTypeTestCases
{
get
{
var theoryData = new TheoryData<ValidateTokenAsyncTokenTypeTheoryData>();

theoryData.Add(new ValidateTokenAsyncTokenTypeTheoryData("Valid_JwtToken")
{
TokenValidationParameters = CreateTokenValidationParameters(),
ValidationParameters = CreateValidationParameters(),
});

theoryData.Add(new ValidateTokenAsyncTokenTypeTheoryData("Valid_UnknownTokenType_NoValidTokenTypes")
{
// If there are no valid token types, any token type is valid
CustomTokenType = "SomeUnknownType",
TokenValidationParameters = CreateTokenValidationParameters(null),
ValidationParameters = CreateValidationParameters(null),
});

theoryData.Add(new ValidateTokenAsyncTokenTypeTheoryData("Valid_CustomToken_AddedAsValidTokenType")
{
CustomTokenType = "PPT",
TokenValidationParameters = CreateTokenValidationParameters(validTokenType: "PPT"),
ValidationParameters = CreateValidationParameters(validTokenType: "PPT"),
});

theoryData.Add(new ValidateTokenAsyncTokenTypeTheoryData("Invalid_CustomToken_NotAddedAsValidTokenType")
{
CustomTokenType = "PPT",
TokenValidationParameters = CreateTokenValidationParameters(),
ValidationParameters = CreateValidationParameters(),
ExpectedIsValid = false,
ExpectedException = ExpectedException.SecurityTokenInvalidTypeException("IDX10257:"),
});

theoryData.Add(new ValidateTokenAsyncTokenTypeTheoryData("Invalid_EmptyTokenType")
{
UseEmptyType = true,
TokenValidationParameters = CreateTokenValidationParameters(),
ValidationParameters = CreateValidationParameters(),
ExpectedIsValid = false,
ExpectedException = ExpectedException.SecurityTokenInvalidTypeException("IDX10256:"),
});

return theoryData;

static TokenValidationParameters CreateTokenValidationParameters(string? validTokenType = "JWT")
{
// only validate the signature and issuer signing key
var tokenValidationParameters = new TokenValidationParameters()
{
ValidateAudience = false,
ValidateIssuer = false,
ValidateLifetime = false,
ValidateTokenReplay = false,
ValidateIssuerSigningKey = false,
RequireSignedTokens = false,
};

if (validTokenType is not null)
tokenValidationParameters.ValidTypes = [validTokenType];

return tokenValidationParameters;
}

static ValidationParameters CreateValidationParameters(string? validTokenType = "JWT")
{
ValidationParameters validationParameters = new ValidationParameters();

// Skip all validations except token type
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;

if (validTokenType is not null)
validationParameters.ValidTypes.Add(validTokenType);

return validationParameters;
}
}
}

public class ValidateTokenAsyncTokenTypeTheoryData : ValidateTokenAsyncBaseTheoryData
{
public ValidateTokenAsyncTokenTypeTheoryData(string testId) : base(testId) { }

public bool UseEmptyType { get; set; } = false;

public string? CustomTokenType { get; set; } = null;
}

// Custom JWT with empty string for type
private static string emptyTypeJWT = "eyJ0eXAiOiIiLCJhbGciOiJub25lIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTcyOTY5NDI1OCwiZXhwIjoxNzI5Njk3ODU4fQ.";

private static string CreateTokenForTokenTypeValidation(bool useEmptyType = false, string? tokenType = null)
{
if (useEmptyType)
return emptyTypeJWT;

JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler();

SecurityTokenDescriptor securityTokenDescriptor = new SecurityTokenDescriptor
{
Subject = Default.ClaimsIdentity,
TokenType = tokenType ?? "JWT",
};

return jsonWebTokenHandler.CreateToken(securityTokenDescriptor);
}
}
}
#nullable restore
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public void SetValidators_NullValue_ThrowsArgumentNullException()
Assert.Throws<ArgumentNullException>(() => validationParameters.IssuerValidatorAsync = null);
Assert.Throws<ArgumentNullException>(() => validationParameters.TokenReplayValidator = null);
Assert.Throws<ArgumentNullException>(() => validationParameters.LifetimeValidator = null);
Assert.Throws<ArgumentNullException>(() => validationParameters.TypeValidator = null);
Assert.Throws<ArgumentNullException>(() => validationParameters.TokenTypeValidator = null);
Assert.Throws<ArgumentNullException>(() => validationParameters.AudienceValidator = null);
Assert.Throws<ArgumentNullException>(() => validationParameters.IssuerSigningKeyValidator = null);
}
Expand Down
Loading