Advertisement
Aguezz

React Axios Refresh Token

Jan 14th, 2024
1,292
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import axios, { AxiosRequestHeaders, AxiosResponse } from 'axios'
  2. import appConfig from '@/configs/app.config'
  3. import {
  4.   TOKEN_TYPE,
  5.   REQUEST_HEADER_AUTH_KEY,
  6.   REQUEST_HEADER_ACCEPT_LANGUAGE_KEY,
  7. } from '@/constants/api.constant'
  8. import {
  9.   PERSIST_STORE_NAME,
  10.   REFRESH_TOKEN_PATH,
  11. } from '@/constants/app.constant'
  12. import deepParseJson from '@/utils/deepParseJson'
  13. import store, { refreshSuccess, signOutSuccess } from '../store'
  14.  
  15. const unauthorizedCode = [401]
  16.  
  17. interface RefreshTokenResponse {
  18.   data: {
  19.     jwt: string
  20.   }
  21. }
  22.  
  23. interface FailedRequest {
  24.   resolve: (token: string) => void
  25.   // eslint-disable-next-line @typescript-eslint/no-explicit-any
  26.   reject: (reason: any) => void
  27. }
  28.  
  29. let isRefreshing = false
  30. let failedRequestsQueue: FailedRequest[] = []
  31.  
  32. const BaseService = axios.create({
  33.   timeout: 60000,
  34.   baseURL: appConfig.apiPrefix,
  35. })
  36.  
  37. const BaseRefreshService = axios.create({
  38.   timeout: 60000,
  39.   baseURL: appConfig.apiPrefix,
  40. })
  41.  
  42. BaseService.interceptors.request.use(
  43.   (config) => {
  44.     if (config?.headers?._retry) {
  45.       delete config.headers._retry
  46.     } else {
  47.       const headers = getHeaders()
  48.       config.headers = { ...config.headers, ...headers } as AxiosRequestHeaders
  49.     }
  50.  
  51.     return config
  52.   },
  53.   (error) => {
  54.     return Promise.reject(error)
  55.   }
  56. )
  57.  
  58. BaseService.interceptors.response.use(
  59.   (response: AxiosResponse) => response,
  60.   (error) => {
  61.     const { response } = error
  62.  
  63.     if (response && unauthorizedCode.includes(response.status)) {
  64.       if (!isRefreshing) {
  65.         isRefreshing = true
  66.  
  67.         const headers = getHeaders()
  68.  
  69.         BaseRefreshService.post<RefreshTokenResponse>(
  70.           REFRESH_TOKEN_PATH,
  71.           {},
  72.           { headers }
  73.         )
  74.           .then((resp) => {
  75.             const accessToken = resp.data.data.jwt
  76.             processQueue(null, accessToken)
  77.             store.dispatch(refreshSuccess(accessToken))
  78.           })
  79.           .catch((err) => {
  80.             processQueue(err)
  81.             store.dispatch(signOutSuccess())
  82.             return Promise.reject(err)
  83.           })
  84.           .finally(() => {
  85.             isRefreshing = false
  86.           })
  87.       }
  88.  
  89.       return new Promise((resolve, reject) => {
  90.         failedRequestsQueue.push({ resolve, reject })
  91.       })
  92.         .then((token) => {
  93.           error.config.headers[
  94.             REQUEST_HEADER_AUTH_KEY
  95.           ] = `${TOKEN_TYPE}${token}`
  96.           error.config.headers._retry = true
  97.  
  98.           return BaseService(error.config)
  99.         })
  100.         .catch((err) => {
  101.           return Promise.reject(err)
  102.         })
  103.     }
  104.  
  105.     return Promise.reject(error)
  106.   }
  107. )
  108.  
  109. /**
  110.  * Retrieves the headers for making API requests.
  111.  * @returns The headers as AxiosRequestHeaders.
  112.  */
  113. function getHeaders() {
  114.   const headers: Record<string, string> = {}
  115.  
  116.   const rawPersistData = localStorage.getItem(PERSIST_STORE_NAME)
  117.   const persistData = deepParseJson(rawPersistData) as {
  118.     auth?: { session: { token: string } }
  119.     locale?: { currentLang: string }
  120.   }
  121.  
  122.   const accessToken =
  123.     persistData?.auth?.session.token || store.getState().auth.session.token
  124.   const currentLang = persistData?.locale?.currentLang || appConfig.locale
  125.  
  126.   if (accessToken) {
  127.     headers[REQUEST_HEADER_AUTH_KEY] = `${TOKEN_TYPE}${accessToken}`
  128.   }
  129.  
  130.   headers[REQUEST_HEADER_ACCEPT_LANGUAGE_KEY] = currentLang
  131.  
  132.   return headers as AxiosRequestHeaders
  133. }
  134.  
  135. /**
  136.  * Processes the failed requests queue.
  137.  *
  138.  * @param error - The error object to reject the promises with.
  139.  * @param token - The token to resolve the promises with. Defaults to null.
  140.  */
  141. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  142. function processQueue(error: any, token: string | null = null): void {
  143.   failedRequestsQueue.forEach((prom) => {
  144.     if (error) {
  145.       prom.reject(error)
  146.     } else {
  147.       prom.resolve(token as string)
  148.     }
  149.   })
  150.  
  151.   failedRequestsQueue = []
  152. }
  153.  
  154. export default BaseService
  155.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement