aspnetcore: JWT token silent renew breaks and causes crash on React template with authentication
Describe the bug
The JWT silent renew in the React template with authentication can fail in some scenarios. What happens is the token is listed as expired (user.expires_in is negative and outside the 5 minute clock skew grace period). As a result, the API request fails with a 401, the token is never renewed, and the user can no longer get into [Authorize] marked areas. The only way to solve this I found (and the workaround isn’t even confirmed), is to explicitly press the logout button and restart my application (presumably to reset authentication/authorization on the client side SPA).
If you don’t explicitly log out, this issue will persist even on restarts of the ASP.NET application presumably because it’s a client side issue. There are a lot of steps to the below so some may or may not actually contribute to the issue but it is a consistent repro.
To Reproduce
- Create a new React based ASP.NET application with Individual User authentication
- Reduce the AccessTokenLifetime parameter by modifying the call .AddApiAuthorization to:
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(opt =>
{
foreach (var c in opt.Clients)
c.AccessTokenLifetime = 65;
});
- The default is an hour but that’s way too long to repro.
- Start the application, register a new account and let it auto-login for you
- Now explicitly log out
- Then login again with your newly created account and make sure to tick the Remember Me box.
- Now stop the application.
- Wait for at least 65 seconds (ie. wait until the JWT access token expires).
- Start the application again with debugging on (if you want to be able to see the expired token live forever; see the last image in this report)
- Wait for 5 minutes while the clock skew grace period is consumed.
- Try to access the Fetch Data page which requires authentication/authorization.
Expected outcome: You get to the Fetch Data page with a renewed JWT. Actual outcome: A crash with a JWT that hasn’t and apparently won’t renew.

You can break into AuthorizeService.js and inspect the user value to see that the token is expired. You can actually see the token work and count more and more negative until it gets to 300 at which point it’ll then fail.:

Further technical details
- ASP.NET Core 3
- VSCode
.NET Core SDK (reflecting any global.json): Version: 3.0.100 Commit: 04339c3a26
Runtime Environment: OS Name: Windows OS Version: 10.0.18363 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\3.0.100\
Host (useful for support): Version: 3.0.0 Commit: 7d57652f33
.NET Core SDKs installed: 3.0.100 [C:\Program Files\dotnet\sdk]
.NET Core runtimes installed: Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
To install additional .NET Core runtimes or SDKs: https://aka.ms/dotnet-download
About this issue
- Original URL
- State: open
- Created 5 years ago
- Comments: 19 (6 by maintainers)
You can work around it quickly in the meantime by triggering a silent sign in for your user when you receive an expired token.
While delay isn’t ideal, it does stop the site crashing.
Unfortunately when getting our user we don’t appear to get the refresh token. Otherwise I’d look at modifying startup.cs, and creating a new tokenController as per https://www.blinkingcaret.com/2018/05/30/refresh-tokens-in-asp-net-core-web-api/