How to Achieve a Seamless “Never‑Logout” Experience with Token Refresh
This article explains why short‑lived access tokens cause user interruptions, introduces the dual‑token authentication model with refresh tokens, and provides a complete Axios interceptor implementation that transparently renews tokens, handles concurrency, and gracefully logs out when refresh fails.
Root cause: Access Token’s inherent paradox
Access tokens are deliberately short‑lived (e.g., 30 minutes to 1 hour) to limit the damage of a leak, but users dislike being forced to re‑login frequently, creating a tension between security and user experience.
Core idea: Dual‑Token Authentication System
The solution is to use two tokens:
Access Token (access token)
Purpose: Sent in the Authorization header to access protected APIs.
Characteristics: Short lifespan (≈1 hour), stateless, stored in client memory (e.g., Vuex/Redux).
Refresh Token (refresh token)
Purpose: Used to obtain a new access token when the current one expires.
Characteristics: Long lifespan (7–30 days), stateful, must be stored securely (commonly in an HttpOnly cookie).
Because the access token is stateless, it cannot be revoked, whereas the refresh token is stateful and can be invalidated via a server‑side whitelist or revocation list when a user changes password or logs out.
Detailed Refresh Workflow (Invisible to Users)
Initial login: Server returns both an access token and a refresh token.
Normal request: Client includes the access token in the Authorization header.
Token expiration: API returns 401 Unauthorized when the access token is expired.
Intercept 401: An Axios interceptor pauses the failed request instead of showing an error.
Refresh request: The interceptor calls a dedicated refresh endpoint (e.g., /api/auth/refresh) using the refresh token.
Handle refresh result:
Success – server validates the refresh token, issues a new access token (optionally a new refresh token), and the client retries the original request.
Failure – if the refresh token is also invalid, the client clears authentication data, logs out the user, and redirects to the login page.
Practical Example: Axios Interceptor for Seamless Refresh
Below is a complete implementation that also handles concurrent 401 responses.
import axios from 'axios';
const service = axios.create({
baseURL: '/api',
timeout: 10000,
});
// Request interceptor – attach access token
service.interceptors.request.use(config => {
const accessToken = getAccessTokenFromStore();
if (accessToken) {
config.headers['Authorization'] = `Bearer ${accessToken}`;
}
return config;
}, error => Promise.reject(error));
import { refreshTokenApi } from './auth';
let isRefreshing = false; // flag to avoid duplicate refreshes
let requests = []; // queue for pending requests
service.interceptors.response.use(response => response, async error => {
const { config, response: { status } } = error;
if (status !== 401) return Promise.reject(error);
if (isRefreshing) {
return new Promise(resolve => {
requests.push(() => resolve(service(config)));
});
}
isRefreshing = true;
try {
const { newAccessToken } = await refreshTokenApi(); // refresh token sent via HttpOnly cookie
setAccessTokenInStore(newAccessToken);
config.headers['Authorization'] = `Bearer ${newAccessToken}`;
requests.forEach(cb => cb());
requests = [];
return service(config);
} catch (refreshError) {
console.error('Unable to refresh token.', refreshError);
logoutUser(); // clear tokens and redirect to login
return Promise.reject(refreshError);
} finally {
isRefreshing = false;
}
});
export default service;The code uses isRefreshing and a requests queue to ensure only one refresh request runs at a time; subsequent 401s wait for the first refresh to finish before retrying.
Key Takeaways
Concurrent handling prevents redundant refresh calls.
The lock‑like mechanism makes the refresh operation atomic, avoiding race conditions.
When the refresh token also expires, the system performs an elegant logout, preserving security.
Implementing an invisible token‑refresh mechanism is now a standard practice for modern web applications, balancing authentication complexity, security, and seamless user experience.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JavaScript
Provides JavaScript enthusiasts with tutorials and experience sharing on web front‑end technologies, including JavaScript, Node.js, Deno, Vue.js, React, Angular, HTML5, CSS3, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
