Handling Access and Refresh Tokens using Axios Interceptors.

Bhavik Savaliya
The Startup
Published in
4 min readJul 31, 2019

Axios is a promise-based HTTP client which is written in JavaScript to perform HTTP communications. It has one powerful feature called Interceptors. Axios interceptors allow you to run your code or modify the request and/or response before the request and/or response reaches their destination.

I assume that you are familiar with Axios libary, Access tokens and Refresh tokens.

Axios Interceptors

Axios interceptors allow you to run your code or modify the request and/or response before the request and/or response is started.

In simple words,

  • It allows you to write or execute a piece of your code before the request gets sent.
  • It allows you to write or execute a piece of your code before response reaches the calling end.

Before we implement interceptors, I have created one LocalStorageService.js. It is one service which allows us to store our data to LocalStorage. Whenever we need LocalStorageService, we just simple Inject it and use it.

// LocalStorageService.jsconst LocalStorageService = (function(){ var _service; function _getService() {
if(!_service) {
_service = this;
return _service
}
return _service
}
function _setToken(tokenObj) {
localStorage.setItem(‘access_token’, tokenObj.access_token);
localStorage.setItem(‘refresh_token’, tokenObj.refresh_token);
}
function _getAccessToken() {
return localStorage.getItem(‘access_token’);
}
function _getRefreshToken() {
return localStorage.getItem(‘refresh_token’);
}
function _clearToken() {
localStorage.removeItem(‘access_token’);
localStorage.removeItem(‘refresh_token’);
}
return {
getService : _getService,
setToken : _setToken,
getAccessToken : _getAccessToken,
getRefreshToken : _getRefreshToken,
clearToken : _clearToken
}
})();export default LocalStorageService;

Imports

import axios from "axios";import LocalStorageService from "./services/storage/localstorageservice";import router from "./router/router";// LocalstorageServiceconst localStorageService = LocalStorageService.getService();

Add a request Interceptor

I will try to modify each request header to set access token in the Authorization HTTP header.

So we have two callbacks in request interceptor one with parameter config object and another one with the error object.

Config is the object of AxiosRequestConfig which contains URL, base URL, headers request, body data, response type, timeout, etc.

In short, it contains all of the information about your request.

I am getting an Access token using localStorageService and modifying the Config object’s headers. I am setting access token in the Authorization HTTP header and also setting Content-type as “application/json”. After my these modifications, I am returning the config object.

Here is how I am doing that.

// Add a request interceptor
axios.interceptors.request.use(
config => {
const token = localStorageService.getAccessToken();
if (token) {
config.headers['Authorization'] = 'Bearer ' + token;
}
// config.headers['Content-Type'] = 'application/json';
return config;
},
error => {
Promise.reject(error)
});

Add a response Interceptor

We also have two callbacks in response interceptors. One gets executed when we have a response from the Http call and another one gets executed when we have an error.

We will simply return our response when there is no error. We’ll handle the error if there is any.

As you can see in the following condition I am checking “Is Request has a 401 status code?” and “Is it failed again?”

if (error.response.status === 401 && !originalRequest._retry) {...}

If the request failed again then return Error object with Promise

return Promise.reject(error);

I have one endpoint(/v1/Auth/token) where If I provide a valid refresh token then It will return new Access token and Refresh token either It will fail with 401 status code.

So I am doing following steps If I got successfully Access token and Refresh Token from my endpoint(/v1/Auth/token).

  1. I will put an Access token and Refresh token to LocalStorage using localStorageService.
  2. Change Authorization header with the new Access token in originalRequest which is failed cause of not valid access token
  3. return originalRequest object with Axios.

Here is how I am doing that.

axios.interceptors.response.use((response) => {
return response
},
function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {

originalRequest._retry = true;
return axios.post('/auth/token',
{
"refresh_token": localStorageService.getRefreshToken()
})
.then(res => {
if (res.status === 201) {
// 1) put token to LocalStorage
localStorageService.setToken(res.data);

// 2) Change Authorization header
axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorageService.getAccessToken();

// 3) return originalRequest object with Axios.
return axios(originalRequest);
}
})
}

If my refresh token is not valid then my endpoint(/v1/Auth/token) will come with 401 status code and If we do not handle it then it will go in an infinite loop.

So here is my condition to stop going in an infinite loop, If the condition is true I just simple redirect to the Login page.

if (error.response.status === 401 &&originalRequest.url === 'http://13.232.130.60:8081/v1/auth/token') {
router.push('/login');
return Promise.reject(error);
}

Here is my Full code of response Interceptor.

axios.interceptors.response.use((response) => {
return response
},
function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {

originalRequest._retry = true;
return axios.post('/auth/token',
{
"refresh_token": localStorageService.getRefreshToken()
})
.then(res => {
if (res.status === 201) {
// 1) put token to LocalStorage
localStorageService.setToken(res.data);

// 2) Change Authorization header
axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorageService.getAccessToken();

// 3) return originalRequest object with Axios.
return axios(originalRequest);
}
})
}

// return Error object with Promise
return Promise.reject(error);
});

Here is full code of axios.js

import axios from "axios";
import LocalStorageService from "./services/storage/localstorageservice";
import router from "./router/router";

// LocalstorageService
const localStorageService = LocalStorageService.getService();

// Add a request interceptor
axios.interceptors.request.use(
config => {
const token = localStorageService.getAccessToken();
if (token) {
config.headers['Authorization'] = 'Bearer ' + token;
}
// config.headers['Content-Type'] = 'application/json';
return config;
},
error => {
Promise.reject(error)
});



//Add a response interceptor

axios.interceptors.response.use((response) => {
return response
}, function (error) {
const originalRequest = error.config;

if (error.response.status === 401 && originalRequest.url ===
'http://13.232.130.60:8081/v1/auth/token) {
router.push('/login');
return Promise.reject(error);
}

if (error.response.status === 401 && !originalRequest._retry) {

originalRequest._retry = true;
const refreshToken = localStorageService.getRefreshToken();
return axios.post('/auth/token',
{
"refresh_token": refreshToken
})
.then(res => {
if (res.status === 201) {
localStorageService.setToken(res.data);
axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorageService.getAccessToken();
return axios(originalRequest);
}
})
}
return Promise.reject(error);
});

I hope you understand the Axios interceptor. Enjoy intercepting request and response.

Happy Coding!

--

--