diff --git a/src/app/api/session/refresh/route.ts b/src/app/api/session/refresh/route.ts
index 02d0cca..751e0ec 100644
--- a/src/app/api/session/refresh/route.ts
+++ b/src/app/api/session/refresh/route.ts
@@ -5,6 +5,8 @@ import type { RefreshTokenApiResponse } from '@/shared/api/data-contracts';
export const dynamic = 'force-dynamic';
+const REFRESH_TIMEOUT_MS = 10_000;
+
export async function POST() {
const session = await getSession();
@@ -12,11 +14,15 @@ export async function POST() {
return NextResponse.json({ error: 'NO_REFRESH_TOKEN' }, { status: 401 });
}
+ const controller = new AbortController();
+ const timeoutId = setTimeout(() => controller.abort(), REFRESH_TIMEOUT_MS);
+
try {
const response = await fetch(`${getApiBaseUrl()}/api/auth/refresh`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken: session.refreshToken }),
+ signal: controller.signal,
});
if (!response.ok) {
@@ -37,8 +43,14 @@ export async function POST() {
return NextResponse.json({ accessToken: payload.data.accessToken });
} catch (error) {
- console.error('[session/refresh] unexpected error', error);
session.destroy();
+ if (error instanceof DOMException && error.name === 'AbortError') {
+ console.error('[session/refresh] timed out after', REFRESH_TIMEOUT_MS, 'ms');
+ return NextResponse.json({ error: 'REFRESH_TIMEOUT' }, { status: 504 });
+ }
+ console.error('[session/refresh] unexpected error', error);
return NextResponse.json({ error: 'REFRESH_ERROR' }, { status: 500 });
+ } finally {
+ clearTimeout(timeoutId);
}
}
diff --git a/src/app/auth/callback/route.ts b/src/app/auth/callback/route.ts
index 9e649fd..4173481 100644
--- a/src/app/auth/callback/route.ts
+++ b/src/app/auth/callback/route.ts
@@ -27,7 +27,7 @@ export async function GET(request: Request) {
const idToken = await getKakaoToken(code, redirectUri);
const { accessToken, refreshToken, isPortalLinked } = await authService.login(idToken, nonce, 'KAKAO');
- if (isPortalLinked === undefined || !refreshToken) {
+ if (typeof isPortalLinked !== 'boolean' || !refreshToken) {
throw new AuthError('User is missing or malformed.');
}
diff --git a/src/app/auth/success/page.tsx b/src/app/auth/success/page.tsx
index 5682fd2..ef57a91 100644
--- a/src/app/auth/success/page.tsx
+++ b/src/app/auth/success/page.tsx
@@ -3,6 +3,7 @@
import { useEffect, Suspense } from 'react';
import { useSearchParams } from 'next/navigation';
import { type RoutePath, useInternalRouter } from '@/hooks/useInternalRouter';
+import styles from './success.module.scss';
const SuccessContent = () => {
const router = useInternalRouter();
@@ -28,12 +29,12 @@ const SuccessContent = () => {
hydrate();
}, [router, searchParams]);
- return
;
+ return
;
};
const SuccessPage = () => {
return (
-
}>
+