Why do we get so many 403's from refresh token calls?

Problem statement

We are concerned about our dashboard logs having too many 403’s when calling /token endpoint for refresh tokens.

Solution

The important configuration that’s relevant to the 403 messages from refresh token calls can be found in this doc:

Please check your values for absolute and inactivity lifetime, based on the doc above. Our default value for absolute lifetime is 30 days. Your configuration may be different depending on the behaviour you’d like to have. Now, allow us to expand on this value:

Imagine the situation that a user logs-in to your application, they will get an access token which is shorter lived than this refresh token. You can find the expiration time of your access tokens checking this doc:

Update Access Token Lifetime

So, normally this access token expiration is too short for a good user experience, since you would need to ask the user to re-login more often. This is where Refresh Tokens come into play. Refresh tokens basically allow having an access token to have a short expiration time and instead you will use these refresh tokens to get new access tokens without user interaction. This gives you good user experience without compromising the security of having a long-live access token.

Most probably, your application / SDK is doing a getAccessTokenSilently call, something close to this:

const auth0 = await createAuth0Client({
    domain: '<your Auth0 domain>',
    client_id: '<your Auth0 client ID>',
    cacheLocation: 'localstorage',
    useRefreshTokens: true
    });
    
    // Logging-in will automatically request the offline_access scope
    // and store the resulting refresh token
    auth0.loginWithRedirect();
    
    // Silently refreshing the access token will use the /token endpoint
    // with ‘refresh_token’ grant and the refresh token from the cache
    await auth0.getTokenSilently();

With refresh tokens, you have a local storage in cache, but when needed(previous access token is expired and you need a new one, remember they last one hour in your case). The getTokenSilently() call does automatically call the /token endpoint in order to refresh the token.
You can read more in our docs about how this works:

Now, coming back to the original topic, the absolute lifetime is for how long you can keep calling “getTokenSilently” method and actually getting a valid access_token back.

Imagine your website is opened for a customer and they’ve left the browser open for longer than your absolute lifetime is or they’ve been inactive for longer than your inactivity period. These calls to refresh the token will return a 403 since the expiration time has passed and they are no longer able to retrieve access tokens.

We would suggest then looking at your implementation code and checking exactly when are you invoking this refresh token calls. Depending on the SDK, it may be a bit different but usually is a “getTokenSilently” or “getAccessTokenSilently” call. Check for these calls, and check what actually happens in your application flow when these return a 403. Are you redirecting the user to the login page or does the site keep trying to generate tokens?

The first 403 you get is completely normal and not an issue, it is just pointing out that they will not get a refresh token anymore. However, it is true, that if your application is trying so many times, it is pointless and just adds noise to your dashboard logs.

Also, the more user activity you have in your application the more of these error logs you will get, it’s common leaving a site open and coming back to them hours later or even days later. This mechanism is just allowing them to not have to re-login.