Created
September 15, 2019 11:00
-
-
Save tonybolanyo/1784ccef6c8b174c0c79763790403033 to your computer and use it in GitHub Desktop.
JWT authentication handler using Axios interceptors. It refreshes access token on the fly when backend API throws out a 401 error. Multiple requests at the same time supported.
This file contains hidden or 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 from 'axios'; | |
import JWTDecode from 'jwt-decode'; | |
import { AuthApi } from './auth.api'; | |
import { config } from '../config'; | |
const { API_ENDPOINT } = config[process.env.NODE_ENV]; | |
axios.defaults.baseURL = API_ENDPOINT; | |
axios.defaults.timeout = 7000; | |
axios.interceptors.request.use( | |
reqConfig => { | |
reqConfig.headers.authorization = localStorage.getItem('access_token'); | |
if (reqConfig.url.includes('/auth/logout')) | |
reqConfig.headers['X-REFRESH-TOKEN'] = localStorage.getItem( | |
'refresh_token', | |
); | |
return reqConfig; | |
}, | |
err => Promise.reject(err), | |
); | |
let isFetchingToken = false; | |
let tokenSubscribers = []; | |
function subscribeTokenRefresh(cb) { | |
tokenSubscribers.push(cb); | |
} | |
function onTokenRefreshed(errRefreshing, token) { | |
tokenSubscribers.map(cb => cb(errRefreshing, token)); | |
} | |
function forceLogout() { | |
isFetchingToken = false; | |
localStorage.clear(); | |
window.location = '/auth/login'; | |
} | |
axios.interceptors.response.use(undefined, err => { | |
if (err.response.config.url.includes('/auth/login')) | |
return Promise.reject(err); | |
if (err.response.status === 403) return forceLogout(); | |
if (err.response.status !== 401) return Promise.reject(err); | |
if (!isFetchingToken) { | |
isFetchingToken = true; | |
const refreshToken = localStorage.getItem('refresh_token'); | |
if (!refreshToken) return forceLogout(); | |
try { | |
const isRefreshTokenExpired = | |
JWTDecode(refreshToken).exp < Date.now() / 1000; | |
if (isRefreshTokenExpired) return forceLogout(); | |
} catch (e) { | |
return forceLogout(); | |
} | |
AuthApi.refreshAccessToken() | |
.then(newAccessToken => { | |
isFetchingToken = false; | |
onTokenRefreshed(null, newAccessToken); | |
tokenSubscribers = []; | |
localStorage.setItem('access_token', newAccessToken); | |
}) | |
.catch(() => { | |
onTokenRefreshed(new Error('Unable to refresh access token'), null); | |
tokenSubscribers = []; | |
forceLogout(); | |
}); | |
} | |
const initTokenSubscriber = new Promise((resolve, reject) => { | |
subscribeTokenRefresh((errRefreshing, newToken) => { | |
if (errRefreshing) return reject(errRefreshing); | |
err.config.headers.authorization = newToken; | |
return resolve(axios(err.config)); | |
}); | |
}); | |
return initTokenSubscriber; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment