title: New JWT token has same expiry as old token
body: <p>I've got JWT token generation working in my ASP.NET Core 2.0 Web API, but I'm running into an issue where subsequent new access tokens have the same expiry as previously generated ones.</p>
  <p>For instance, I post login credentials, and return an access token. The access token works as expected on [Authorize] API endpoints. For testing purposed, I set the token to expire after 1 minute. After 1 minute, the token expires and the authenticated endpoints return a 401, as expected.</p>
  <p>I'm handling the 401's in my client side application. The login form appears, and the user logs in again. A new token is generated and returned. The only issue is, this new token has the exact same 'ValidTo&quot; DateTime as the initially generated token. Causing any calls after using this new token to return 401 because the token is already expired. I've confirmed that two different tokens are being checked, so it's not an issue with me passing the wrong token</p>
  <p>First token failure (expected, as token expired):</p>
  <blockquote>
  <p>Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Information: Failed to validate the token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZGFtQHBytNDRiYS1...Do1NzM5NS8ifQ.t8DjvlGV7GZ3xucwu-1hlJRXA5owPdP9t7kfYiiJHyQ.</p>
  <p>Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException: IDX10223: Lifetime validation failed. The token is expired.</p>
  <p>ValidTo: '11/08/2017 19:23:09'</p>
  <p>Current time: '11/08/2017 19:23:13'.</p>
  </blockquote>
  <p>Second token failure (not expected, ValidTo same as previous token)</p>
  <blockquote>
  <p>Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Information: Failed to validate the token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZGFtQ...dDo1NzM5NS8ifQ.2TMPJvYnQl1Jw78M2nj40uD3qejBEciXfKC845saGNI.</p>
  <p>Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException: IDX10223: Lifetime validation failed. The token is expired.</p>
  <p>ValidTo: '11/08/2017 19:23:09'</p>
  <p>Current time: '11/08/2017 19:23:34'.</p>
  </blockquote>
  <p>JWT Configuration in Startup.cs</p>
  <pre><code>services.Configure&lt;JwtIssuerOptions&gt;(options =&gt; {
              options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
              options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
              options.SigningCredentials = new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);
              options.ValidFor = TimeSpan.FromMinutes(1);
          });
          services.AddAuthentication(o =&gt; 
          {
              o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
              o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
          }).AddJwtBearer(o =&gt;
          {
              o.TokenValidationParameters = new TokenValidationParameters
              {
                  ValidateIssuer = true,
                  ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
  
                  ValidateAudience = true,
                  ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
  
                  ValidateIssuerSigningKey = true,
                  IssuerSigningKey = SigningKey,
  
                  RequireExpirationTime = true,
                  ValidateLifetime = true,
                  ClockSkew = TimeSpan.Zero,
              };
          });
  </code></pre>
  <p>Login action where Token is created:</p>
  <pre><code>[HttpPost]
      public async Task&lt;IActionResult&gt; Login([FromBody]CredentialsViewModel credentials)
      {
          if (!ModelState.IsValid)
          {
              return BadRequest(ModelState);
          }
  
          var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);
          if (identity == null)
          {
              return BadRequest(Errors.AddErrorToModelState(&quot;login_failure&quot;, &quot;Invalid username or password.&quot;, ModelState));
          }
  
          // Serialize and return the response
          var response = new
          {
              id = identity.Claims.Single(c =&gt; c.Type == &quot;id&quot;).Value,
              auth_token = await _jwtFactory.GenerateEncodedToken(credentials.UserName, identity),
              expires_in = (int)_jwtOptions.ValidFor.TotalSeconds
          };
  
          var json = JsonConvert.SerializeObject(response, _serializerSettings);
          return new OkObjectResult(json);
      }
  </code></pre>
  <p>JwtFactory method where token is being generated:</p>
  <pre><code>private readonly JwtIssuerOptions _jwtOptions;
  
  public JwtFactory(IOptions&lt;JwtIssuerOptions&gt; jwtOptions)
  {
     _jwtOptions = jwtOptions.Value;
     ThrowIfInvalidOptions(_jwtOptions);
  }
  
  public async Task&lt;string&gt; GenerateEncodedToken(string userName, ClaimsIdentity identity)
      {
          var claims = new[]
       {
               new Claim(JwtRegisteredClaimNames.Sub, userName),
               new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
               new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64),
               identity.FindFirst(&quot;rol&quot;),
               identity.FindFirst(&quot;id&quot;)
           };
  
          // Create the JWT security token and encode it.
          var jwt = new JwtSecurityToken(
              issuer: _jwtOptions.Issuer,
              audience: _jwtOptions.Audience,
              claims: claims,
              notBefore: _jwtOptions.NotBefore,
              expires: _jwtOptions.Expiration,
              signingCredentials: _jwtOptions.SigningCredentials);
  
          var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
  
          return encodedJwt;
      }
  </code></pre>