API request using JWT is implemented in flask and Vue.js. The JWT is stored in a cookie, and the server validates the JWT for each request.
If the token has expired, a 401 error will be returned. f you receive a 401 error, refresh the token as in the code below, The original API request is made again. The following code is common to all requests.
http.interceptors.response.use((response) => {
return response;
}, error => {
if (error.config && error.response && error.response.status === 401 && !error.config._retry) {
error.config._retry = true;
http
.post(
"/token/refresh",
{},
{
withCredentials: true,
headers: {
"X-CSRF-TOKEN": Vue.$cookies.get("csrf_refresh_token")
}
}
)
.then(res => {
if (res.status == 200) {
const config = error.config;
config.headers["X-CSRF-TOKEN"] = Vue.$cookies.get("csrf_access_token");
return Axios.request(error.config);
}
})
.catch(error => {
});
}
return Promise.reject(error);
});
When making multiple API requests at the same time with the token expired Uselessly refreshing the token. For example, requests A, B, and C are executed almost simultaneously. Since 401 is returned with each request, Each interceptor will refresh the token.
There is no real harm, but I don't think it's a good way. There is a good way to solve this.
My idea is to first make an API request to validate the token expiration, This method is to make requests A, B, and C after verification and refresh are completed. Because cookies are HttpOnly, the expiration date cannot be verified on the client side (JavaScript).
Sorry in poor english...
Protecting your refresh tokens Concretely, refresh tokens exposed to the browser should be protected with Refresh Token Rotation (RTR). In a nutshell, RTR makes refresh tokens only valid for one-time use. Each time a refresh token is used, the security token service issues a new access token and a new refresh token.
A refresh token doesn't expire until the new access token obtained from it was executed in an API call. When the new access token has been used, the refresh token that was used to obtain it automatically expires.
So in summary when authorization is successful you need to issue two token ACCESS_TOKEN and REFRESH_TOKEN. When ACCESS_TOKEN expires you need to call another api with REFRESH_TOKEN to get new ACCESS_TOKEN. The client application can get a new access token as long as the refresh token is valid and unexpired.
When an access token expires, a refresh token is used to get a new access token and it also returns a new refresh token. Now if this new access token expires & a new/updated refresh token is used to get the next access token, it will also receive a newer refresh token.
What you'll need to do is maintain some state outside the interceptor. Something that says
Hold up, I'm in the middle of getting a new token.
This is best done by keeping a reference to a Promise
. That way, the first 401 interceptor can create the promise, then all other requests can wait for it.
let refreshTokenPromise // this holds any in-progress token refresh requests
// I just moved this logic into its own function
const getRefreshToken = () => http.post('/token/refresh', {}, {
withCredentials: true,
headers: { 'X-CSRF-TOKEN': Vue.$cookies.get('csrf_refresh_token') }
}).then(() => Vue.$cookies.get('csrf_access_token'))
http.interceptors.response.use(r => r, error => {
if (error.config && error.response && error.response.status === 401) {
if (!refreshTokenPromise) { // check for an existing in-progress request
// if nothing is in-progress, start a new refresh token request
refreshTokenPromise = getRefreshToken().then(token => {
refreshTokenPromise = null // clear state
return token // resolve with the new token
})
}
return refreshTokenPromise.then(token => {
error.config.headers['X-CSRF-TOKEN'] = token
return http.request(error.config)
})
}
return Promise.reject(error)
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With