-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
JwtSecurityTokenHandler token expiration date validation fails if date more than 25 years #63001
Comments
Tagging subscribers to this area: @dotnet/area-system-security, @vcsjones, @krwq Issue DetailsDescription
Reproduces on .NET 6 with more than 25 years expiration dates. On .NET 5 fails around dates with 100 years more than now. Reproduction StepsToken issuing var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new(ClaimTypes.NameIdentifier, /* some data */)
},
Expires = DateTime.UtcNow.AddYears(26 /* or other number more than 25 years on .NET 6 */),
/* signing key, issuer, etc */
}; Token validationidentity = tokenHandler.ValidateToken(jwtToken, new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(signingKey)
}, out _); Issued token (from jwt.io)Expected behaviorValidation must be successful. Actual behaviorValidation code above throws Regression?No response Known WorkaroundsNo response ConfigurationSDK: .NET 6 (but bug can be reproduced on .NET 5 and further) Other informationNo response
|
Hello, can you please clarify whether the behavior exists in 5.0, what does the exact same code do in 5.0? Linking this here https://stackoverflow.com/questions/43593074/jwt-validation-fails/46654832#46654832 |
I'm unable to reproduce this. Here is a fully running example: using System;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.JsonWebTokens;
static class Program {
static void Main()
{
byte[] signingKey = new byte[32];
SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor {
Subject = new ClaimsIdentity(new Claim[] {
new Claim(ClaimTypes.NameIdentifier, "Kevin")
}),
Expires = DateTime.UtcNow.AddYears(100),
Issuer = "vcsjones",
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(signingKey),
SecurityAlgorithms.HmacSha256),
};
JsonWebTokenHandler handler = new();
string token = handler.CreateToken(tokenDescriptor);
Console.WriteLine(token);
TokenValidationResult result = handler.ValidateToken(token,
new TokenValidationParameters {
ValidateAudience = false,
ValidateIssuer = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuers = new string[] { "vcsjones" },
IssuerSigningKey = new SymmetricSecurityKey(signingKey)
}
);
Console.WriteLine(result.IsValid);
}
} <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="6.15.0" />
</ItemGroup>
</Project> For me, this prints "true" at the end with an expiration 100 years in the future. Can you share a complete code example that reproduces the issue, including the .csproj? |
This issue has been marked |
@vcsjones, my fault, i did not paste a token handler type 😅 I am using I've already send a complete part of code, so here is a csproj (most likely, you will not see anything strange here) <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Deterministic>false</Deterministic>
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
<RootNamespace>BlaBlaBla</RootNamespace>
<AssemblyName>BlaBlaBla</AssemblyName>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\ProductSettings.cs" Link="Properties\ProductSettings.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Ensure.That" Version="10.1.0" />
<PackageReference Include="FluentMigrator" Version="3.3.1" />
<PackageReference Include="FluentMigrator.Runner" Version="3.3.1" />
<PackageReference Include="FluentMigrator.Runner.Postgres" Version="3.3.1" />
<PackageReference Include="linq2db" Version="4.0.0-preview.8" />
<PackageReference Include="linq2db.PostgreSQL" Version="4.0.0-preview.8" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
<PackageReference Include="MongoDB.Driver" Version="2.13.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Npgsql" Version="5.0.10" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="YamlDotNet" Version="11.2.1" />
</ItemGroup>
</Project> |
@yoyrandao Thanks for the update. I can reproduce the behavior, but I don't see a difference between .NET 5 and .NET 6.
Given that, I would expect 75 years to fail on .NET 6, and work on .NET 5. However when I try it, it fails for both: Based on your description, it sounds like this is failing right around the Unix Epoch 32-bit overflow, the Year 2038 problem. Indeed, this seems to be the case. This will work: DateTime unixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
byte[] signingKey = new byte[32];
SecurityTokenDescriptor tokenDescriptor = new() {
Subject = new ClaimsIdentity(new Claim[] {
new Claim(ClaimTypes.NameIdentifier, "Kevin")
}),
Expires = unixEpoch.AddSeconds(int.MaxValue),
Issuer = "vcsjones",
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(signingKey),
SecurityAlgorithms.HmacSha256),
};
JsonWebTokenHandler handler = new();
string token = handler.CreateToken(tokenDescriptor); and this won't: DateTime unixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
byte[] signingKey = new byte[32];
SecurityTokenDescriptor tokenDescriptor = new() {
Subject = new ClaimsIdentity(new Claim[] {
new Claim(ClaimTypes.NameIdentifier, "Kevin")
}),
Expires = unixEpoch.AddSeconds(int.MaxValue).AddSeconds(1),
// ^ Overflow unix epoch
Issuer = "vcsjones",
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(signingKey),
SecurityAlgorithms.HmacSha256),
}; Essentially, I don't know enough about the internals of Can you confirm my findings, that the behavior is not dependent on the .NET version? If you are still able to reproduce differing behavior between .NET 5 and .NET 6, can you adapt my example below to reproduce the issue? Here is the complete code I used to reproduce my findings: using System;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.JsonWebTokens;
using System.IdentityModel.Tokens.Jwt;
static class Program {
static void Main()
{
DateTime unixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
RunExample(unixEpoch.AddSeconds(int.MaxValue));
Console.WriteLine("-------------");
RunExample(unixEpoch.AddSeconds(int.MaxValue).AddSeconds(1));
static void RunExample(DateTime expires)
{
Console.WriteLine($"Expires: {expires}");
byte[] signingKey = new byte[32];
SecurityTokenDescriptor tokenDescriptor = new() {
Subject = new ClaimsIdentity(new Claim[] {
new Claim(ClaimTypes.NameIdentifier, "Kevin")
}),
Expires = expires,
Issuer = "vcsjones",
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(signingKey),
SecurityAlgorithms.HmacSha256),
};
JsonWebTokenHandler handler = new();
string token = handler.CreateToken(tokenDescriptor);
JwtSecurityTokenHandler validationHandler = new();
try
{
ClaimsPrincipal result = validationHandler.ValidateToken(token,
new TokenValidationParameters {
ValidateAudience = false,
ValidateIssuer = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuers = new string[] { "vcsjones" },
IssuerSigningKey = new SymmetricSecurityKey(signingKey)
}, out SecurityToken securityToken
);
Console.WriteLine($"Token is valid and expires at {securityToken.ValidTo}");
}
catch
{
Console.WriteLine("Could not validate token.");
}
}
}
} <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net5.0;net6.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="6.15.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.15.0" />
</ItemGroup>
</Project> Run: |
This issue has been marked |
@vcsjones yeah, I can confirm. I test it again ang get the same result. Thanks for helping me, my solution is using |
Looks like this has previously been reported here: AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet#92 |
Since the class with the issue is in a different repository, and they already have an issue for it, closing this one. |
Description
JwtSecurityTokenHandler.ValidateToken
fails on expire date validation if it is more than 25 years.Reproduces on .NET 6 with more than 25 years expiration dates. On .NET 5 fails around dates with 100 years more than now.
Reproduction Steps
Token issuing
Token validation
Issued token (from jwt.io)
Expected behavior
Validation must be successful.
Actual behavior
Validation code above throws
SecurityTokenNoExpirationException
with messageIDX10225: Lifetime validation failed. The token is missing an Expiration Time. Tokentype: 'System.String'.
.Regression?
No response
Known Workarounds
No response
Configuration
SDK: .NET 6 (but bug can be reproduced on .NET 5 and further)
OS: Windows 11 21H2 x64 (22000.376)
Other information
No response
The text was updated successfully, but these errors were encountered: