JAVASCRIPT

Automatic JWT Token Refresh with Axios Interceptors

Implement robust JWT token refreshing in web applications using Axios interceptors to automatically renew expired tokens, ensuring continuous API access without user re-authentication.

import axios from 'axios';

const API_URL = 'https://api.example.com';
const REFRESH_URL = 'https://api.example.com/auth/refresh';

const axiosInstance = axios.create({
  baseURL: API_URL,
  headers: {
    'Content-Type': 'application/json',
  },
});

let isRefreshing = false;
let failedQueue = [];

const processQueue = (error, token = null) => {
  failedQueue.forEach(prom => {
    if (error) {
      prom.reject(error);
    } else {
      prom.resolve(token);
    }
  });
  failedQueue = [];
};

axiosInstance.interceptors.request.use(
  config => {
    const accessToken = localStorage.getItem('accessToken');
    if (accessToken) {
      config.headers['Authorization'] = `Bearer ${accessToken}`;
    }
    return config;
  },
  error => Promise.reject(error)
);

axiosInstance.interceptors.response.use(
  response => response,
  async error => {
    const originalRequest = error.config;
    if (error.response?.status === 401 && !originalRequest._retry) {
      if (isRefreshing) {
        return new Promise((resolve, reject) => {
          failedQueue.push({ resolve, reject });
        })
        .then(token => {
          originalRequest.headers['Authorization'] = `Bearer ${token}`;
          return axiosInstance(originalRequest);
        })
        .catch(err => Promise.reject(err));
      }

      originalRequest._retry = true;
      isRefreshing = true;

      const refreshToken = localStorage.getItem('refreshToken');
      if (!refreshToken) {
        // No refresh token, redirect to login or clear auth
        return Promise.reject(error);
      }

      try {
        const res = await axios.post(REFRESH_URL, { refreshToken });
        const newAccessToken = res.data.accessToken;
        const newRefreshToken = res.data.refreshToken; // If refresh token also rotates

        localStorage.setItem('accessToken', newAccessToken);
        if (newRefreshToken) localStorage.setItem('refreshToken', newRefreshToken);

        originalRequest.headers['Authorization'] = `Bearer ${newAccessToken}`;
        processQueue(null, newAccessToken); // Resolve all waiting requests
        return axiosInstance(originalRequest);
      } catch (refreshError) {
        processQueue(refreshError); // Reject all waiting requests
        // Handle refresh token failure (e.g., clear tokens, redirect to login)
        localStorage.removeItem('accessToken');
        localStorage.removeItem('refreshToken');
        // Optionally, redirect to login page
        // window.location.href = '/login';
        return Promise.reject(refreshError);
      } finally {
        isRefreshing = false;
      }
    }
    return Promise.reject(error);
  }
);

export default axiosInstance;

// Example Usage:
// axiosInstance.get('/data')
//   .then(response => console.log(response.data))
//   .catch(err => console.error('API Error:', err));
How it works: This snippet demonstrates how to automatically refresh expired JWT access tokens using Axios interceptors. The `request` interceptor adds the current access token to outgoing requests. The `response` interceptor catches 401 Unauthorized errors. If a 401 occurs due to an expired token, it attempts to use a refresh token to obtain a new access token. It also queues concurrent requests and retries them once the token is refreshed, preventing multiple refresh calls and ensuring a seamless user experience.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs