No articles found
Try different keywords or browse our categories
Fix: Token expired error
Complete guide to fix 'Token expired' error. Learn how to implement token refresh, handle expiration, and manage authentication tokens effectively.
The ‘Token expired’ error occurs when an authentication token has exceeded its validity period and can no longer be used for authorization. This is a common issue in applications using time-limited authentication tokens.
How the Error Happens
This error typically occurs when:
- JWT token has exceeded its expiration time (exp claim)
- Session token has timed out
- Access token lifetime has elapsed
- Refresh token has also expired
- Clock synchronization issues between client and server
- Token wasn’t refreshed before expiration
Solution 1: Implement Token Refresh Mechanism
// ✅ Token refresh utility
class TokenManager {
constructor() {
this.accessToken = localStorage.getItem('accessToken');
this.refreshToken = localStorage.getItem('refreshToken');
}
isTokenExpired(token) {
try {
const payload = jwt.decode(token);
const currentTime = Math.floor(Date.now() / 1000);
return payload.exp < currentTime;
} catch (error) {
return true; // If we can't decode, assume expired
}
}
async refreshToken() {
if (!this.refreshToken) {
throw new Error('No refresh token available');
}
try {
const response = await fetch('/api/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
refreshToken: this.refreshToken
})
});
if (!response.ok) {
throw new Error('Refresh token failed');
}
const data = await response.json();
this.accessToken = data.accessToken;
this.refreshToken = data.refreshToken;
localStorage.setItem('accessToken', data.accessToken);
localStorage.setItem('refreshToken', data.refreshToken);
return data.accessToken;
} catch (error) {
// ✅ Clear invalid tokens
this.clearTokens();
throw error;
}
}
clearTokens() {
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
this.accessToken = null;
this.refreshToken = null;
}
}
Solution 2: Automatic Token Refresh in API Calls
// ✅ Axios interceptor for automatic token refresh
import axios from 'axios';
const api = axios.create({
baseURL: process.env.REACT_APP_API_URL
});
let isRefreshing = false;
let failedQueue = [];
const processQueue = (error, token = null) => {
failedQueue.forEach(prom => {
if (error) {
prom.reject(error);
} else {
prom.resolve(token);
}
});
failedQueue = [];
};
api.interceptors.response.use(
response => response,
async error => {
const originalRequest = error.config;
if (error.response?.status === 401 && error.response.data.error === 'Token expired') {
if (isRefreshing) {
// ✅ Queue request while refresh is in progress
return new Promise((resolve, reject) => {
failedQueue.push({ resolve, reject });
}).then(token => {
originalRequest.headers['Authorization'] = 'Bearer ' + token;
return axios(originalRequest);
}).catch(err => {
return Promise.reject(err);
});
}
isRefreshing = true;
try {
const newToken = await refreshToken();
processQueue(null, newToken);
originalRequest.headers['Authorization'] = 'Bearer ' + newToken;
return axios(originalRequest);
} catch (refreshError) {
processQueue(refreshError, null);
// ✅ Redirect to login
window.location.href = '/login';
return Promise.reject(refreshError);
} finally {
isRefreshing = false;
}
}
return Promise.reject(error);
}
);
Solution 3: Frontend Token Expiration Handling
// ✅ React hook for token management
import { useState, useEffect } from 'react';
function useTokenExpiration() {
const [isExpired, setIsExpired] = useState(false);
const [timeUntilExpiration, setTimeUntilExpiration] = useState(null);
useEffect(() => {
const checkTokenExpiration = () => {
const token = localStorage.getItem('accessToken');
if (!token) {
setIsExpired(true);
return;
}
try {
const payload = jwt.decode(token);
const currentTime = Math.floor(Date.now() / 1000);
const timeLeft = payload.exp - currentTime;
if (timeLeft <= 0) {
setIsExpired(true);
return;
}
setTimeUntilExpiration(timeLeft);
setIsExpired(false);
// ✅ Refresh token 5 minutes before expiration
if (timeLeft <= 300) {
refreshToken();
}
} catch (error) {
setIsExpired(true);
}
};
checkTokenExpiration();
// ✅ Check every minute
const interval = setInterval(checkTokenExpiration, 60000);
return () => clearInterval(interval);
}, []);
return { isExpired, timeUntilExpiration };
}
Solution 4: Backend Token Validation with Expiration Check
// ✅ Express.js middleware with expiration handling
const jwt = require('jsonwebtoken');
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) {
if (err.name === 'TokenExpiredError') {
return res.status(401).json({
error: 'Token expired',
expired: true
});
}
return res.status(403).json({ error: 'Invalid token' });
}
req.user = decoded;
next();
});
}
// ✅ Token refresh endpoint
app.post('/api/refresh', async (req, res) => {
const { refreshToken } = req.body;
if (!refreshToken) {
return res.status(401).json({ error: 'Refresh token required' });
}
try {
// ✅ Verify refresh token
const decoded = jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET);
const userId = decoded.userId;
// ✅ Check if refresh token is still valid in database
const storedRefreshToken = await getUserRefreshToken(userId, refreshToken);
if (!storedRefreshToken) {
return res.status(403).json({ error: 'Invalid refresh token' });
}
// ✅ Generate new tokens
const newAccessToken = jwt.sign(
{ userId: decoded.userId },
process.env.JWT_SECRET,
{ expiresIn: '15m' } // Short-lived access token
);
const newRefreshToken = jwt.sign(
{ userId: decoded.userId },
process.env.REFRESH_TOKEN_SECRET,
{ expiresIn: '7d' } // Longer-lived refresh token
);
// ✅ Update refresh token in database
await updateUserRefreshToken(userId, newRefreshToken);
res.json({
accessToken: newAccessToken,
refreshToken: newRefreshToken
});
} catch (error) {
return res.status(403).json({ error: 'Invalid refresh token' });
}
});
Solution 5: Proactive Token Refresh
// ✅ Proactive token refresh before expiration
class ProactiveTokenManager {
constructor() {
this.refreshTimer = null;
}
scheduleTokenRefresh(token) {
// ✅ Clear existing timer
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
}
try {
const payload = jwt.decode(token);
const currentTime = Math.floor(Date.now() / 1000);
const timeUntilExpiration = payload.exp - currentTime;
// ✅ Refresh 2 minutes before expiration
const refreshTime = Math.max(0, (timeUntilExpiration - 120) * 1000);
this.refreshTimer = setTimeout(async () => {
try {
await this.refreshToken();
} catch (error) {
console.error('Token refresh failed:', error);
// ✅ Handle refresh failure
this.handleRefreshFailure();
}
}, refreshTime);
} catch (error) {
console.error('Failed to schedule token refresh:', error);
}
}
async refreshToken() {
const refreshToken = localStorage.getItem('refreshToken');
if (!refreshToken) {
throw new Error('No refresh token available');
}
const response = await fetch('/api/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ refreshToken })
});
if (!response.ok) {
throw new Error('Token refresh failed');
}
const data = await response.json();
localStorage.setItem('accessToken', data.accessToken);
// ✅ Schedule next refresh
this.scheduleTokenRefresh(data.accessToken);
return data.accessToken;
}
handleRefreshFailure() {
// ✅ Clear tokens and redirect to login
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
window.location.href = '/login';
}
}
Solution 6: Token Expiration Monitoring
// ✅ Token expiration monitoring service
class TokenExpirationMonitor {
constructor() {
this.checkInterval = null;
this.warningCallback = null;
this.expiredCallback = null;
}
startMonitoring(warningMinutes = 5) {
this.stopMonitoring();
this.checkInterval = setInterval(() => {
const token = localStorage.getItem('accessToken');
if (!token) return;
try {
const payload = jwt.decode(token);
const currentTime = Math.floor(Date.now() / 1000);
const timeLeft = payload.exp - currentTime;
if (timeLeft <= 0) {
// ✅ Token expired
if (this.expiredCallback) {
this.expiredCallback();
}
this.stopMonitoring();
return;
}
// ✅ Warning threshold
if (timeLeft <= warningMinutes * 60 && this.warningCallback) {
this.warningCallback(timeLeft);
}
} catch (error) {
console.error('Token monitoring error:', error);
}
}, 30000); // Check every 30 seconds
}
stopMonitoring() {
if (this.checkInterval) {
clearInterval(this.checkInterval);
this.checkInterval = null;
}
}
setCallbacks(onWarning, onExpired) {
this.warningCallback = onWarning;
this.expiredCallback = onExpired;
}
}
// ✅ Usage
const monitor = new TokenExpirationMonitor();
monitor.setCallbacks(
(secondsLeft) => {
console.warn(`Token expires in ${Math.floor(secondsLeft / 60)} minutes`);
// Show warning to user
},
() => {
alert('Your session has expired. Please log in again.');
// Redirect to login
window.location.href = '/login';
}
);
monitor.startMonitoring(5); // Warn 5 minutes before expiration
Solution 7: Graceful Token Expiration Handling
// ✅ Graceful handling of expired tokens
function handleExpiredToken(error) {
if (error.response?.status === 401 &&
(error.response.data.error === 'Token expired' ||
error.response.data.message?.includes('expired'))) {
// ✅ Show user-friendly message
showNotification('Your session has expired. Please log in again.', 'warning');
// ✅ Clear local storage
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
// ✅ Redirect after delay to show notification
setTimeout(() => {
window.location.href = '/login';
}, 2000);
return true; // Handled
}
return false; // Not handled
}
// ✅ Notification utility
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 5000);
}
Solution 8: Token Rotation Strategy
// ✅ Implement token rotation for security
class SecureTokenManager {
constructor() {
this.currentToken = null;
this.rotationThreshold = 300; // 5 minutes
}
async rotateTokenIfNeeded() {
const token = localStorage.getItem('accessToken');
if (!token) return;
const payload = jwt.decode(token);
const currentTime = Math.floor(Date.now() / 1000);
const timeLeft = payload.exp - currentTime;
// ✅ Rotate token if approaching threshold
if (timeLeft <= this.rotationThreshold) {
try {
const newToken = await this.refreshToken();
this.currentToken = newToken;
return newToken;
} catch (error) {
console.error('Token rotation failed:', error);
throw error;
}
}
this.currentToken = token;
return token;
}
async getValidToken() {
if (this.currentToken) {
const payload = jwt.decode(this.currentToken);
const currentTime = Math.floor(Date.now() / 1000);
if (payload.exp > currentTime) {
return this.currentToken;
}
}
return await this.rotateTokenIfNeeded();
}
}
Solution 9: Session Management with Tokens
// ✅ Comprehensive session management
class SessionManager {
constructor() {
this.tokenManager = new TokenManager();
this.sessionTimeout = null;
this.inactivityTimeout = null;
this.maxInactivityTime = 30 * 60 * 1000; // 30 minutes
}
startSession() {
this.resetInactivityTimer();
this.setupEventListeners();
}
resetInactivityTimer() {
if (this.inactivityTimeout) {
clearTimeout(this.inactivityTimeout);
}
this.inactivityTimeout = setTimeout(() => {
this.endSession('Inactivity timeout');
}, this.maxInactivityTime);
}
setupEventListeners() {
['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'].forEach(event => {
document.addEventListener(event, () => this.resetInactivityTimer(), true);
});
}
async endSession(reason = 'User logout') {
if (this.inactivityTimeout) {
clearTimeout(this.inactivityTimeout);
}
// ✅ Clear tokens
this.tokenManager.clearTokens();
// ✅ Notify backend
try {
await fetch('/api/logout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ reason })
});
} catch (error) {
console.error('Logout notification failed:', error);
}
// ✅ Redirect to login
window.location.href = '/login';
}
}
// ✅ Initialize session management
const sessionManager = new SessionManager();
sessionManager.startSession();
Solution 10: Testing Token Expiration
// ✅ Test token expiration handling
function testTokenExpiration() {
// ✅ Create an expired token for testing
const expiredPayload = {
userId: 123,
exp: Math.floor(Date.now() / 1000) - 3600, // 1 hour ago
iat: Math.floor(Date.now() / 1000) - 7200 // 2 hours ago
};
const expiredToken = jwt.sign(expiredPayload, process.env.JWT_SECRET);
// ✅ Test expiration detection
const isExpired = jwt.decode(expiredToken).exp < Math.floor(Date.now() / 1000);
console.assert(isExpired, 'Token should be detected as expired');
// ✅ Test refresh mechanism
try {
const newToken = await refreshToken();
console.log('Token refresh successful:', newToken);
} catch (error) {
console.error('Token refresh failed:', error);
}
}
// ✅ Automated testing
describe('Token Expiration', () => {
test('should detect expired tokens', () => {
const expiredToken = createExpiredToken();
expect(TokenManager.isTokenExpired(expiredToken)).toBe(true);
});
test('should refresh expired tokens', async () => {
const result = await TokenManager.refreshToken();
expect(result).toBeDefined();
expect(jwt.decode(result).exp).toBeGreaterThan(Math.floor(Date.now() / 1000));
});
});
Prevention Tips
- Short-lived tokens: Use shorter expiration times for access tokens
- Refresh tokens: Implement secure refresh token mechanisms
- Proactive refresh: Refresh tokens before they expire
- Monitoring: Monitor token expiration times
- Graceful handling: Handle expiration gracefully without disrupting user experience
- Security: Implement token rotation and proper cleanup
- Testing: Test expiration scenarios thoroughly
When to Contact Support
Contact your authentication service provider when:
- Following all best practices still results in unexpected expiration
- Suspected service-side token generation issues
- Clock synchronization problems between systems
- Unexpected changes in token expiration policies
- Refresh token mechanisms are not working as expected
Related Articles
Fix: JWT malformed error
Complete guide to fix 'JWT malformed' error. Learn how to properly handle, decode, and validate JSON Web Tokens in your applications.
Fix: JWT token invalid or expired error - Complete Guide
Complete guide to fix JWT token invalid or expired errors. Learn how to handle JWT authentication issues with practical solutions, token refresh strategies, and best practices for secure applications.
Fix: invalid_client OAuth error
Complete guide to fix 'invalid_client' OAuth error. Learn how to resolve client credentials and configuration issues in OAuth implementations.