skip to Main Content

I have setup for axios and react-query together for my food application . I want to implement refreshing token only once for more than 2 parallel request and retry all the pending failed request . I searched so many solutions on the web , tried them but none of them worked . Here is my code reference .

let isRefreshing = false;
let failedQueue = [];

async function refreshToken() {
  const response = await normalRoute.post("/users/refresh");
  return response.data.accessToken;
}

privateRoute.interceptors.response.use(
  (response) => response,
  async (error) => {
    if (error.response.status === 401) {
      failedQueue.push(error.config);
      if (!isRefreshing) {
        isRefreshing = true;
        const token = await refreshToken();
        store.dispatch(setAccessToken(token));

        const promsieArray = failedQueue.map((config) => {
          config.headers.authorization = `Bearer ${token}`;
          return privateRoute(config);
        });

        failedQueue = [];
        return Promise.resolve(promsieArray);
      }
    }
  }
);

2

Answers


  1. Chosen as BEST ANSWER

    Thankyou I found solution ..

    import { setAccessToken } from "../Store/authSlice";
    import store from "../Store";
    import { normalRoute } from "./axios";
    
    // for multiple requests
    let isRefreshing = false;
    let failedQueue = [];
    
    const processQueue = (error, token = null) => {
      failedQueue.forEach((prom) => {
        if (error) {
          prom.reject(error);
        } else {
          prom.resolve(token);
        }
      });
    
      failedQueue = [];
    };
    
    const interceptor = (axiosInstance) => (error) => {
      const _axios = axiosInstance;
      const originalRequest = error.config;
    
      if (error.response.status === 401 && !originalRequest._retry) {
        if (isRefreshing) {
          return new Promise(function (resolve, reject) {
            failedQueue.push({ resolve, reject });
          })
            .then((token) => {
              originalRequest.headers["Authorization"] = "Bearer " + token;
              return _axios.request(originalRequest);
            })
            .catch((err) => {
              return Promise.reject(err);
            });
        }
    
        originalRequest._retry = true;
        isRefreshing = true;
    
        return new Promise(async (resolve, reject) => {
          try {
            const response = await normalRoute.post("/users/refresh");
            _axios.defaults.headers.common["Authorization"] =
              "Bearer " + response.data.accessToken;
            originalRequest.headers["Authorization"] = "Bearer " + response.data.accessToken;
            processQueue(null, response.data.accessToken);
            resolve(_axios(originalRequest));
          } catch (err) {
            processQueue(err, null);
            reject(err);
          } finally {
            isRefreshing = false;
          }
        });
      }
    
      return Promise.reject(error);
    };
    
    export default interceptor;
    
    import axios from "axios";
    import interceptor from "./authInterceptor";
    
    const privateInstance = axios.create({
      baseURL: "http://localhost:5000/api/v1",
    });
    
    privateInstance.defaults.withCredentials = true;
    
    privateInstance.interceptors.response.use(undefined, interceptor(privateInstance));
    
    export default privateInstance;
    
    

  2. I think your solution is a little too complicated. I’d simply store the Promise returned by refreshToken() untill it is fulfilled and let all incoming 401s await the same Promise, then retrying.

    let refreshPromise = null;
    const clearPromise = () => refreshPromise = null;
    
    async function refreshToken() {
      const response = await normalRoute.post("/users/refresh");
      return response.data.accessToken;
    }
    
    privateRoute.interceptors.response.use(
      undefined,
      async (error) => {
        const config = error.config;
        
        if (error.response.status === 401 && !config._retry) {
          config._retry = true;
    
          if (!refreshPromise) {
            refreshPromise = refreshToken().finally(clearPromise);
          }
    
          const token = await refreshPromise;
          config.headers.authorization = `Bearer ${token}`;
    
          return privateRoute(config);
        }
      }
    );
    
    

    And kill that Promise when it is fulfilled so that later 401s have a chance to refresh the token yet again.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search