This commit is contained in:
alisaza 2026-09-05 15:15:35 +03:30
parent f9a9afc697
commit 0bb22059d0
2 changed files with 24 additions and 8 deletions

View File

@ -32,7 +32,6 @@ const AUTH_CONFIG = {
const TOKEN_MESSAGES = { const TOKEN_MESSAGES = {
accessInvalid: new Set(['ACCESS_TOKEN_INVALID', 'ACCESS_TOKEN_EXPIRED', 'UNAUTHORIZED']), accessInvalid: new Set(['ACCESS_TOKEN_INVALID', 'ACCESS_TOKEN_EXPIRED', 'UNAUTHORIZED']),
refreshInvalid: new Set(['REFRESH_TOKEN_INVALID', 'REFRESH_TOKEN_EXPIRED', 'UNAUTHORIZED']),
} }
type RequestConfigWithRetry = InternalAxiosRequestConfig & { _retry?: boolean } type RequestConfigWithRetry = InternalAxiosRequestConfig & { _retry?: boolean }
@ -419,23 +418,33 @@ const getErrorMessage = (error: AxiosError): string => {
return data?.code ?? data?.message ?? '' return data?.code ?? data?.message ?? ''
} }
const isSessionAuthFailure = (error: AxiosError): boolean => {
const message = getErrorMessage(error)
// Bare 401 (no code/message) is treated as a dead session.
if (!message) return true
return TOKEN_MESSAGES.accessInvalid.has(message) || message === 'Unauthorized'
}
const shouldTryRefreshFor401 = (error: AxiosError, config: RequestConfigWithRetry): boolean => { const shouldTryRefreshFor401 = (error: AxiosError, config: RequestConfigWithRetry): boolean => {
if (isRefreshTokenRequest(config)) return false if (isRefreshTokenRequest(config)) return false
if (config._retry) return false if (config._retry) return false
if (!canAttemptTokenRefresh()) return false if (!canAttemptTokenRefresh()) return false
if (!isSessionAuthFailure(error)) return false
const message = getErrorMessage(error) return true
if (TOKEN_MESSAGES.refreshInvalid.has(message) && isRefreshTokenRequest(config)) return false
if (!message) return true
return TOKEN_MESSAGES.accessInvalid.has(message) || message === 'Unauthorized'
} }
async function handle401Response(error: AxiosError): Promise<AxiosResponse> { async function handle401Response(error: AxiosError): Promise<AxiosResponse> {
const config = error.config as RequestConfigWithRetry | undefined const config = error.config as RequestConfigWithRetry | undefined
if (!config) { if (!config) {
// بدون config نمی‌توان retry کرد؛ 401 نشست را expire کن، بقیه (مثلاً OTP) را رد کن.
if (isSessionAuthFailure(error)) {
return handleSessionExpired()
}
return Promise.reject(error) return Promise.reject(error)
} }
@ -444,6 +453,13 @@ async function handle401Response(error: AxiosError): Promise<AxiosResponse> {
} }
if (!shouldTryRefreshFor401(error, config)) { if (!shouldTryRefreshFor401(error, config)) {
// قبلاً فقط reject می‌شد → داشبورد ارور می‌دید ولی لاگ‌اوت نمی‌شد
// (مثلاً 401 بدون توکن، یا retry بعد از refresh که باز هم UNAUTHORIZED بود).
// OTP_INVALID و سایر 401های کسب‌وکاری نباید نشست را پاک کنند.
if (isSessionAuthFailure(error)) {
return handleSessionExpired()
}
return Promise.reject(error) return Promise.reject(error)
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "ghabilee-admin", "name": "ghabilee-admin",
"version": "0.1.0", "version": "0.1.1",
"license": "MIT", "license": "MIT",
"private": true, "private": true,
"scripts": { "scripts": {