score:2

I have a template that works in a system with millions of access every day.

This solved my problems with refresh token and reattemp the request without crashing

First I have a "api.js" with axios, configurations, addresses, headers. In this file there are two methods, one with auth and another without. In this same file I configured my interceptor:

import axios from "axios";
import { ResetTokenAndReattemptRequest } from "domain/auth/AuthService";
    
export const api = axios.create({
    baseURL: process.env.REACT_APP_API_URL,
    headers: {
        "Content-Type": "application/json",
    },
});

export const apiSecure = axios.create({
    baseURL: process.env.REACT_APP_API_URL,
    headers: {
        Authorization: "Bearer " + localStorage.getItem("Token"),
        "Content-Type": "application/json",
    },
    
    export default api;
    
    apiSecure.interceptors.response.use(
        function (response) {
            return response;
        },
        function (error) {
            const access_token = localStorage.getItem("Token");
            if (error.response.status === 401 && access_token) {
                return ResetTokenAndReattemptRequest(error);
            } else {
                console.error(error);
            }
            return Promise.reject(error);
        }
    );

Then the ResetTokenAndReattemptRequest method. I placed it in another file, but you can place it wherever you want:

import api from "../api";
import axios from "axios";

let isAlreadyFetchingAccessToken = false;

let subscribers = [];

export async function ResetTokenAndReattemptRequest(error) {
  try {
    const { response: errorResponse } = error;
    const retryOriginalRequest = new Promise((resolve) => {
      addSubscriber((access_token) => {
        errorResponse.config.headers.Authorization = "Bearer " + access_token;
        resolve(axios(errorResponse.config));
      });
    });
    if (!isAlreadyFetchingAccessToken) {
      isAlreadyFetchingAccessToken = true;
      await api
        .post("/Auth/refresh", {
          Token: localStorage.getItem("RefreshToken"),
          LoginProvider: "Web",
        })
        .then(function (response) {
          localStorage.setItem("Token", response.data.accessToken);
          localStorage.setItem("RefreshToken", response.data.refreshToken);
          localStorage.setItem("ExpiresAt", response.data.expiresAt);
        })
        .catch(function (error) {
          return Promise.reject(error);
        });
      isAlreadyFetchingAccessToken = false;
      onAccessTokenFetched(localStorage.getItem("Token"));
    }
    return retryOriginalRequest;
  } catch (err) {
    return Promise.reject(err);
  }
}

function onAccessTokenFetched(access_token) {
  subscribers.forEach((callback) => callback(access_token));
  subscribers = [];
}

function addSubscriber(callback) {
  subscribers.push(callback);
}

score:6

Common Approach (localStorage)

It is a common practice to store the JWT in the localStorage with

localStorage.setItem('token', 'your_jwt_eykdfjkdf...');

on login or page refresh, and make a module that exports an Axios instance with the token attached. We will get the token from localStorage

custom-axios.js

import axios from 'axios';

// axios instance for making requests 
const axiosInstance = axios.create();

// request interceptor for adding token
axiosInstance.interceptors.request.use((config) => {
  // add token to request headers
  config.headers['Authorization'] = localStorage.getItem('token');
  return config;
});

export default axiosInstance;

And then, just import the Axios instance we just created and make requests.

import axios from './custom-axios';

axios.get('/url');
axios.post('/url', { message: 'hello' });

Another approach (when you've token stored in the state)

If you have your JWT stored in the state or you can grab a fresh token from the state, make a module that exports a function that takes the token as an argument and returns an axios instance with the token attached like this:

custom-axios.js

import axios from 'axios';

const customAxios = (token) => {
  // axios instance for making requests
  const axiosInstance = axios.create();

  // request interceptor for adding token
  axiosInstance.interceptors.request.use((config) => {
    // add token to request headers
    config.headers['Authorization'] = token;
    return config;
  });

  return axiosInstance;
};

export default customAxios;

And then import the function we just created, grab the token from state, and make requests:

import axios from './custom-axios';

// logic to get token from state (it may vary from your approach but the idea is same)
const token = useSelector(token => token);

axios(token).get('/url');
axios(token).post('/url', { message: 'hello' });

Related Query

More Query from same tag