ASP.NET Core 8.0 JWT Validation Issue: SecurityTokenNoExpirationException Despite Valid Token
Asked Answered
D

2

5

I'm working on an ASP.NET Core 8.0 Web API project with a layered architecture. I've encountered an issue during the JWT validation process in my authentication and registration endpoints.

In my program.cs, I have the following token validation parameters set up:

var tokenValidationParameters = new TokenValidationParameters()
 {
  ValidateIssuer = true,
  ValidateAudience = true,
  ValidAudience = jwtSettings.ValidAudience,
  ValidIssuer = jwtSettings.ValidIssuer,
  IssuerSigningKey = new 
  SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtSettings.Secret)),
  ClockSkew = jwtSettings.TokenLifetime
 };

builder.Services.AddAuthentication(options =>
{
  options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
  options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
  options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
  .AddJwtBearer(options =>
  {
    options.SaveToken = true;
    options.RequireHttpsMetadata = false;
    options.TokenValidationParameters = tokenValidationParameters;
 
    options.Events = new JwtBearerEvents
    {
        OnMessageReceived = context =>
        {
            context.Token = context.Request.Cookies["authorization"];
            return Task.CompletedTask;
        }
      };
   });

builder.Services.AddSingleton(tokenValidationParameters);

For token generation, I use the following method:

public async Task<Response<RefreshTokenDto>> GenerateAuthResultForCustomAsync(Customer 
customer)
{
  try
  {
     var tokenHandler = new JwtSecurityTokenHandler();
     var key = Encoding.ASCII.GetBytes(_jwtSettings.Secret);

     var userRoles = await _userManager.GetRolesAsync(customer);

     var authClaims = new List<Claim>
                       {
                           new Claim(JwtRegisteredClaimNames.Sub, customer.Email),
                           new Claim(JwtRegisteredClaimNames.Jti, 
Guid.NewGuid().ToString()),
                           new Claim("customerId", customer.Id),
                           new Claim("firstName", customer.FirstName),
                           new Claim("lastName", customer.LastName),
                           new Claim("countryId", customer.CountryId.ToString()),
                           new Claim("phoneNumber", customer.PhoneNumber),
                           new Claim("userName", customer.UserName)
                       };

     authClaims.AddRange(userRoles.Select(role => new Claim(ClaimTypes.Role, role)));

     var tokenDescriptor = new SecurityTokenDescriptor()
     {
         Subject = new ClaimsIdentity(authClaims),
         Issuer = _jwtSettings.ValidIssuer,
         Audience = _jwtSettings.ValidAudience,
         Expires = _dateTimeProvider.Now.Add(_jwtSettings.TokenLifetime).UtcDateTime,
         SigningCredentials = new SigningCredentials(
                                       new SymmetricSecurityKey(key),
                                       SecurityAlgorithms.HmacSha256Signature)
     };

     var token = tokenHandler.CreateToken(tokenDescriptor);

}

Lastly, the method that validates the token is:

public class PrincipalTokenService : IPrincipalTokenService
{
  private readonly TokenValidationParameters _tokenValidationParameters;

  public PrincipalTokenService(TokenValidationParameters tokenValidationParameters)
  {
    _tokenValidationParameters = tokenValidationParameters;
  }

  public ClaimsPrincipal GetPrincipalFromToken(string token)
  {
    var tokenHandler = new JwtSecurityTokenHandler();

    try
    {
        var handler = tokenHandler.ValidateToken(
            token,
            _tokenValidationParameters,
            out var validatedToken);

        return !IsJwtWithValid(validatedToken) ? null : handler;
    }
    catch
    {
        return null;
    }
  }

}

When executing GetPrincipalFromToken, I get the following error:

Microsoft.IdentityModel.Tokens.SecurityTokenNoExpirationException: 'IDX10225: Lifetime validation failed. The token is missing an Expiration Time. Tokentype: 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken'

However, removing the expiration date from the token and disabling its validation in the configuration leads to similar issues with the issuer, and subsequently with the audience. Despite these validations, the generated token is valid as confirmed by jwt.io.

The entire token generation and validation logic was ported from a working project in ASP.NET Core 6.0, which has been functioning without issues for over 3 years. The server environment and configuration have remained unchanged, suggesting the code itself is not the issue.

I suspect the problem might be related to package versions. Here are the relevant packages used in the 8.0 project:

<PackageReference Include="Asp.Versioning.Mvc" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.3">
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.3" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="7.4.1 />

Has anyone else encountered similar issues with JWT validation in ASP.NET Core 8.0? Any insights or suggestions would be greatly appreciated.

P.S. Additionally, I'd like to clarify that the jwtSettings is a model used to populate the tokenValidationParameters with data from the application settings. The generated token, when verified on jwt.io, appears correct in all respects - the exp field contains the correct time, the iss field is present, and all the data within the token is accurate. I've even attempted to bypass the jwtSettings model entirely by hardcoding the parameters directly into tokenValidationParameters, but I encounter the same issue, which leads me to suspect that the problem might be within the package handling ValidateToken itself.

Dato answered 19/3, 2024 at 20:51 Comment(10)
What's your jwtSettings.TokenLifetime and ClockSkew = jwtSettings.TokenLifetime ? Have a try ValidateLifetime = false in your tokenValidationParameters . If you added minimal reproducible example, it would be easier to tell. I'm not sure, at this stage.Markham
Have a try to remove ClockSkew = jwtSettings.TokenLifetime and set Expires = DateTime.UtcNow.AddMinutes(5).Markham
@QingGuo Thank you for your suggestions. I've already tried setting ValidateLifetime = false, but then I encounter an issue with the issuer, and after removing the issuer validation, the problem appears with the audience. It seems like removing or bypassing one validation check just leads to another issue.Dato
@QingGuo Regarding the ClockSkew and TokenLifetime, I initially set the ClockSkew to match the TokenLifetime to ensure some consistency in the token's validity period, but removing the ClockSkew didn't resolve the issue either.Dato
@QingGuo jwtSettings is a model used to populate the tokenValidationParameters with data from the application settings. The generated token, when verified on jwt.io, appears correct in all respects - the exp field contains the correct time, the iss field is present, and all the data within the token is accurate.I've even attempted to bypass the jwtSettings model entirely by hardcoding the parameters directly into tokenValidationParameters, but I encounter the same issue, which leads me to suspect that the problem might be within the package handling ValidateToken itself.Dato
@QingGuo As for a minimal reproducible example, I'm not entirely sure how to condense the code further while still highlighting the issue, but I'll work on extracting the most relevant parts that contribute to this problemDato
In my project I don't have the package "Microsoft.IdentityModel.Tokens" Version="7.4.1 , try to remove it and rebuid the project.Markham
@QingGuo, you were absolutely right. Removing the "Microsoft.IdentityModel.Tokens" Version="7.4.1" package from my project and rebuilding it resolved the issue I was facing with token validation. I really appreciate your help and insight on this matter. It saved me a lot of time and frustration. Thank you!Dato
@QingGuo your suggestion was spot-on and resolved my issue. Could you please post your advice about removing the "Microsoft.IdentityModel.Tokens" package as an answer instead of a comment? I'd like to mark it as the accepted solution to help others who might face the same issue. Thank you again for your valuable input!Dato
When removing Microsoft.IdentityModel.Tokens, creating a new SymmetricSecurityKey stops working. This is because SymmetricSecurityKey is part of that library.Interceptor
M
16

Remove the package Microsoft.IdentityModel.Tokens" Version="7.4.1, then rebuild the project.

Markham answered 20/3, 2024 at 8:6 Comment(3)
wow! spent hours debugging this, thanks!Avi
WOW why is this a thing?! also spent hours debugging this!Edric
OMG! Also spent hours to discover the problem it was on this package. Thanks a lot!Redness
M
1

I got the very same exception, and was able to resolve it by adding Microsoft.IdentityModel.Protocols.OpenIdConnect to my package references.

Macmacabre answered 17/5, 2024 at 11:7 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.