-
-
Save bragma/f68391596de71e1bfae066be80c259dc to your computer and use it in GitHub Desktop.
import axios from 'axios' | |
export default class ApiClient { | |
constructor(baseUrl, tokenStorage) { | |
this.http = axios.create({ | |
baseURL: baseUrl | |
}) | |
this.tokenStorage = tokenStorage | |
this.setupTokenInterceptors() | |
} | |
setupTokenInterceptors() { | |
this.setTokenInterceptor = this.http.interceptors.request.use(request => { | |
let token = this.tokenStorage.getAccessToken() | |
if (token) { | |
request.headers['Authorization'] = `Bearer ${token}` | |
} | |
return request | |
}) | |
this.refreshTokenInterceptor = this.http.interceptors.response.use(undefined, async error => { | |
const response = error.response | |
if (response) { | |
if (response.status === 401 && error.config && !error.config.__isRetryRequest) { | |
try { | |
await this.tokenStorage.refreshAccessToken() | |
} catch (authError) { | |
// refreshing has failed, but report the original error, i.e. 401 | |
return Promise.reject(error) | |
} | |
// retry the original request | |
error.config.__isRetryRequest = true | |
return this.http(error.config) | |
} | |
} | |
return Promise.reject(error) | |
}) | |
} | |
} |
export default class TokenService { | |
constructor(baseKey) { | |
this.accessTokenKey = `${baseKey}-access-token` | |
this.getAccessToken = this.getAccessToken.bind(this) | |
this.setAccessToken = this.setAccessToken.bind(this) | |
this.removeAccessToken = this.removeAccessToken.bind(this) | |
this.tokenRefreshHandler = null | |
this.refreshing = null | |
this.refreshAccessToken = this.refreshAccessToken.bind(this) | |
} | |
getAccessToken() { | |
return localStorage.getItem(this.accessTokenKey) | |
} | |
setAccessToken(accessToken) { | |
localStorage.setItem(this.accessTokenKey, accessToken) | |
} | |
removeAccessToken() { | |
localStorage.removeItem(this.accessTokenKey) | |
} | |
refreshAccessToken() { | |
if (!this.tokenRefreshHandler) throw new Error('No token refresh handler installed') | |
if (!this.refreshing) { | |
this.refreshing = this.tokenRefreshHandler() | |
this.refreshing.then(() => { | |
this.refreshing = null | |
}) | |
} | |
return this.refreshing | |
} | |
} |
Don't yo think this rejection https://gist.github.com/bragma/f68391596de71e1bfae066be80c259dc#file-apiclient-js-L33 should be marked with the
__isRetryRequest
flag, in order to avoid inifnite loops when refresh fails?
Hmmm... I have to admit I am a bit rusty on this code and I may be wrong, but actually I don't think it will infinitely loop, because due to the return, the same request will not be retried. I have to admin I am using this code in production, but the actual request to get a new token does not use axios, so it may not be looping due to this.
Can you describe which flow could cause infinite loop? Do you mean if the tokenRefreshHandler uses axios to get a token an receives a 401 response?
Do you mean if the tokenRefreshHandler uses axios to get a token an receives a 401 response?
Yes, that is the case I had in mind, but looking again at the snippet, I think that token refresh request would fall here https://gist.github.com/bragma/f68391596de71e1bfae066be80c259dc#file-apiclient-js-L37
So I think you are right, it won't cause infinite loops
Don't yo think this rejection https://gist.github.com/bragma/f68391596de71e1bfae066be80c259dc#file-apiclient-js-L33 should be marked with the
__isRetryRequest
flag, in order to avoid inifnite loops when refresh fails?