5

OK, so this is my third wall I'm hitting with this problem, first off here's my axios config file

import axios from "axios";
import { withRouter } from "react-router-dom";

const instance = axios.create({
  baseURL: process.env.REACT_APP_API,
  headers: {
    "content-type": "application/json"
  },
  responseType: "json"
});
function pushToLogin() {
  this.props.history.push("/");
}
function createAxiosResponseInterceptor(axiosInstance) {
  const interceptor = axiosInstance.interceptors.response.use(
    response => response,
    error => {
      // Reject promise if usual error
      console.log(error);
      if (error.response.status !== 401) {
        return Promise.reject(error);
      }
      /*
       * When response code is 401, try to refresh the token.
       * Eject the interceptor so it doesn't loop in case
       * token refresh causes the 401 response
       */
      axiosInstance.interceptors.response.eject(interceptor);
      return axiosInstance
        .post("token/refresh/", {
          refresh: localStorage.getItem("refreshToken")
        })
        .then(response => {
          localStorage.setItem("accessToken", response.data.access);
          error.response.config.headers["Authorization"] =
            "Bearer " + response.data.access;
          return axiosInstance(error.response.config);
        })
        .catch(error => {
          localStorage.setItem("accessToken", null);
          localStorage.setItem("refreshToken", null);
          pushToLogin();
          return Promise.reject(error);
        })
        .finally(createAxiosResponseInterceptor(this));
    }
  );
}

createAxiosResponseInterceptor(instance);

export default withRouter(instance);

What I want, is: Request gets 401 > Get a new token and store it > repeat request > if it's good to go we're good, if not return it's normal error.

What's happening: Request gets 401 > Gets new token and stores it > Repeat request> if it's good we're good to go, if not, it goes into that final catch block on my interceptor, which I thought was intended specifically for the refreshToken request, or atleast that's what I desire it to be.

Bonus question: I'd like to push to login on refreshToken failure as u can see, I'm trying to use withRouter for that but I cannot read props of undefined on that push line, so that's another issue.

Many thanks!

Edit:Even if this request has been done by other ways I'd like to know what's wrong with my code than learn someone else's.

Omar Hussein
  • 1,057
  • 2
  • 13
  • 31
  • 2
    I created the _axios-middleware_ module to isolate the behaviour of interceptors and made some examples, one of which is exactly what you're asking: https://emileber.github.io/axios-middleware/#/examples/auth-middleware – Emile Bergeron Nov 07 '19 at 15:42
  • Possible duplicate of [Axios Interceptors retry original request and access original promise](https://stackoverflow.com/questions/51563821/axios-interceptors-retry-original-request-and-access-original-promise) – Emile Bergeron Nov 07 '19 at 15:44
  • @EmileBergeron Even if it's a similar end goal I'd rather figure out the problem with my own implementation that has a bug than have to learn someone else's. – Omar Hussein Nov 07 '19 at 15:55
  • What I meant is that you can use that example to implement your own interceptor. – Emile Bergeron Nov 07 '19 at 15:58
  • Like, instead of ejecting the interceptor within the interceptor (which is a recipe for problems), just add a check to avoid loops, like in the example I linked. – Emile Bergeron Nov 07 '19 at 16:00

1 Answers1

9

I switched to using the axios-auth-refresh package, the config code I'm using is here:

import axios from "axios";
import createAuthRefreshInterceptor from "axios-auth-refresh";

const instance = axios.create({
  baseURL: process.env.REACT_APP_API,
  headers: {
    "content-type": "application/json"
  },
  responseType: "json"
});
const refreshAuthLogic = failedRequest =>
  instance
    .post("token/refresh/", {
      refresh: localStorage.getItem("refreshToken")
    })
    .then(tokenRefreshResponse => {
      localStorage.setItem("accessToken", tokenRefreshResponse.data.access);
      failedRequest.response.config.headers["Authorization"] =
        "Bearer " + tokenRefreshResponse.data.access;
      return Promise.resolve();
    })
    .catch(error => {
      console.log("refresh fail");
      localStorage.setItem("accessToken", null);
      localStorage.setItem("refreshToken", null);
      //pushToLogin();
      return Promise.reject(error);
    });
createAuthRefreshInterceptor(instance, refreshAuthLogic);

export default instance;
Omar Hussein
  • 1,057
  • 2
  • 13
  • 31
  • Note you should be using different axios instance for the `/token/refresh` call; otherwise 40x response would hang. – laur Mar 11 '22 at 11:40