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

JsonWebTokenHandler to return the JsonWebToken on validation failure #1989

Merged
merged 5 commits into from
Jan 18, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -1319,7 +1319,8 @@ private TokenValidationResult ValidateJWS(JsonWebToken jsonWebToken, TokenValida
return new TokenValidationResult
{
Exception = ex,
IsValid = false
IsValid = false,
TokenOnFailedValidation = validationParameters.IncludeTokenOnFailedValidation ? jsonWebToken : null
};
}
}
Expand Down Expand Up @@ -1354,7 +1355,8 @@ private TokenValidationResult ValidateJWE(JsonWebToken jwtToken, TokenValidation
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we define a constant instead of hard-coding it?

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