fix: don't intercept 401 on login/refresh endpoints for token refresh

Login 401 (TOTP required) was being caught by the refresh interceptor,
masking the actual error detail with "인증이 만료되었습니다".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-03 06:58:36 +09:00
parent 17c1b7cf30
commit aebfa14984

View File

@@ -101,8 +101,9 @@ export async function api<T = unknown>(
credentials: 'include', credentials: 'include',
}); });
// 401 → refresh 1회 시도 // 401 → refresh 1회 시도 (로그인/리프레시 엔드포인트는 제외)
if (res.status === 401 && accessToken) { const isAuthEndpoint = path.startsWith('/auth/login') || path.startsWith('/auth/refresh');
if (res.status === 401 && accessToken && !isAuthEndpoint) {
try { try {
await handleTokenRefresh(); await handleTokenRefresh();
headers['Authorization'] = `Bearer ${accessToken}`; headers['Authorization'] = `Bearer ${accessToken}`;
@@ -113,11 +114,12 @@ export async function api<T = unknown>(
}); });
if (!retryRes.ok) { if (!retryRes.ok) {
const err = await retryRes.json().catch(() => ({ detail: 'Unknown error' })); const err = await retryRes.json().catch(() => ({ detail: 'Unknown error' }));
throw { status: retryRes.status, detail: err.detail || retryRes.statusText }; throw { status: retryRes.status, detail: err.detail || retryRes.statusText } as ApiError;
} }
return retryRes.json(); return retryRes.json();
} catch { } catch (e) {
throw { status: 401, detail: '인증이 만료되었습니다' }; if ((e as ApiError).detail) throw e;
throw { status: 401, detail: '인증이 만료되었습니다' } as ApiError;
} }
} }