21

I am generating JWT's to use with my WebApi project. I'm set the token to expire in one minute so that I can test if it rejects the token when submitted after the expiration date.

CreateToken Controller

public async Task<IActionResult> CreateToken([FromBody] CredentialModel model)
{
    var user = await _unitOfWork.UserManager.FindByNameAsync(model.UserName);

    if (user == null) return BadRequest();
    if (Hasher.VerifyHashedPassword(user, user.PasswordHash, model.Password) !=
        PasswordVerificationResult.Success) return BadRequest();

    var userClaims = await UserManager.GetClaimsAsync(user);

    var claims = new[]
    {
        new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString()),
        new Claim(JwtRegisteredClaimNames.GivenName, user.FirstName), 
        new Claim(JwtRegisteredClaimNames.FamilyName, user.LastName),
        new Claim(JwtRegisteredClaimNames.Email, user.Email)
    }
    .Union(userClaims);

    var cert = new Certificate(Configuration["Tokens:Certificate"]);
    var token = new JwtSecurityToken(
        issuer: Configuration["Tokens:Issuer"],
        audience: Configuration["Tokens:Audience"],
        claims: claims,
        expires: DateTime.UtcNow.AddMinutes(1),
        signingCredentials: cert.Signature
    );

    return Ok(new
    {
        token = new JwtSecurityTokenHandler().WriteToken(token),
        expiration = token.ValidTo
    });
}

Token Authentication - Startup Class

app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    TokenValidationParameters = new TokenValidationParameters()
    {
        ValidIssuer = Configuration["Tokens:Issuer"],
        ValidAudience = Configuration["Tokens:Audience"],
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new Certificate(Configuration["Tokens:Certificate"]).SecurityKey,
        ValidateLifetime = true
    },
});

Although I am setting validateLifetime = true the tokes are not rejected two minutes later. It will keep accepting the token. Is there a minimum expiration time that I am not aware of or is my setup wrong?

Dblock247
  • 4,603
  • 6
  • 35
  • 61
  • I don't suppose that you have a runnable version of this code, do you? In any case, I looked up documentation for `UseJwtBearerAuthentication` and it looks like it has been deprecated :/ https://github.com/aspnet/Security/blob/99aa3bd35dd5fbe46a93eef8a2c8ab1f9fe8d05b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerAppBuilderExtensions.cs – Maria Ines Parnisari May 30 '17 at 01:30
  • @MariaInesParnisari yes my code is running and the issuing and validating of the token works. It's pretty much all there except for the small Certificate class that I wrote to import the asymmetric X509Certificate2. It Just doesn't seem to always validate the expiration. I looked at your link and it does seem to be deprecated. But I don't see what replaces it. – Dblock247 May 30 '17 at 01:38
  • I actually meant if this code is hosted in GitHub so I can download it and try it myself. When you say that validation works, have you tried manually altering any other of the properties of the token (e.g. the audience) to see that it fails? – Maria Ines Parnisari May 30 '17 at 01:51
  • 1
    No its in a private repo for a customer. I actually just stumbled over the solution here. https://stormpath.com/blog/token-authentication-asp-net-core. The is a time Clockskew property that allows for a certain amount of clock drift. I assume there is a default drift value. If I set it to TimeSpan.Zero it works perfect. I'm still worried about the deprecation. I guess I will have to search for its replacement. – Dblock247 May 30 '17 at 01:55

1 Answers1

27

I stumbled over the answer here if anyone is interested.

app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    TokenValidationParameters = new TokenValidationParameters()
    {
        ValidIssuer = Configuration["Tokens:Issuer"],
        ValidAudience = Configuration["Tokens:Audience"],
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new Certificate(certPath: Configuration["Tokens:Certificate"], isValid: false).SecurityKey,
        ValidateLifetime = true,
        ValidateIssuer = true,
        ValidateAudience = true,
        ClockSkew = TimeSpan.Zero
    },
});
Dblock247
  • 4,603
  • 6
  • 35
  • 61
  • 5
    Be careful with a clockskew set to Zero. You might run into issues with some clients. – Cyprien Autexier May 30 '17 at 02:01
  • @CyprienAutexier While that's true, a large expiration window likely wouldn't have an issue. For the opposite situation: with a small expiration window, you probably *do* want to restrict clock skew if you're strict about how quickly the token may expire – Rob May 30 '17 at 03:20
  • @CyprienAutexier the 1 minute expiration was only for testing. I will increase in the production. Any recommended values for Clockskew and expiration length. – Dblock247 May 30 '17 at 03:57
  • @Dblock247 I never had any issues with the default value which is I think 5 minutes. But I don't pretend to be a security expert. – Cyprien Autexier Jun 16 '17 at 12:57
  • @CyprienAutexier thanks for the info. How long do you set your expiration time for? – Dblock247 Jun 16 '17 at 17:03
  • @Dblock247 This one is really tricky and depends on what's protected. I've had cases where I had very short times (5 min) and other with longer times. I think it depends on the revocation needs you might have and on the way you implement it. – Cyprien Autexier Jun 17 '17 at 12:01
  • 2
    You could always do `ClockSkew = Debugger.IsAttached ? TimeSpan.Zero : TimeSpan.FromMinutes(10)` so during debugging you don't need to deal with it - assuming you're hitting your own machine or one whose time you know to be correct. But if you forget to change it back you won't get in trouble later. – Simon_Weaver Feb 04 '19 at 06:55
  • @CyprienAutexier what are the issues that can arise with `ClockSkew` set to zero? – Alejo Feb 25 '19 at 21:48
  • @Ajejo nbf (not before) can cause issues: one machine verifying the token is lagging by say a couple seconds (and it's quite common) the token will fail to be verified as it will be considered not yet valid for instance. If you have a retry mechanism to acquire a new token it will just loop on it and always fail (hopefully you have a backoff retry and it won't be a ddos combo). Also, if you have short lived token, it can cause issues. if people rely on their supposed lifetime and there a lock mismatch between servers. – Cyprien Autexier Feb 25 '19 at 22:52
  • little note - default value for ClockSkew is 5 minutes – Sam Sch Jul 24 '19 at 11:42