React Axios Interceptor - How to use refresh tokens while polling api every 5 seconds

Viewed 552

I have a refresh token that needs to be refreshed every 2 minutes. In order to do this I check for a 401 response (using an axios interceptor). If the response from the server is "expired" I retry the api call with a new token I get from making a new POST request to a token endpoint.


const REFRESH_TOKEN_ERRORS = ["token_expired", "invalid_token"];

export const apiHeaders = {
  Authorization: `Bearer ${window.localStorage.getItem("jwt")}`,
  "Content-Type": "application/json",
};

axios.interceptors.response.use(
  function (response) {
    return response;
  },
  async function (error) {
    const originalRequest = error.config;
    const apiUrl = String(process.env.REACT_APP_API_ROOT);

    if (originalRequest.url.includes(apiUrl)) {
      // REFRESH_TOKEN_ERRORS contains a list of specific error messages in the API body that should trigger refresh & retry
      if (
        REFRESH_TOKEN_ERRORS.includes(error.response.data.error_description) &&
        !originalRequest._retry
      ) {
        originalRequest._retry = true;
        // refresh and set the new headers
        const newToken = await getNewRefreshToken();
        if (newToken === null) {
          return null;
        }

        axios.defaults.headers.common["Authorization"] = "Bearer " + newToken;

        if (originalRequest.config) {
          originalRequest.config.headers = apiHeaders;
        } else {
          originalRequest.headers = apiHeaders;
        }

        // Retry the original request
        return axios(originalRequest);
      }
    } else {
      console.log("Unhandled error", error.response.data);
    }

    return Promise.reject(error);
  }
);

async function getNewRefreshToken() {
  // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#refresh-the-access-token

  const tokenUrl = process.env.REACT_APP_OAUTH_ROOT_URL + "/oauth2/token";
  const clientId = process.env.REACT_APP_OAUTH_CLIENT_ID;
  const refreshToken = window.localStorage.getItem("jwt_refresh_token");

  try {
    const res = await axios.post(
      tokenUrl,
      {},
      {
        headers: {
          grant_type: "refresh_token",
          client_id: String(clientId),
          refresh_token: String(refreshToken),
        },
      }
    );
    window.localStorage.setItem("jwt", res.data.access_token);
    window.localStorage.setItem("jwt_exp", res.data.exp);
    window.localStorage.setItem("jwt_refresh_token", res.data.refresh_token);

    return res.data.access_token;
  } catch (err) {
    console.log("Refresh error", err);

    window.localStorage.removeItem("jwt");
    window.localStorage.removeItem("jwt_refresh_token");
    window.localStorage.removeItem("jwt_exp");
    return null;
  }
}

This works fine until I try and poll the api at intervals (e.g,. every 5 seconds)

This triggers the axios interceptor and the function is run again (effectively resetting my refresh token every 5 seconds instead of every 2 minutes).

How can I ensure that the refresh token is not reset every 5 seconds/how can I implement polling and refreshing of tokens without the above conflicts?

With my current implementation (whilst polling) I get a "invalid_token" response from the server after 2 minutes and the interceptor does not perform a silent re-request of the refresh token as I would like

0 Answers
Related