2

I am issuing JWT bearer tokens from a .net core API that is then being used by a mobile app. I'm seeing an issue where the Expiry time seems to be ignored - until I restart the API.

This is all using the Microsoft packages (e.g. not a 3rd party like IdentityServer etc.)

In my Startup.cs I have the following to configure the Token:

    services.AddAuthentication(auth =>
        {
            auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidAudience = Configuration["AuthSettings:Audience"],
                ValidIssuer = Configuration["AuthSettings:Issuer"],
                RequireExpirationTime = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["AuthSettings:Key"])),
                ValidateIssuerSigningKey = true,
                RequireSignedTokens = true,
                ValidateLifetime = true
            };
        });

To issue the Token, I have the following code:

public async Task<UserManagerResponse> LoginUserAsync(LoginViewModel model)
    {
        var user = await _userManager.FindByEmailAsync(model.Email);

        if (user == null)
        {
            return new UserManagerResponse
            {
                Message = "There is no user with that Email address",
                IsSuccess = false,
            };
        }

        var result = await _userManager.CheckPasswordAsync(user, model.Password);

        if (!result)
            return new UserManagerResponse
            {
                Message = "Invalid password",
                IsSuccess = false,
            };

        var claims = new[]
        {
            new Claim("Email", model.Email),
            new Claim(ClaimTypes.NameIdentifier, user.Id),
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["AuthSettings:Key"]));

        var token = new JwtSecurityToken(
            issuer: _configuration["AuthSettings:Issuer"],
            audience: _configuration["AuthSettings:Audience"],
            claims: claims,
            expires: DateTime.Now.AddSeconds(10), //.AddMinutes(1),
            signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)) ;

        string tokenAsString = new JwtSecurityTokenHandler().WriteToken(token);

        return new UserManagerResponse
        {
            Message = tokenAsString,
            IsSuccess = true,
            ExpireDate = token.ValidTo
        };
    }

Decoding the Token on jwt.io for example does show the correct expiry time (e.g. 10 seconds, or 1 minute etc).

However when calling the Api with the expired token, I still get a 200 OK response as if the token was still valid. When I then restart the Api, and call it using the expired token, I then get the expected 401 Unauthorized. Currently running the Api via Visual Studio on IIS Express. It's .net core 3.1.

Struggling to see where to go next here. I had added "ValidatedLifetime = true" which doesn't seem to have done anything.

Thanks,

Steve.

Steve
  • 729
  • 14
  • 29
  • Yep - that is what it was. When i set ClockSkew = TimeSpan.Zero it worked as I had expected – Steve Dec 23 '20 at 12:54

1 Answers1

1

OK - looks like it was due to Clock Skew...

Confirmed by adding ClockSkew = TimeSpan.Zero into options.TokenValidationParameters and the expired token then results in an Unauthorised being returned

Steve
  • 729
  • 14
  • 29