diff --git a/devfront/src/features/auth/AuthCallbackPage.tsx b/devfront/src/features/auth/AuthCallbackPage.tsx index 339dade5..929bc7b4 100644 --- a/devfront/src/features/auth/AuthCallbackPage.tsx +++ b/devfront/src/features/auth/AuthCallbackPage.tsx @@ -10,7 +10,9 @@ export default function AuthCallbackPage() { useEffect(() => { // 팝업으로 열린 경우 signinPopupCallback 처리 if (window.opener) { - userManager.signinPopupCallback(); + userManager.signinPopupCallback().catch((error) => { + console.error("Popup callback failed:", error); + }); return; } diff --git a/devfront/src/features/auth/AuthGuard.tsx b/devfront/src/features/auth/AuthGuard.tsx index 50abe838..26069583 100644 --- a/devfront/src/features/auth/AuthGuard.tsx +++ b/devfront/src/features/auth/AuthGuard.tsx @@ -4,7 +4,7 @@ import { Navigate, Outlet } from "react-router-dom"; export default function AuthGuard() { const auth = useAuth(); - if (auth.isLoading) { + if (auth.isLoading || auth.activeNavigator) { return
Loading...
; } diff --git a/devfront/src/features/auth/LoginPage.tsx b/devfront/src/features/auth/LoginPage.tsx index 4d592b68..3c87a65e 100644 --- a/devfront/src/features/auth/LoginPage.tsx +++ b/devfront/src/features/auth/LoginPage.tsx @@ -1,4 +1,5 @@ import { ExternalLink, LogIn, ShieldHalf } from "lucide-react"; +import { useEffect } from "react"; import { useAuth } from "react-oidc-context"; import { useNavigate } from "react-router-dom"; import { Button } from "../../components/ui/button"; @@ -14,10 +15,15 @@ function LoginPage() { const auth = useAuth(); const navigate = useNavigate(); + useEffect(() => { + if (auth.isAuthenticated) { + navigate("/clients", { replace: true }); + } + }, [auth.isAuthenticated, navigate]); + const handleSSOLogin = async () => { try { await auth.signinPopup(); - navigate("/clients", { replace: true }); } catch (error) { console.error("Popup login failed", error); } diff --git a/devfront/src/lib/apiClient.ts b/devfront/src/lib/apiClient.ts index 65a6995c..49b83cee 100644 --- a/devfront/src/lib/apiClient.ts +++ b/devfront/src/lib/apiClient.ts @@ -26,12 +26,16 @@ apiClient.interceptors.request.use(async (config) => { apiClient.interceptors.response.use( (response) => response, - (error) => { + async (error) => { if (error.response?.status === 401) { // 401 발생 시 로그인 페이지로 리다이렉트 - const isAuthPath = window.location.pathname.startsWith("/callback"); + const isAuthPath = window.location.pathname.startsWith("/auth/callback"); const isLoginPath = window.location.pathname === "/login"; - if (!isAuthPath && !isLoginPath) { + const user = await userManager.getUser(); + // 인증 토큰이 없는 경우에만 로그인으로 보낸다. + // 토큰이 있는데 401이면 권한/백엔드 정책 이슈로 간주하고 화면에서 에러를 노출한다. + const hasAccessToken = Boolean(user?.access_token); + if (!hasAccessToken && !isAuthPath && !isLoginPath) { window.location.href = "/login"; } } diff --git a/devfront/src/main.tsx b/devfront/src/main.tsx index b718dd50..cb85c263 100644 --- a/devfront/src/main.tsx +++ b/devfront/src/main.tsx @@ -7,7 +7,7 @@ import { queryClient } from "./app/queryClient"; import { router } from "./app/routes"; import { Toaster } from "./components/ui/toaster"; import "./index.css"; -import { oidcConfig } from "./lib/auth"; +import { oidcConfig, userManager } from "./lib/auth"; const rootElement = document.getElementById("root"); @@ -17,7 +17,7 @@ if (!rootElement) { createRoot(rootElement).render( - +