79

I'm building a .net core web api.

Preface - I've implemented token authentication as per https://stormpath.com/blog/token-authentication-asp-net-core and https://dev.to/samueleresca/developing-token-authentication-using-aspnet-core. I've also read a few issues on github and here on SO.

This also came in handy https://goblincoding.com/2016/07/24/asp-net-core-policy-based-authorisation-using-json-web-tokens/.

After implementing it all I'm feeling like I'm missing something.

I've created a simple Angular application that sits in a web client. When I authenticate, client is sent a token. I'm storing that in session for now (still in dev so will address security concerns around where to store it later).

Not really sure this (JWT (JSON Web Token) automatic prolongation of expiration) is useful as I haven't implemented refresh tokens as far as I can see.

I noticed that when I call logout, and then log back in again, the client is sent a new token - as expected. However, if the token expiry time is passed (I set it to 1 minute for testing) and then the page is refreshed, the token seems to remain the same in my app. i.e. it's as if the token never expires?!

I would have expected the client to be returned a 401 Unauthorised error and I can then handle forcing the user to re-authenticate.

Is this not how this should work? Is there some auto-refresh token magic going on in the background that is default (I haven't set up any notion of refresh tokens in the tutorials explicitly)? Or am I missing something about the concept of token auth?

Also - if this is a perpetually refreshing token, should I be concerned about security if the token was ever compromised?

Thanks for your help

5 Answers 5

157

I believe this has to do with ClockSkew in JwtBearerOptions.

Change to TimeSpan.Zero as I think the default is set to 5 minutes (not 100% sure though).

I have posted some sample code below that is to be placed in Startup.cs => Configure.

        app.UseJwtBearerAuthentication(new JwtBearerOptions()
        {
            AuthenticationScheme = "Jwt",
            AutomaticAuthenticate = true,
            AutomaticChallenge = true,
            TokenValidationParameters = new TokenValidationParameters()
            {
                ValidAudience = Configuration["Tokens:Audience"],
                ValidIssuer = Configuration["Tokens:Issuer"],
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"])),
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero
            }
        });
Sign up to request clarification or add additional context in comments.

2 Comments

Agreed the default ClockSkew is set to 5 minutes. If you run in debug, and then inspect, you will be able to see it.
29

If your expiry time is well over the default (5 mins) or over a set a time like I had and it still considers expired token as valid, and setting the ClockSkew to TimeSpan.Zero has no effect, make sure you have the property

ValidateLifetime 

set to true as I had mine set to false causing the problem, which totally make sense, but it was an easy oversight.

services.AddAuthentication(option =>
    {
        option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["JwtToken:Issuer"],
            ValidAudience = Configuration["JwtToken:Issuer"],
            IssuerSigningKey = new SymmetricSecurityKey(
               Encoding.UTF8.GetBytes(Configuration["JwtToken:SecretKey"]))
        };
    });

2 Comments

For me, I needed both ValidateLifetime AND ClockSkew using Core 2.2
I can confirm that this works in NetCore3.1. Funny that this is not really documented in the docu: learn.microsoft.com/en-us/dotnet/api/…. The default is 5 minutes (Check learn.microsoft.com/en-us/dotnet/api/…).
21

There is an additional delay of 5 minutes in the library itself.

If you are setting 1 minute as indicated for expiration, the total will be 6 minutes. If you set 1 hour the total will be 1 hour and 5 minutes.

5 Comments

what do you mean with additional delay of 5 minutes in the library itself? Do you mean additional to the already mentioned ClockSkew?
He means the already mentioned ClockSkew I would say.
Maybe, but there's already a good and clear answer about the ClockSkew, so why do we need an additonal, quite unclear answer?
Yes, I'm talking about ClockSkew, it was a way that I found to exemplify.
1

In my case, I added a new SecurityTokenDescriptor which contains properties that take the current date and time and expires based on our requirements. Below is a sample login controller with a post request, which in turn returns the user details with a token.

        public async Task<ActionResult<UserWithToken>> Login([FromBody] User user)
        {
             user = await _context.Users
                                    .Include(u => u.Reservations)
                                .Where(u => u.Email == user.Email
                                   && u.Password == user.Password)
                                .FirstOrDefaultAsync();

            if (user == null)
            {
                return NotFound();
            }

            UserWithToken userWithToken = new UserWithToken(user);

            if (userWithToken == null)
            {
                return NotFound();
            }

            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(_jwtsettings.SecretKey);
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new Claim[]
                {
                    new Claim(ClaimTypes.Name, user.Email)
                }),
                Expires = DateTime.UtcNow.AddMinutes(10),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
                SecurityAlgorithms.HmacSha256Signature)
            };
            var token = tokenHandler.CreateToken(tokenDescriptor);
            userWithToken.Token = tokenHandler.WriteToken(token);

            return userWithToken;
        }

Here token will expires in 10 minutes.

Comments

1

JWT Often Add additional 5 minutes to expiry.

If you generate a token with expiry of 5 minutes from now, it will add additional 5 minutes to it.

exp: 5+5 = 10

exp: 60+5 = 65

Example:

var token = new JwtSecurityToken(
    issuer: any issuer,
    claims: claims,
    expires: DateTime.UtcNow.AddMinutes(5),
    signingCredentials: credentials
);

Tthis will create for 10 minutes. I hope it will help you and dont forget to set ValidateLifetime = true, in program.cs (if using .NET 6 or later).

2 Comments

It's configurable parameter of the token validation. It's named ClockSkew, accepts TimeSpan. Link to the resource: learn.microsoft.com/en-us/dotnet/api/…
Try to add details to your answer and how it helps solve the problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.