Describe a robust automatic token refresh strategy in a React SPA
This tests token rotation without UX interruption in SPAs. Use HttpOnly cookies for refresh tokens, in-memory access tokens, an interceptor with a promise lock, and proactive background refresh.
What's really being asked
The interviewer is looking for awareness that refresh tokens are typically single-use credentials, which creates a race condition when multiple concurrent API requests expire at the same time. They want to see a frontend architecture that silently rotates access tokens without dropping in-flight requests, exposing long-lived credentials to XSS, or creating infinite retry loops.
The full answer
First, a secure storage hierarchy. The refresh token must live in an HttpOnly, Secure, SameSite=strict cookie so JavaScript cannot read it, while the short-lived access token can be held in memory or a non-HttpOnly cookie. Second, an interceptor with a promise lock. Configure a global Axios or fetch response interceptor so that when a 401 occurs, the app attempts a refresh, but uses a single in-flight promise as a lock. If ten requests fail simultaneously, only one refresh call reaches the auth server and the other nine await the same new token. Third, proactive background refresh. Instead of waiting for a 401, schedule a refresh when the access token reaches eighty percent of its lifetime, or refresh on a visibilitychange event, preventing mid-request expiry. Fourth, graceful degradation. If the refresh endpoint returns 401 or 403, clear the client-side auth state and redirect to login rather than retrying forever.
The mistakes people make
Storing the refresh token in localStorage or sessionStorage where XSS can exfiltrate it. Ignoring the single-use nature of refresh tokens and allowing every failed request to trigger its own refresh, which causes the first request to succeed and all subsequent ones to fail because the refresh token was already consumed. Using setInterval in a React component without cleanup, leading to memory leaks or refreshes after logout. Refreshing on every page load regardless of expiry, which wastes resources and increases attack surface.
What usually comes next
How would you handle this across multiple browser tabs? You can use a BroadcastChannel to notify other tabs when a refresh completes, or rely on the cookie being updated and picked up by the next request in each tab. What happens if the user suspends the laptop and returns after both tokens expire? Silent refresh is impossible, so you redirect to login. How do you prevent CSRF against the refresh endpoint? By using SameSite cookies and optionally a double-submit pattern with a request header.
A concrete example
Imagine an Axios instance with a response interceptor. When a 401 arrives, the interceptor checks if a refresh is already in flight. If not, it creates a promise that POSTs to the refresh endpoint with credentials included. All concurrent 401s attach to that same promise. When it resolves, the new access token is stored in a closure variable and the queued requests are retried with the updated Authorization header. If the promise rejects, all queued requests fail and the user is redirected to login.
Interview question
When ten API requests in a React SPA simultaneously fail with 401 due to an expired access token, how should the frontend handle rotation if refresh tokens are single-use?
- a.Refresh the access token on every page load so that concurrent requests never encounter a 401 response
- b.Queue the failed requests and retry them one by one, allowing each retry to trigger its own separate refresh request
- c.Store the refresh token in localStorage and have each failed request independently call the refresh endpoint to obtain a new access token
- d.Use a response interceptor with a promise lock so only one refresh call is made and all ten requests await the same new token before retryingCorrect
Why? this is the answer
A promise lock ensures only one refresh call reaches the auth server, preventing single-use refresh token invalidation while all queued requests retry silently. Storing the token in localStorage exposes it to XSS and issuing independent refresh calls creates a race condition where only the first request succeeds.
Just read this? Test yourself on what you have been reading.
Read the original → authjs.dev
Put your scrolling time to good use
Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on react — each one lists the topics its interview covers.
See open roles