Refresh Token - sonuprajapati15/Authentication-Authorization GitHub Wiki
This document provides a solution for handling authentication token expiration and refresh without requiring the user to log in again. It describes two approaches:
- Client-Managed Token Refresh
- Backend-Managed Token Refresh
The document also includes High-Level Design (HLD), Low-Level Design (LLD), and Architecture Diagrams.

- Access tokens expire after a fixed duration (e.g., 30 minutes).
- Users should not have to log in again if they are active before the refresh token expires.
- Ensure security while minimizing latency.
- Access Token (Short-lived, e.g., 15-30 mins) - Used for API authentication.
- Refresh Token (Long-lived, e.g., 30 days) - Used to obtain a new access token when it expires.
- Two strategies:
- Client-Managed Refresh: The client checks token expiry and refreshes before making API requests.
- Backend-Managed Refresh: The backend handles token refresh before responding to the client.
graph TD;
User -->|Login| AuthServer;
AuthServer -->|Generate Access & Refresh Token| User;
User -->|Send Access Token| API;
API -->|Validate Token| AuthServer;
AuthServer -->|Token Expired?| CheckExpiration;
CheckExpiration -->|Yes| RefreshToken;
RefreshToken -->|Return New Access Token| API;
API -->|Serve Request| User;
- Client stores access and refresh tokens securely.
- Before making an API call, the client checks if the access token is expired.
- If expired, the client requests a new access token using the refresh token.
- The client updates the stored token and proceeds with the request.
import axios from 'axios';
const api = axios.create({
baseURL: 'https://your-api.com',
withCredentials: true,
});
api.interceptors.request.use(async (config) => {
let accessToken = localStorage.getItem('accessToken');
if (isTokenExpired(accessToken)) {
accessToken = await refreshToken();
}
config.headers.Authorization = Bearer ${accessToken}
;
return config;
}, (error) => Promise.reject(error));
async function refreshToken() {
try {
const response = await axios.post('https://your-api.com/refresh-token', {}, { withCredentials: true });
localStorage.setItem('accessToken', response.data.accessToken);
return response.data.accessToken;
} catch (error) {
console.error('Session expired. Please log in again.');
}
}
- Client sends an API request with the access token.
- Backend validates the token.
- If expired, backend uses the refresh token (stored in HttpOnly cookies) to generate a new access token.
- Backend responds with a new access token.
app.use(async (req, res, next) => {
let accessToken = req.headers.authorization?.split(" ")[1];
if (!accessToken) return res.sendStatus(401);
try {
jwt.verify(accessToken, ACCESS_SECRET);
return next();
} catch (err) {
if (err.name === 'TokenExpiredError') {
const refreshToken = req.cookies.refreshToken;
if (!refreshToken) return res.sendStatus(403);
try {
const payload = jwt.verify(refreshToken, REFRESH_SECRET);
const newAccessToken = jwt.sign({ userId: payload.userId }, ACCESS_SECRET, { expiresIn: '15m' });
res.setHeader('Authorization', `Bearer ${newAccessToken}`);
req.headers.authorization = `Bearer ${newAccessToken}`;
return next();
} catch (refreshErr) {
return res.sendStatus(403);
}
} else {
return res.sendStatus(401);
}
}
});
Feature | Client-Managed Refresh | Backend-Managed Refresh |
---|---|---|
Security | Moderate (client storage) | High (server-controlled) |
Performance | Faster | Slightly slower (extra backend processing) |
Backend Load | Lower | Higher |
Works for APIs | Requires client logic | Fully managed by server |
- Use Client-Managed Refresh for frontend-heavy applications.
- Use Backend-Managed Refresh for secure API-driven applications.
- Always store refresh tokens securely using HttpOnly cookies.
- Implement token revocation for better security.
- Use OAuth 2.0 with PKCE for enhanced security.
- Implement rate limiting to prevent abuse of refresh token endpoints.