Skip to content

Commit

Permalink
JsonWebTokenHandler to return the JsonWebToken on validation failure (#…
Browse files Browse the repository at this point in the history
…1989)

* return the security token when the validation fails if validationParameters.IncludeTokenOnFailedValidation is set

* resolved review comments

* return the security token when the validation fails if validationParameters.IncludeTokenOnFailedValidation is set

* fixed failed tests

* define a constant
  • Loading branch information
dannybtsai authored Jan 18, 2023
1 parent 73691c9 commit 2d18df9
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1325,7 +1325,8 @@ private async Task<TokenValidationResult> ValidateJWSAsync(
return new TokenValidationResult
{
Exception = ex,
IsValid = false
IsValid = false,
TokenOnFailedValidation = validationParameters.IncludeTokenOnFailedValidation ? jsonWebToken : null
};
}
}
Expand Down Expand Up @@ -1363,7 +1364,8 @@ private async Task<TokenValidationResult> ValidateJWEAsync(
return new TokenValidationResult
{
Exception = ex,
IsValid = false
IsValid = false,
TokenOnFailedValidation = validationParameters.IncludeTokenOnFailedValidation ? jwtToken : null
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ protected TokenValidationParameters(TokenValidationParameters other)
ConfigurationManager = other.ConfigurationManager;
CryptoProviderFactory = other.CryptoProviderFactory;
DebugId = other.DebugId;
IncludeTokenOnFailedValidation = other.IncludeTokenOnFailedValidation;
IgnoreTrailingSlashWhenValidatingAudience = other.IgnoreTrailingSlashWhenValidatingAudience;
IssuerSigningKey = other.IssuerSigningKey;
IssuerSigningKeyResolver = other.IssuerSigningKeyResolver;
Expand Down Expand Up @@ -427,6 +428,10 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken,
[DefaultValue(true)]
public bool IgnoreTrailingSlashWhenValidatingAudience { get; set; } = true;

/// <summary>
/// Gets or sets the flag that indicates whether to include the <see cref="SecurityToken"/> when the validation fails.
/// </summary>
public bool IncludeTokenOnFailedValidation { get; set; } = false;

/// <summary>
/// Gets or sets a delegate for validating the <see cref="SecurityKey"/> that signed the token.
Expand Down
5 changes: 5 additions & 0 deletions src/Microsoft.IdentityModel.Tokens/TokenValidationResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ public bool IsValid
/// </summary>
public SecurityToken SecurityToken { get; set; }

/// <summary>
/// The <see cref="SecurityToken"/> to be returned when validation fails.
/// </summary>
public SecurityToken TokenOnFailedValidation { get; internal set; }

/// <summary>
/// Gets or sets the <see cref="CallContext"/> that contains call information.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3358,6 +3358,77 @@ public static TheoryData<CreateTokenTheoryData> SecurityKeyNotFoundExceptionTest
},
};
}

[Theory, MemberData(nameof(IncludeSecurityTokenOnFailureTestTheoryData))]
public void IncludeSecurityTokenOnFailedValidationTest(CreateTokenTheoryData theoryData)
{
var context = TestUtilities.WriteHeader($"{this}.IncludeSecurityTokenOnFailedValidationTest", theoryData);

try
{
var handler = new JsonWebTokenHandler();
var token = handler.CreateToken(theoryData.TokenDescriptor);
var validationResult = handler.ValidateToken(token, theoryData.ValidationParameters);
if (theoryData.ValidationParameters.IncludeTokenOnFailedValidation)
{
Assert.NotNull(validationResult.TokenOnFailedValidation);
}
else
{
Assert.Null(validationResult.TokenOnFailedValidation);
}
}
catch (Exception ex)
{
theoryData.ExpectedException.ProcessException(ex, context);
}

TestUtilities.AssertFailIfErrors(context);
}

public static TheoryData<CreateTokenTheoryData> IncludeSecurityTokenOnFailureTestTheoryData()
{
return new TheoryData<CreateTokenTheoryData>()
{
new CreateTokenTheoryData
{
First = true,
TestId = "TokenExpiredIncludeTokenOnFailedValidation",
TokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(Default.PayloadClaimsExpired),
Expires = DateTime.UtcNow.Subtract(new TimeSpan(0, 10, 0)),
IssuedAt = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)),
NotBefore = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)),
SigningCredentials = Default.AsymmetricSigningCredentials,
},
ValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = Default.SymmetricSigningKey,
ValidIssuer = Default.Issuer,
IncludeTokenOnFailedValidation = true
}
},
new CreateTokenTheoryData
{
First = true,
TestId = "TokenExpiredNotIncludeTokenOnFailedValidation",
TokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(Default.PayloadClaimsExpired),
Expires = DateTime.UtcNow.Subtract(new TimeSpan(0, 10, 0)),
IssuedAt = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)),
NotBefore = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0)),
SigningCredentials = Default.AsymmetricSigningCredentials,
},
ValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = Default.SymmetricSigningKey,
ValidIssuer = Default.Issuer,
}
},
};
}
}

public class CreateTokenTheoryData : TheoryDataBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ namespace Microsoft.IdentityModel.Tokens.Tests
{
public class TokenValidationParametersTests
{
int ExpectedPropertyCount = 57;

[Fact]
public void Publics()
{
TokenValidationParameters validationParameters = new TokenValidationParameters();
Type type = typeof(TokenValidationParameters);
PropertyInfo[] properties = type.GetProperties();
if (properties.Length != 56)
Assert.True(false, "Number of properties has changed from 56 to: " + properties.Length + ", adjust tests");
if (properties.Length != ExpectedPropertyCount)
Assert.True(false, $"Number of properties has changed from {ExpectedPropertyCount} to: " + properties.Length + ", adjust tests");

TokenValidationParameters actorValidationParameters = new TokenValidationParameters();
SecurityKey issuerSigningKey = KeyingMaterial.DefaultX509Key_2048_Public;
Expand Down Expand Up @@ -157,8 +159,8 @@ public void GetSets()
TokenValidationParameters validationParameters = new TokenValidationParameters();
Type type = typeof(TokenValidationParameters);
PropertyInfo[] properties = type.GetProperties();
if (properties.Length != 56)
Assert.True(false, "Number of public fields has changed from 56 to: " + properties.Length + ", adjust tests");
if (properties.Length != ExpectedPropertyCount)
Assert.True(false, $"Number of public fields has changed from {ExpectedPropertyCount} to: " + properties.Length + ", adjust tests");

GetSetContext context =
new GetSetContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public void GetSets()
TokenValidationResult tokenValidationResult = new TokenValidationResult();
Type type = typeof(TokenValidationResult);
PropertyInfo[] properties = type.GetProperties();
if (properties.Length != 9)
Assert.True(false, "Number of public fields has changed from 9 to: " + properties.Length + ", adjust tests");
if (properties.Length != 10)
Assert.True(false, "Number of public fields has changed from 10 to: " + properties.Length + ", adjust tests");

GetSetContext context =
new GetSetContext
Expand Down

0 comments on commit 2d18df9

Please sign in to comment.