Created
March 3, 2021 08:21
-
-
Save isumix/28daba0671348c169b88d5176eab5e99 to your computer and use it in GitHub Desktop.
auto-login/refresh token with axios interceptors and queue pending requests
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import axios, { AxiosError } from 'axios'; | |
import { getLoginData, LoginFormInputs, openLoadingScreen } from 'my-gui-lib'; | |
const STORAGE_NAME = 'auth'; | |
const HEADER_NAME = 'Authorization'; | |
let _refreshToken = ''; | |
let _authorizing: Promise<void> | null = null; | |
{ // init | |
const str = localStorage.getItem(STORAGE_NAME); | |
if(str) { | |
const { accessToken, refreshToken } = JSON.parse(str); | |
axios.defaults.headers.common[HEADER_NAME] = accessToken; | |
_refreshToken = refreshToken; | |
} | |
} | |
const login = (accessToken: string, refreshToken: string) => { | |
axios.defaults.headers.common[HEADER_NAME] = accessToken; | |
_refreshToken = refreshToken; | |
localStorage.setItem(STORAGE_NAME, JSON.stringify({ accessToken, refreshToken })); | |
}; | |
const logout = () => { | |
delete axios.defaults.headers.common[HEADER_NAME]; | |
_refreshToken = ''; | |
localStorage.removeItem(STORAGE_NAME); | |
}; | |
const authorize = async () => { | |
let loginData: LoginFormInputs | undefined, errorMessage; | |
for (;;) { | |
loginData = await getLoginData({ ...loginData, errorMessage }); | |
const closeLoadingScreen = openLoadingScreen(); | |
try { | |
const response = await axios.post('/auth/signin', loginData); | |
const { accessToken, refreshToken } = response.data; | |
login(accessToken, refreshToken); | |
break; | |
} | |
catch (e) { | |
errorMessage = 'Invalid username or password! Please try again.'; | |
} | |
finally { | |
closeLoadingScreen(); | |
} | |
} | |
}; | |
const refresh = async () => { | |
const closeLoadingScreen = openLoadingScreen(); | |
try { | |
const response = await axios.post('/auth/refresh', { refreshToke: _refreshToken }); | |
const { accessToken, refreshToken } = response.data; | |
login(accessToken, refreshToken); | |
} | |
catch (e) { | |
logout(); | |
} | |
closeLoadingScreen(); | |
}; | |
axios.interceptors.response.use(undefined, async (error: AxiosError) => { | |
if(error.response?.status !== 401) { | |
return Promise.reject(error); | |
} | |
// create pending authorization | |
_authorizing ??= (_refreshToken ? refresh : authorize)() | |
.finally(() => _authorizing = null) | |
.catch(error => Promise.reject(error)); | |
const originalRequestConfig = error.config; | |
delete originalRequestConfig.headers[HEADER_NAME]; // use from defaults | |
// delay original requests until authorization has been completed | |
return _authorizing.then(() => axios.request(originalRequestConfig)); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment