Problem
An API call fails with 401 Unauthorized and error: token_expired. The user is still signed in (they didn't click logout, their tab is open), but the access token they hold is past its exp.
Cause
The most common causes, in order:
The tab was inactive long enough that the silent refresh paused — most browsers throttle background timers, so a tab left for hours without focus doesn't refresh on schedule.
The clock on the user's device is meaningfully off — the SDK trusts the device clock when scheduling refresh. A device 10 minutes ahead refreshes 10 minutes early; a device 10 minutes behind never realises the token is expired.
The refresh token itself expired (its TTL is hit) — silent refresh fails, the SDK doesn't loop; the user must sign in again.
The refresh token was revoked — by an admin, by reuse detection, or by the user from another device.
Resolution
For users who just want to keep working
The SDK's getAccessToken() method handles this transparently when called fresh:
const token = await getAccessToken() // returns a fresh token, refreshing if needed
const res = await fetch(url, { headers: { 'Authorization': `Bearer ${token}` } })
If your code grabs the token once at boot and reuses it indefinitely, you'll hit token_expired after the first hour. Always call getAccessToken() immediately before each API call.
For background tabs that need to keep refreshing
Two options:
Accept that the token will expire when the tab is inactive, and have your code re-call
getAccessToken()when the user returns to the tab. The Page Visibility API event (document.addEventListener('visibilitychange', ...)) is the right hook.Move the refresh to a Service Worker, which isn't subject to background-throttle.
For most apps, the first is fine — refreshing only when the tab is active is a reasonable energy posture.
For clock-drift
The SDK adds a 60s leeway window to refresh timing — it refreshes 60s before exp, not at exp. If your users' devices are reliably more than 60s off, expand the leeway via the provider's silentRefreshLeeway prop.
<IntelliAuthProvider silentRefreshLeeway={120} ... />
When the refresh token has expired or been revoked
The SDK's error will be session_expired or session_revoked, not token_expired. Show a "please sign in again" message and redirect through sign-in. This is the correct behaviour — long-lived sessions are bounded for a reason.
How to tell which cause
Catch the error in your SDK call. The error's code is specific:
code | Cause |
|---|---|
| Access token past |
| Refresh token TTL hit; user must sign in again |
| Refresh token invalidated (reuse detection, admin) |
| Network failure during refresh; retry |
Branch your UI accordingly. Calling loginWithRedirect() for session_expired is correct; for silent_auth_failed, retrying the API call (which will trigger a fresh refresh) is correct.
Prevention
Always call
getAccessToken()right before the API call.Configure a sensible refresh-token TTL on the application (default 14 days for browser; tune up for desktop / mobile).
Surface a friendly "your session has ended" UI rather than a generic 401 toast.