61 lines
2.1 KiB
TypeScript
61 lines
2.1 KiB
TypeScript
import { AxiosInstance, AxiosRequestConfig } from 'axios'
|
|
import { refreshAccessToken } from '../services/authApi'
|
|
import { getUserRole } from './role'
|
|
import { clearAuthTokens, getAccessToken, getRefreshToken, storeAuthTokens } from './authToken'
|
|
|
|
interface RetriableRequestConfig extends AxiosRequestConfig {
|
|
_retried?: boolean
|
|
}
|
|
|
|
// shared across every axios instance — concurrent 401s while a refresh is
|
|
// already in flight must wait on the same promise, not trigger separate refreshes
|
|
let refreshPromise: Promise<string | null> | null = null
|
|
|
|
function refreshAndStoreAccessToken(): Promise<string | null> {
|
|
const refreshToken = getRefreshToken()
|
|
if (!refreshToken) return Promise.resolve(null)
|
|
|
|
if (!refreshPromise) {
|
|
refreshPromise = refreshAccessToken(refreshToken)
|
|
.then((tokens) => {
|
|
storeAuthTokens(tokens.access_token, tokens.refresh_token)
|
|
return tokens.access_token
|
|
})
|
|
.catch(() => {
|
|
clearAuthTokens()
|
|
return null
|
|
})
|
|
.finally(() => {
|
|
refreshPromise = null
|
|
})
|
|
}
|
|
return refreshPromise
|
|
}
|
|
|
|
export function attachAuthInterceptors(instance: AxiosInstance) {
|
|
instance.interceptors.request.use((config) => {
|
|
const token = getAccessToken()
|
|
if (token) {
|
|
config.headers.Authorization = `Bearer ${token}`
|
|
}
|
|
return config
|
|
})
|
|
|
|
instance.interceptors.response.use(
|
|
(response) => response,
|
|
async (error) => {
|
|
const originalRequest = error.config as RetriableRequestConfig | undefined
|
|
if (error.response?.status === 401 && originalRequest && !originalRequest._retried) {
|
|
originalRequest._retried = true
|
|
const newAccessToken = await refreshAndStoreAccessToken()
|
|
if (newAccessToken) {
|
|
originalRequest.headers = { ...originalRequest.headers, Authorization: `Bearer ${newAccessToken}` }
|
|
return instance(originalRequest)
|
|
}
|
|
// refresh token is gone/invalid — nothing left to do but send the user back to login
|
|
window.location.href = getUserRole() === 'cafe_owner' ? '/cafe/login' : '/login'
|
|
}
|
|
return Promise.reject(error)
|
|
},
|
|
)
|
|
}
|