axios: Reponse Interceptor : unable to handle an expired refresh_token (401)

Hi,

I have the following interceptor on my axios reponse :

window.axios.interceptors.response.use(
                response => {
                    return response;
                },
                error => {
                    let errorResponse = error.response;
                    if (errorResponse.status === 401 && errorResponse.config && !errorResponse.config.__isRetryRequest) {
                        return this._getAuthToken()
                            .then(response => {
                                this.setToken(response.data.access_token, response.data.refresh_token);
                                errorResponse.config.__isRetryRequest = true;
                                errorResponse.config.headers['Authorization'] = 'Bearer ' + response.data.access_token;
                                return window.axios(errorResponse.config);
                            }).catch(error => {
                                return Promise.reject(error);
                            });
                    }
                    return Promise.reject(error);
                }
            );

The _getAuthToken method is :

_getAuthToken() {
            if (!this.authTokenRequest) {
                this.authTokenRequest = window.axios.post('/api/refresh_token', {
                    'refresh_token': localStorage.getItem('refresh_token')
                });
                this.authTokenRequest.then(response => {
                    this.authTokenRequest = null;
                }).catch(error => {
                    this.authTokenRequest = null;
                });
            }

            return this.authTokenRequest;
        }

The code is heavily inspired by https://github.com/axios/axios/issues/266#issuecomment-335420598.

Summary : when the user makes a call to the API and if his access_token has expired (a 401 code is returned by the API) the app calls the /api/refresh_token endpoint to get a new access_token. If the refresh_token is still valid when making this call, everything works fine : I get a new access_token and a new refresh_token and the initial API call requested by the user is made again and returned correctly.

The problem occurs when the refresh_token has also expired.
In that case, the call to /api/refresh_token returns a 401 and nothing happens. I tried several things but I’m unable to detect that in order to redirect the user to the login page of the app.
I found that in that case the if (!this.authTokenRequest) statement inside the _getAuthToken method returns a pending Promise that is never resolved. I don’t understand why this is a Promise. In my opinion it should be null…

I’m a newbie with Promises so I may be missing something ! Thanks for any help !

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 19 (1 by maintainers)

Most upvoted comments

I may have found a way much simpler to handle this : use axios.interceptors.response.eject() to disable the interceptor when I call the /api/refresh_token endpoint, and re-enable it after.

The code :

createAxiosResponseInterceptor() {
            this.axiosResponseInterceptor = window.axios.interceptors.response.use(
                response => {
                    return response;
                },
                error => {
                    let errorResponse = error.response;
                    if (errorResponse.status === 401) {
                        window.axios.interceptors.response.eject(this.axiosResponseInterceptor);
                        return window.axios.post('/api/refresh_token', {
                            'refresh_token': this._getToken('refresh_token')
                        }).then(response => {
                            this.setToken(response.data.access_token, response.data.refresh_token);
                            errorResponse.config.headers['Authorization'] = 'Bearer ' + response.data.access_token;
                            this.createAxiosResponseInterceptor();
                            return window.axios(errorResponse.config);
                        }).catch(error => {
                            this.destroyToken();
                            this.createAxiosResponseInterceptor();
                            this.router.push('/login');
                            return Promise.reject(error);
                        });
                    }
                    return Promise.reject(error);
                }
            );
        },

Does it looks good or bad ? Any advice or comment appreciated.

@NanoDev777 : with axios 0.16.2 and follow-redirect 1.2.6 it works, with axios 0.16.2 and follow-redirect 1.3.0 it doesn’t.

@ligne13 : the problem with your solution, from what I understand, is that it won’t works as well, because the bug is somewhere between axios and follow-redirect.

let errorResponse = error.response;
if (errorResponse.status === 401)` 

That code will fail because error.response is undefined, in my case it’s a Network Error with some text, kinda like here : https://github.com/axios/axios/issues/383