I'm using this code to intercept every HTTP call in Axios, in order to keep the user logged in by refreshing the JWT token when it expires:
const { token } = window.localStorage;
const axiosInstance = axios.create({
baseURL: process.env.VUE_APP_API_ENDPOINT,
withCredentials: false,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: token && token.length > 0 ? `Bearer ${token}` : ""
}
});
axiosInstance.interceptors.request.use(request => {
if (request.url.endsWith("refresh")) { // prevent infinite loop
return request;
}
const { expiryDate } = window.localStorage;
const currentTimestamp = Math.round(new Date().getTime() / 1000);
if (expiryDate && currentTimestamp >= expiryDate) {
console.log("token expired.");
return store
.dispatch("auth/refreshToken") // refreshToken action will place the token in localStorage.token
.then(() => {
const newToken = window.localStorage.token;
request.headers.Authorization = `Bearer ${newToken}`;
return Promise.resolve(request);
})
.catch(() => Promise.resolve(request));
}
console.log("token not expired.");
return request;
});
This used to work fine until I added more requests at page load, each request is trying to refresh the token which causes an error in the back-end. How do I solve this problem? I have searched a lot but all the solutions I found were for interceptors.response
like this one, I'd like to use interceptors.request
because logging in is optional and there will be no 401 response if the user is not logged in.
EDIT: Based on the answer from User 28 below the code has changed to:
axiosInstance.interceptors.request.use(async config => {
if (config.url.endsWith("refresh")) {
// if we are making a refresh token call, return it to prevent infinite loop
return config;
}
await axiosInstance.refreshingToken;
const { expiryDate } = window.localStorage; // token expiry date
const currentTimestamp = Math.round(new Date().getTime() / 1000);
if (expiryDate && currentTimestamp >= expiryDate) {
axiosInstance.refreshingToken = store.dispatch("auth/refreshToken"); // this will update the token in localstorage
await axiosInstance.refreshingToken;
}
const { token } = window.localStorage; // grab the token from localstorage
config.headers.Authorization = `Bearer ${token}`;
return config;
});
You need a global variable to determine you have to wait or not. The easiest way is to assign it to the axios instance.
Example Code:
axios.interceptors.request.use(async config => {
if (isRefreshTokenRequest) {
return config
}
if (axios.refreshingToken) {
await axios.refreshingToken
}
if (isTokenExpired) {
axios.refreshingToken = store.dispatch('auth/refreshToken')
await axios.refreshingToken
}
// set authorization header
return config
})