Careeroza
Access vs Refresh Tokens
The Expiry Dilemma
A short access token (15 min) is safe: if it is stolen, it is useless soon. But users hate logging in every 15 minutes. A long access token (30 days) is convenient but dangerous, and you cannot revoke a stateless JWT. The answer is two tokens:
| Access token | Refresh token | |
|---|---|---|
| Purpose | Call the API | Get a new access token |
| Lifetime | 5–15 minutes | 7–30 days |
| Format | Signed JWT (stateless) | Random string, stored (hashed) in the database |
| Sent to | Every API call, Authorization header | Only POST /api/auth/refresh |
| Stored in the browser | Memory (JS variable) | HttpOnly; Secure; SameSite=Strict cookie |
| Can be revoked | No (wait for expiry) | Yes (delete or mark revoked in DB) |
The Flow
login ---------> 200 {accessToken: A1} + Set-Cookie: shopease_rt=R1 (HttpOnly)
API calls -----> Authorization: Bearer A1 ... works for 15 min
API call ------> 401 "Access token expired"
refresh -------> POST /api/auth/refresh (cookie R1 sent automatically)
<--- 200 {accessToken: A2} + Set-Cookie: shopease_rt=R2 (R1 now used up)
retry call ----> Authorization: Bearer A2 -> 200
logout --------> POST /api/auth/logout -> R2 revoked, cookie clearedWhy Not a JWT Refresh Token?
Note: You can make the refresh token a JWT too, but a random opaque string stored in the database is simpler and safer: the server must look it up anyway to support revocation and rotation. Store only its SHA-256 hash, like a password, so a database leak does not leak usable tokens.