DEV Community

Cover image for Effortless Token Refresh in React Using Axios Interceptors
Justin J Daniel
Justin J Daniel

Posted on • Originally published at justinjdaniel.com

Effortless Token Refresh in React Using Axios Interceptors

Keeping users logged in securely is essential for any modern web app. One way to achieve this is with token rotation, a method that automatically refreshes expired access tokens using a refresh token, all without interrupting the user experience. In this beginner-friendly guide, you'll learn how to implement token rotation in a React app using Axios interceptors, without interfering with your server logic. We'll also compare common token storage options so you can choose what's best for your project.


What is Token Rotation?

Token rotation is a technique where, instead of keeping a refresh token forever, you get a new refresh token each time you use it. This makes your app more secure because even if a refresh token is stolen, it can only be used once before it becomes invalid.

How Does Axios Interceptor Help?

Axios interceptors let you run logic before a request is sent or after a response is received. Here, we'll use a response interceptor to detect when an access token has expired and automatically fetch a new one using the refresh token-without the user noticing.

Step-by-Step Example: Token Rotation with Axios

Below is a simple implementation.

// api.js
import axios from 'axios';

// Create an Axios instance
const api = axios.create({
  baseURL: 'https://your-api-url.com', // Replace with your backend URL
});

// Function to get tokens from storage
const getTokens = () => {
  return {
    accessToken: localStorage.getItem('accessToken'),
    refreshToken: localStorage.getItem('refreshToken'),
  };
};

// Function to save tokens to storage
const setTokens = ({ accessToken, refreshToken }) => {
  localStorage.setItem('accessToken', accessToken);
  if (refreshToken) localStorage.setItem('refreshToken', refreshToken);
};

// Add the access token to every request
api.interceptors.request.use((config) => {
  const { accessToken } = getTokens();
  if (accessToken) {
    config.headers.Authorization = `Bearer ${accessToken}`;
  }
  return config;
});

// Handle token expiration and rotation
api.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;
    // If the access token has expired, try to refresh
    if (
      error.response &&
      error.response.status === 401 &&
      !originalRequest._retry
    ) {
      originalRequest._retry = true;
      const { refreshToken } = getTokens();
      if (refreshToken) {
        try {
          // Request new access token
          const res = await axios.post('/auth/refresh', {
            refreshToken,
          });
          // Save new tokens
          setTokens(res.data);
          // Retry the original request with new token
          originalRequest.headers.Authorization = `Bearer ${res.data.accessToken}`;
          return api(originalRequest);
        } catch (refreshError) {
          // Refresh token invalid, force logout or redirect to login
          localStorage.clear();
          window.location.href = '/login';
        }
      }
    }
    // Any other error
    return Promise.reject(error);
  }
);

export default api;
Enter fullscreen mode Exit fullscreen mode

How it works:

Visual Flow

User clicks "Profile" →
  Access token expired →
    401 error →
      Interceptor calls refresh endpoint →
        Success? →
          Retry original request →
            User sees profile (no error)
        Failure? →
          Show login prompt (user sees error)
Enter fullscreen mode Exit fullscreen mode
  • Every request includes the access token.
  • If the server returns 401 (Unauthorized), the interceptor tries to refresh the token using the refresh token.
  • If successful, it retries the original request with the new access token.
  • If refresh fails, it logs the user out.

Token Storage Options: Pros & Cons

Storage Option Pros Cons
Local Storage Easy to use, persists after reload Vulnerable to XSS attacks
Session Storage Safer than localStorage, cleared on tab close Still vulnerable to XSS, not persistent
HttpOnly Cookies Protected from XSS, sent automatically More complex setup, vulnerable to CSRF

Summary:

  • Local Storage: Simple, but less secure.
  • Session Storage: Slightly safer, but data is lost on tab close.
  • HttpOnly Cookies: Most secure, but requires backend changes and CSRF protection.

Conclusion

With Axios interceptors, you can easily implement token rotation in your React app. This keeps users logged in seamlessly and securely, without extra hassle. Remember to choose the token storage method that best fits your security needs and app requirements.

Thank you for reading, and have a beautiful day! ❤️

Top comments (0)