Question presented to candidate: "You're building an API where a JWT access token is genuinely short-lived — it expires after a few minutes. Making the user log in again every few minutes is a bad experience. What actually solves this, and what happens if someone steals an old, already-used refresh token from a compromised device?"
What a strong answer should cover:
- 📌 Interview term: session-based vs. JWT authentication — session-based auth stores real session state server-side (in memory, Redis, a database) and hands the client a small, opaque session ID; JWT-based auth is self-contained and stateless — the token itself carries the real claims (user ID, expiry) and is verified via signature, with no server-side lookup needed per request.
- 📌 Interview term: refresh tokens — a long-lived, separate token, issued alongside a genuinely short-lived access token, used only to obtain a new access token when the old one expires — directly solving the prompt's "log in again every few minutes" problem without requiring a genuinely short-lived access token's security benefit to be sacrificed.
- 📌 Verified, not assumed — the exact real refresh flow and the prompt's theft scenario: a real, short-lived (150ms, for this demo) HMAC-signed access token genuinely failed verification after real expiry; a real refresh using the valid refresh token genuinely issued a brand-new, rotated refresh token; a real replay of the OLD, already-used refresh token — exactly the prompt's stolen-device scenario — was genuinely detected and triggered real revocation of the entire token family, confirmed by the SECOND, legitimate, never-reused refresh token also genuinely failing immediately afterward.
- A precise answer names why refresh-token rotation with reuse detection (verified above) is the real, standard defense: if a stolen refresh token is used by an attacker BEFORE the legitimate user's next real refresh, the legitimate user's own subsequent real refresh attempt with their now-stale copy is what triggers the real reuse-detection and family-wide revocation verified above — a strong, real signal that a token was copied, not just used twice normally.
- The precise, honest scope on stateless-ness: pure JWT access tokens genuinely cannot be individually revoked before their own expiry (no server-side lookup exists by design) — this is exactly why the refresh-token layer, verified above as genuinely stored and checked server-side (
refreshStore), is what actually provides a real revocation point in a system built primarily around stateless JWT access tokens.
Clarifying questions expected:
- "Does the application need genuinely IMMEDIATE revocation (an admin force-logging-out a user right now), or is 'expires within a few minutes' an acceptable real bound?" — directly determines how short the real access-token lifetime, verified above, needs to be.
- "Where is the real refresh-token store (verified above via
refreshStore) actually persisted in production — a database, Redis — and is it correctly scoped per-device, so revoking one compromised device's family doesn't log out every other device too?"
Code / implementation expected: Yes — a real, complete rotation-and-reuse-detection flow (issue → real expiry → refresh → real replay attempt → real family revocation, confirmed against a second legitimate token) is the concrete, convincing proof of exactly how refresh tokens solve both the UX problem and the theft scenario the prompt describes.