I'm implementing token authentication in my web app. My access token
expires every N minutes and than a refresh token
is used to log in and get a new access token
.
I use Axios for all my API calls. I have an interceptor set up to intercept 401
responses.
axios.interceptors.response.use(undefined, function (err) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
serviceRefreshLogin(
getRefreshToken(),
success => { setTokens(success.access_token, success.refresh_token) },
error => { console.log('Refresh login error: ', error) }
)
err.config.__isRetryRequest = true
err.config.headers.Authorization = 'Bearer ' + getAccessToken()
return axios(err.config);
}
throw err
})
Basically, as I intercept a 401 response, I want to do a login and than retry the original rejected request with the new tokens. My serviceRefreshLogin
function calls setAccessToken()
in its then
block. But the problem is that
the then
block happens later than the getAccessToken()
in the interceptor, so the retry happens with the old expired credentials.
getAccessToken()
and getRefreshToken()
simply return the existing tokens stored in the browser (they manage localStorage, cookies, etc).
How would I go about ensuring statements do not execute until a promise returns?
(Here's a corresponding issue on github: https://github.com/mzabriskie/axios/issues/266)
Just use another promise :D
axios.interceptors.response.use(undefined, function (err) {
return new Promise(function (resolve, reject) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
serviceRefreshLogin(
getRefreshToken(),
success => {
setTokens(success.access_token, success.refresh_token)
err.config.__isRetryRequest = true
err.config.headers.Authorization = 'Bearer ' + getAccessToken();
axios(err.config).then(resolve, reject);
},
error => {
console.log('Refresh login error: ', error);
reject(error);
}
);
}
throw err;
});
});
If your enviroment doesn't suport promises use polyfill, for example https://github.com/stefanpenner/es6-promise
But, it may be better to rewrite getRefreshToken to return promise and then make code simpler
axios.interceptors.response.use(undefined, function (err) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
return getRefreshToken()
.then(function (success) {
setTokens(success.access_token, success.refresh_token) ;
err.config.__isRetryRequest = true;
err.config.headers.Authorization = 'Bearer ' + getAccessToken();
return axios(err.config);
})
.catch(function (error) {
console.log('Refresh login error: ', error);
throw error;
});
}
throw err;
});