Axios Interceptor Refresh Token Typescript
▌Introduction
Axios is the Promise based HTTP client, and is often used in my Vue.js
projects.
This article shows how to use Axios Interceptor to
refresh the JWT token when the origin token had expired.
The flow (activity diagram) is as following.
However, they can be apply to other Http Client framework, and
of course, also write the code in Javascript.
|
▋Related articles
▌Environment
▋axios 0.19.0
▋vuex 3.0.1
▋typescript 3.2.1
▌Implement
▋Vuex: store Access token and Refresh token
I use Vuex store to keep the Access-token (JWT
token) and Refresh-token.
And create an Vuex Action to send request to refreshing-token
API and then update the new Access-token.
If you are not familiar with Vuex, here are my
tutorials about it!
|
▋Model: UserToken.ts
export default class UserToken {
public
accessToken: string | null;
public expiresIn:
number | null;
public
refreshToken: string | null;
constructor(fields?: {
accessToken?:
string,
expiresIn?:
string,
refreshToken?:
string,
}) {
if (fields) {
Object.assign(this, fields);
}
}
}
▋token-store.ts
import { UserToken } from '../classes/apiModel';
import axios from '../modules/axios-module';
export default {
state: {
tokens: {} as UserToken,
},
getters: {
tokens: (state:
any) => state.tokens,
},
mutations: {
setTokens(state: any, tokens: UserToken) {
state.tokens
= tokens;
}
},
actions: {
async
refreshToken({ commit, state }: { commit: any, state: any}) {
const isRefreshOk
= true;
if
(!state.tokens || !state.tokens.refreshToken) {
return !isRefreshOk;
}
try {
const url = 'api/Auth/RefreshToken';
const res: any = await
axios.post(state.tokens.refreshToken);
const userToken =
res.data;
// Mutation
commit('setTokens',
userToken);
// Return true/false for OK/NG
return !!userToken
;
} catch (err) {
throw err;
}
},
},
};
▋Axios: Interceptor
We would like Axios to send request with Access
token on Http header automatically.
This can be done by setting request’s configuration
of interceptor like this,
▋axios-module.ts
const instance = axios.create();
instance.interceptors.request.use( (config: AxiosRequestConfig)
=> {
// Optional
headers
if
(store.state.tokenStore && store.state.tokenStore.tokens) {
const token =
store.state.tokenStore.tokens.accessToken;
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, (error) => {
return
Promise.reject(error);
});
Now we can implement that when receiving 498
response, dispatching “refreshToken” to get the NEW Access-Token, and re-send
the failed request.
▋axios-module.ts
const instance = axios.create();
let isAlreadyFetchingAccessToken = false;
instance.interceptors.response.use( (response:
AxiosResponse<any>) => {
return response;
}, async (error: any) => {
const { config,
response: { status } } = error;
const originalRequest = config;
let
isRefreshOk: boolean = false;
if (status ===
498) {
if
(!isAlreadyFetchingAccessToken) {
isAlreadyFetchingAccessToken = true;
isRefreshOk = await store.dispatch('refreshToken');
isAlreadyFetchingAccessToken = false;
}
if
(isRefreshOk) {
const
retryOriginalRequest = new
Promise((resolve) => {
resolve(instance(originalRequest));
});
return retryOriginalRequest;
// Return the resposne from original request with new
token
} else {
return
Promise.reject(error); // Return original response
}
} else { // Non 498
response
return Promise.reject(error);
}
});
export default instance;
▋Demo
1. Send a request to API: [GetAll] and received 498 response
2. Send a request to API: [RefreshToken]
3. Re-send a request to API: [GetAll] and OK
Look into the response
of refreshing token, we got a new token: “…KAWai_yRG7UXLfYwwFg”
And the new token was exactly
what we used in the last request.
▋Source Code
▌Reference
沒有留言:
張貼留言