fix(auth): prevent logout on transient API failures
This commit is contained in:
@@ -12,6 +12,62 @@ const axiosClient = axios.create({
|
||||
},
|
||||
});
|
||||
|
||||
export const axiosAuth = axios.create({
|
||||
baseURL: BASE_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
let refreshPromise: Promise<string> | null = null;
|
||||
|
||||
const isInvalidSessionResponse = (error: unknown) => {
|
||||
if (!axios.isAxiosError(error)) return false;
|
||||
|
||||
return error.response?.status === 401 || error.response?.status === 403;
|
||||
};
|
||||
|
||||
const refreshAccessToken = () => {
|
||||
if (!refreshPromise) {
|
||||
refreshPromise = axiosAuth
|
||||
.post(
|
||||
'api/auth/refresh',
|
||||
{},
|
||||
{
|
||||
withCredentials: true,
|
||||
},
|
||||
)
|
||||
.then((response) => {
|
||||
const accessToken = response.data?.access_token;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('Refresh failed: no access token');
|
||||
}
|
||||
|
||||
useAuthStore.getState().setAccessToken(accessToken);
|
||||
|
||||
return accessToken;
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
if (isInvalidSessionResponse(error)) {
|
||||
useAuthStore.getState().logout();
|
||||
useAppStore.getState().clear();
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
})
|
||||
.finally(() => {
|
||||
refreshPromise = null;
|
||||
});
|
||||
}
|
||||
|
||||
return refreshPromise;
|
||||
};
|
||||
|
||||
axiosClient.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = useAuthStore.getState().accessToken;
|
||||
@@ -49,31 +105,11 @@ axiosClient.interceptors.response.use(
|
||||
originalRequest._retry = true;
|
||||
|
||||
try {
|
||||
const response = await axiosAuth.post(
|
||||
'api/auth/refresh',
|
||||
{},
|
||||
{
|
||||
withCredentials: true,
|
||||
},
|
||||
);
|
||||
const accessToken = response.data?.access_token;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('Refresh failed: no access token');
|
||||
}
|
||||
|
||||
useAuthStore.getState().setAccessToken(accessToken);
|
||||
const accessToken = await refreshAccessToken();
|
||||
originalRequest.headers.Authorization = `Bearer ${accessToken}`;
|
||||
|
||||
return axiosClient(originalRequest);
|
||||
} catch (refreshError) {
|
||||
useAuthStore.getState().logout();
|
||||
useAppStore.getState().clear();
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
|
||||
return Promise.reject(refreshError);
|
||||
}
|
||||
}
|
||||
@@ -82,11 +118,4 @@ axiosClient.interceptors.response.use(
|
||||
},
|
||||
);
|
||||
|
||||
export const axiosAuth = axios.create({
|
||||
baseURL: BASE_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
export default axiosClient;
|
||||
|
||||
Reference in New Issue
Block a user