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(
-
+