Axios Interceptors tutorial with Refresh Token example

In this tutorial, I will show you how to work with Axios Interceptors: eject, error, 401 status, handling infinite loop and Refresh Token example.

Related Post:
Axios request: Get/Post/Put/Delete example
React Refresh Token with Axios Interceptors
React + Redux: Refresh Token with Axios Interceptors
Vue Refresh Token with Axios Interceptors
Vue 3 Refresh Token with Axios Interceptors

Axios interceptors Overview

An Interceptor can be understood as a filter of http requests and responses before they are actually sent or received.

This allows us to manipulate the header, body, parameters of the requests sent to the server as well as check the responses received from the server for the most reasonable.

We can intercept requests or responses before they are handled by then or catch.

// Request interceptor
axios.interceptors.request.use((config) => {...});

// Response interceptor
axios.interceptors.response.use((response) => {...});

Axios interceptors Error

You can also get request errors or response errors in Axios interceptors:

– Request:

axios.interceptors.request.use(
  (config) => {
    // Do something before request is sent
    return config;
  },
  (error) => {
    // Do something with request error
    return Promise.reject(error);
  });

– Response:

axios.interceptors.response.use(
  (response) => { // Any status code from range of 2xx
    // Do something with response data
    return response;
  },
  (error) => { // Any status codes outside range of 2xx
    // Do something with response error
    return Promise.reject(error);
  });

Axios interceptors Eject

You can add an Interceptors to an instance of Axios.

const instance = axios.create(...);
const myInterceptor = instance.interceptors.request.use(...);

And then, remove the interceptor:

axios.interceptors.request.eject(myInterceptor);

Axios interceptors for 401 status

If we want to handle 401 status on interceptor response or ANOTHER_STATUS_CODE, just check error.response.status like this:

axios.interceptors.response.use(
  (response) => {
    return res;
  },
  async (error) => {
    if (error.response) {
      if (error.response.status === 401) {
        // Do something, call refreshToken() request for example;
        // return a request
        return axios_instance(config);
      }

      if (error.response.status === ANOTHER_STATUS_CODE) {
        // Do something 
        return Promise.reject(error.response.data);
      }
    }

    return Promise.reject(error);
  }
);

Axios interceptor Infinite loop

In case the request is failed again, and the server continue to return 401 status code, it may go to Infinite loop. How to handle this?

We use a flag call _retry on original Request (config). _retry is set to true right after the first time we meet 401 status.

axios.interceptors.response.use(
  (response) => {
    return res;
  },
  async (error) => {
    const originalConfig = error.config;
    if (error.response) {
      if (error.response.status === 401 && !originalConfig._retry) {
        originalConfig._retry = true;

        // Do something, call refreshToken() request for example;
        // return a request
        return axios_instance(config);
      }

      if (error.response.status === ANOTHER_STATUS_CODE) {
        // Do something 
        return Promise.reject(error.response.data);
      }
    }

    return Promise.reject(error);
  }
);

Axios Interceptors with Refresh Token example

In previous posts, we have implement JWT refresh token on server side:

The diagram shows flow of how we implemented Authentication process with Access Token and Refresh Token.

axios-interceptors-refresh-token-flow

– A legal JWT must be added to HTTP Header if Client accesses protected resources.
– A refreshToken will be provided at the time user signs in.

This is Client that we’re gonna create:

– Login and receive access Token and refresh Token:

axios-interceptors-refresh-token-signin-request

– Access resource successfully with accessToken.

axios-interceptors-refresh-token-access-resource

– Continue to access resource with accessToken but when the server returns response telling that the accessToken is expired.

Interceptor automatically sends /refreshtoken request, get new accessToken.

axios-interceptors-refresh-token-access-resource-new-token

– Wait for the Refresh Token expired, send a new Request.

axios-interceptors-refresh-token-expire

Implement Refresh Token using Axios Interceptors

First we create HTML file with following code.

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Axios Interceptors - Refresh Token</title>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
    />
  </head>
  <body>
    <div class="container my-3" style="max-width: 600px">
      <h2>Axios Interceptors - Refresh Token</h2>

      <div class="card mt-3">
        <div class="card-header">Get Tokens</div>
        <div class="card-body">
          <button class="btn btn-primary" onclick="login()">Login</button>
          <button class="btn btn-warning" onclick="clearOutput1()">
            Clear
          </button>
        </div>
        <div class="card-body" id="getResult1"></div>
      </div>

      <div class="card mt-3">
        <div class="card-header">Access Data</div>
        <div class="card-body">
          <button class="btn btn-primary" onclick="getData()">Get Data</button>
          <button class="btn btn-warning" onclick="clearOutput2()">
            Clear
          </button>
        </div>
        <div class="card-body" id="getResult2"></div>
      </div>

      <p class="mt-3">
        ©
        <a href="https://www.bezkoder.com" target="_blank">bezkoder.com</a>
      </p>
    </div>

    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

    <script src="main.js"></script>
  </body>
</html>

Now we use Axios Interceptors to work with Refresh Token.

main.js

function getLocalAccessToken() {
  const accessToken = window.localStorage.getItem("accessToken");
  return accessToken;
}

function getLocalRefreshToken() {
  const refreshToken = window.localStorage.getItem("refreshToken");
  return refreshToken;
}

const instance = axios.create({
  baseURL: "http://localhost:8080/api",
  headers: {
    "Content-Type": "application/json",
  },
});

instance.interceptors.request.use(
  (config) => {
    const token = getLocalAccessToken();
    if (token) {
      config.headers["x-access-token"] = token;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

instance.interceptors.response.use(
  (res) => {
    return res;
  },
  async (err) => {
    const originalConfig = err.config;

    if (err.response) {
      // Access Token was expired
      if (err.response.status === 401 && !originalConfig._retry) {
        originalConfig._retry = true;

        try {
          const rs = await refreshToken();
          const { accessToken } = rs.data;
          window.localStorage.setItem("accessToken", accessToken);
          instance.defaults.headers.common["x-access-token"] = accessToken;

          return instance(originalConfig);
        } catch (_error) {
          if (_error.response && _error.response.data) {
            return Promise.reject(_error.response.data);
          }

          return Promise.reject(_error);
        }
      }

      if (err.response.status === 403 && err.response.data) {
        return Promise.reject(err.response.data);
      }
    }

    return Promise.reject(err);
  }
);

function signin() {
  return instance.post("/auth/signin", {
    username: "zkoder",
    password: "12345678",
  });
}

function refreshToken() {
  return instance.post("/auth/refreshtoken", {
    refreshToken: getLocalRefreshToken(),
  });
}

function getUserContent() {
  return instance.get("/test/user");
}

async function login() {
  var resultElement = document.getElementById("getResult1");
  resultElement.innerHTML = "";

  try {
    const res = await signin();

    const { accessToken, refreshToken } = res.data;
    window.localStorage.setItem("accessToken", accessToken);
    window.localStorage.setItem("refreshToken", refreshToken);

    resultElement.innerHTML =
      "<pre>" +
      JSON.stringify({ accessToken, refreshToken }, null, 2) +
      "</pre>";
  } catch (err) {
    resultElement.innerHTML = err;
  }
}

async function getData() {
  var resultElement = document.getElementById("getResult2");
  resultElement.innerHTML = "";

  try {
    const res = await getUserContent();

    resultElement.innerHTML =
      "<pre>" + JSON.stringify(res.data, null, 2) + "</pre>";
  } catch (err) {
    resultElement.innerHTML = "<pre>" + JSON.stringify(err, null, 2) + "</pre>";
  }
}

function clearOutput1() {
  var resultElement = document.getElementById("getResult1");
  resultElement.innerHTML = "";
}

function clearOutput2() {
  var resultElement = document.getElementById("getResult2");
  resultElement.innerHTML = "";
}

Conclusion

Today we’ve known the way to work with Refresh Token using Axios Interceptors. I also show you how to use Axios Interceptors eject, error along with handling 401 status and infinite loop.

You will need back-end code that implements JWT with Refresh Token in one of following tutorials:

You can also apply this in:
React Refresh Token with Axios Interceptors
React + Redux: Refresh Token with Axios Interceptors
Vue Refresh Token with Axios Interceptors
Vue 3 Refresh Token with Axios Interceptors

Further Reading

Axios request: Get/Post/Put/Delete example

– Nodejs Express + React/Angular/Vue:

– Spring Boot + React/Angular/Vue:

Leave a Reply

Your email address will not be published. Required fields are marked *