The server verifies the signature to ensure the token hasn't been tampered with.
Access vs Refresh Tokens
Property
Access Token
Refresh Token
Purpose
Authorize API requests
Obtain new access tokens
Lifetime
Short (15 min)
Long (7-30 days)
Storage
Memory or httpOnly cookie
httpOnly cookie only
Sent with
Every API request
Only to token refresh endpoint
Token Flow
1. User logs in with credentials2. Server returns access token + refresh token3. Client sends access token with each request4. When access token expires (401), client uses refresh token5. Server verifies refresh token, returns new access token6. If refresh token is expired, user must log in again
Storage Best Practices
httpOnly Cookie (Recommended)
// Server: Set tokens as httpOnly cookiesres.cookie("access_token", accessToken, { httpOnly: true, secure: true, sameSite: "strict", maxAge: 15 * 60 * 1000, // 15 minutes path: "/",});res.cookie("refresh_token", refreshToken, { httpOnly: true, secure: true, sameSite: "strict", maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days path: "/api/auth/refresh", // Only sent to refresh endpoint});
Why NOT localStorage
Accessible via JavaScript — vulnerable to XSS attacks
Any injected script can steal the token
No automatic CSRF protection
Token Rotation
Rotate refresh tokens on every use to limit the window of a stolen token: