Axios Interceptor Refresh Token called multiple times on parallel axios request
Version
module: "5.0.0-1643791578.532b3d6" nuxt: ^2.15.7
Nuxt configuration
mode:
- [x] universal
- [x] spa
Nuxt configuration
Reproduction
- in the cookie, update token expiry to 1 (making the token expired)
- Run axios request on parallel
What is expected?
- RequestToken is only run once
What is actually happening?
- Refresh Token runs twice

Additional information
I have separate axios instance for calling API other than auth0
const internalAxios = $axios.create()
internalAxios.onRequest(async (config: AxiosRequestConfig): Promise<AxiosRequestConfig> => {
const auth0Strategy = app.$auth.strategy as Auth0Scheme
if (!auth0Strategy.token.status().valid()) {
if (auth0Strategy.refreshToken.status().valid()) {
await app.$auth.refreshTokens()
} else {
app.$auth.reset()
app.$auth.loginWith('auth0')
}
}
config.headers.authorization = decodeURI(auth0Strategy.token.get() as string)
return config
})
Calling refreshToken via $auth will call the refresh Token only once On further investigation calling refreshToken via $auth will trigger https://github.com/nuxt-community/auth-module/blob/c9880dc28fa13ba036f078d37ff76e1267c65d21/src/core/auth.ts#L266-L279
https://github.com/nuxt-community/auth-module/blob/c9880dc28fa13ba036f078d37ff76e1267c65d21/src/inc/refresh-controller.ts#L13-L20
But the interceptor will call this in which it doesnt check whether refresh Token is still ongoing or not https://github.com/nuxt-community/auth-module/blob/c9880dc28fa13ba036f078d37ff76e1267c65d21/src/inc/request-handler.ts#L61-L68
https://github.com/nuxt-community/auth-module/blob/c9880dc28fa13ba036f078d37ff76e1267c65d21/src/schemes/oauth2.ts#L433
Checklist
- [ ] I have tested with the latest Nuxt version and the issue still occurs
- [ ] I have tested with the latest module version and the issue still occurs
- [x] I have searched the issue tracker and this issue hasn't been reported yet
Hi @tokidoki11, we face exactly the same issue. We decorated the RequestHandlers "requestInterceptor" and added a promise queue to let other requests wait on the one who initiated it:
async requestInterceptor(config) {
// eslint-disable-next-line no-underscore-dangle,no-console
console.log('this token', this)
// eslint-disable-next-line no-underscore-dangle
if (!this._needToken(config) || config.url === this.refreshEndpoint) {
return config
}
const {
valid,
tokenExpired,
refreshTokenExpired,
isRefreshable,
} = this.scheme.check(true)
let isValid = valid
if (refreshTokenExpired) {
this.scheme.reset()
throw new ExpiredAuthSessionError()
}
if (tokenExpired) {
if (!isRefreshable) {
this.scheme.reset()
throw new ExpiredAuthSessionError()
}
// eslint-disable-next-line no-console
console.log('token expired')
if (this.promiseQueue.working) {
// eslint-disable-next-line no-console
console.log('waiting on the refresh')
await this.promiseQueue.enqueue(() => new Promise((resolve) => {
resolve()
}))
return this.requestInterceptor(config)
}
isValid = await this.promiseQueue.enqueue(async () => {
// eslint-disable-next-line no-console
console.log('enqueue new refresh')
this.scheme.refreshTokens()
.then(() => true)
.catch(() => {
this.scheme.reset()
throw new ExpiredAuthSessionError()
})
})
}
const token = this.scheme.token.get()
if (!isValid) {
// eslint-disable-next-line no-underscore-dangle
if (!token && this._requestHasAuthorizationHeader(config)) {
throw new ExpiredAuthSessionError()
}
return config
}
// eslint-disable-next-line no-underscore-dangle
return this._getUpdatedRequestConfig(config, token)
}
initializeRequestInterceptor(refreshEndpoint) {
this.refreshEndpoint = refreshEndpoint
this.interceptor = this.axios.interceptors.request.use(this.requestInterceptor.bind(this))
}
What do people think about this solution?
Regards, Flo
That's fine @florianPat - could you post the implementation your promise queue? Maybe thats a fix that can be implemented directly in the RequestHandler.initializeRequestInterceptor()?
Or would it be an alternative to request a new token just every time before it gets expired and not when it is "too late". But, I know, requests during token updating would have to be queued, too.
What do people think about this solution?
Regards, Flo
Where should I put this function?
I opened a PR to fix this: https://github.com/nuxt-community/auth-module/pull/1796
This bug has been fixed in v5.0.0-1667386184.dfbbb54.