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 THIS TESTS: 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.
A GOOD ANSWER COVERS: 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.
COMMON WRONG ANSWERS: 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.
LIKELY FOLLOW-UPS: 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.
ONE 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.
Read the original → authjs.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.