From cce8d64f250626b3eb158d00b6d944c83ed25f1f Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Mon, 29 Dec 2025 11:04:31 +0330 Subject: [PATCH] Refresh token error fix --- src/App.tsx | 42 +++++++++++++++++++++++++++++++++++++++- src/config/axios.ts | 41 +++++++++++++++++++++++++++++++++++---- src/types/error.types.ts | 4 ++++ 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index c9ba256..ca49956 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -30,6 +30,7 @@ i18next.init({ declare global { interface Window { isRefreshingToken?: boolean; + isRefreshTokenExpired?: boolean; } } @@ -37,6 +38,23 @@ const queryClient = new QueryClient({ queryCache: new QueryCache({ onError: async (error: IApiErrorRepsonse) => { if (error?.response?.status === 401) { + // اگر refresh token منقضی شده باشد، دیگر تلاش نکن + if (window.isRefreshTokenExpired) { + return; + } + + // بررسی کن که آیا خطا از refresh endpoint است + const isRefreshEndpoint = error?.config?.url?.includes('/admin/auth/refresh'); + + // اگر خطا از refresh endpoint است، refresh token منقضی شده + if (isRefreshEndpoint) { + window.isRefreshTokenExpired = true; + await removeToken(); + await removeRefreshToken(); + window.location.href = Pages.auth.login; + return; + } + try { // Use a flag to track if refresh is in progress if (window.isRefreshingToken) { @@ -57,6 +75,15 @@ const queryClient = new QueryClient({ try { const refreshTokenValue = await getRefreshToken(); + + if (!refreshTokenValue) { + window.isRefreshTokenExpired = true; + await removeToken(); + await removeRefreshToken(); + window.location.href = Pages.auth.login; + return; + } + const { data } = await refreshToken({ refreshToken: refreshTokenValue || '' }); if (data?.accessToken?.token) { @@ -68,6 +95,9 @@ const queryClient = new QueryClient({ await setRefreshToken(data.refreshToken?.token); } + // Reset expired flag if refresh was successful + window.isRefreshTokenExpired = false; + // Retry the failed request queryClient.invalidateQueries(); return; @@ -79,10 +109,20 @@ const queryClient = new QueryClient({ } } catch (refreshError: unknown) { console.error('Token refresh failed:', refreshError); + + // بررسی کن که آیا خطا از refresh endpoint است + const errorWithConfig = refreshError as IApiErrorRepsonse; + const isRefreshError = errorWithConfig?.config?.url?.includes('/admin/auth/refresh'); + + // اگر refresh token خودش 401 خورده، آن را منقضی شده علامت بزن + if (isRefreshError || errorWithConfig?.response?.status === 401) { + window.isRefreshTokenExpired = true; + } + // Clear tokens and redirect to login await removeToken(); await removeRefreshToken(); - // window.location.href = '/auth/login'; + window.location.href = Pages.auth.login; } } } diff --git a/src/config/axios.ts b/src/config/axios.ts index 24f2c04..bc69c04 100644 --- a/src/config/axios.ts +++ b/src/config/axios.ts @@ -12,6 +12,7 @@ import { refreshToken } from "../pages/auth/service/AuthService"; declare global { interface Window { isRefreshingToken?: boolean; + isRefreshTokenExpired?: boolean; } } @@ -25,6 +26,23 @@ axiosInstance.interceptors.response.use( const originalRequest = error.config; if (error?.response?.status === 401 && !originalRequest._retry) { + // اگر refresh token منقضی شده باشد، دیگر تلاش نکن + if (window.isRefreshTokenExpired) { + return Promise.reject(error); + } + + // بررسی کن که آیا خطا از refresh endpoint است + const isRefreshEndpoint = originalRequest?.url?.includes('/admin/auth/refresh'); + + // اگر خطا از refresh endpoint است، refresh token منقضی شده + if (isRefreshEndpoint) { + window.isRefreshTokenExpired = true; + await removeToken(); + await removeRefreshToken(); + window.location.href = `/`; + return Promise.reject(error); + } + if (window.isRefreshingToken) { // Wait for the refresh to complete await new Promise((resolve) => { @@ -44,10 +62,11 @@ axiosInstance.interceptors.response.use( try { const refreshTokenValue = await getRefreshToken(); if (!refreshTokenValue) { + window.isRefreshTokenExpired = true; await removeRefreshToken(); await removeToken(); window.location.href = `/`; - return; + return Promise.reject(error); } const data = await refreshToken({ refreshToken: refreshTokenValue, @@ -59,19 +78,33 @@ axiosInstance.interceptors.response.use( await setRefreshToken(data.data.refreshToken.token); } + // Reset expired flag if refresh was successful + window.isRefreshTokenExpired = false; + originalRequest.headers.Authorization = `Bearer ${data.data.accessToken.token}`; return axiosInstance(originalRequest); } else { + window.isRefreshTokenExpired = true; await removeRefreshToken(); await removeToken(); - // window.location.href = `/`; - return; + window.location.href = `/`; + return Promise.reject(error); } } catch (refreshError) { console.error("Token refresh failed:", refreshError); + + // بررسی کن که آیا خطا از refresh endpoint است + const isRefreshError = (refreshError as { response?: { config?: { url?: string }; status?: number } })?.response?.config?.url?.includes('/admin/auth/refresh'); + const is401Error = (refreshError as { response?: { status?: number } })?.response?.status === 401; + + // اگر refresh token خودش 401 خورده، آن را منقضی شده علامت بزن + if (isRefreshError || is401Error) { + window.isRefreshTokenExpired = true; + } + await removeToken(); await removeRefreshToken(); - // window.location.href = `/`; + window.location.href = `/`; return Promise.reject(refreshError); } finally { window.isRefreshingToken = false; diff --git a/src/types/error.types.ts b/src/types/error.types.ts index eda8cec..5b71506 100644 --- a/src/types/error.types.ts +++ b/src/types/error.types.ts @@ -11,4 +11,8 @@ export interface IErrorResponse extends IBaseResponse { export interface IApiErrorRepsonse extends DefaultError { response?: IErrorResponse; + config?: { + url?: string; + [key: string]: unknown; + }; }