From 2864a946f21b03d3c444a526c429fa784a80be56 Mon Sep 17 00:00:00 2001 From: chan Date: Wed, 11 Feb 2026 14:32:37 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=96=B4=EB=93=9C=EB=AF=BC=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EB=B0=A9=EC=8B=9D=EC=9D=84=20SSO=20?= =?UTF-8?q?=ED=8C=9D=EC=97=85=20=EC=97=B0=EB=8F=99=20=EB=B0=A9=EC=8B=9D?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20#243?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adminfront/src/features/auth/LoginPage.tsx | 149 +++++++++++---------- adminfront/src/lib/apiClient.ts | 5 +- 2 files changed, 81 insertions(+), 73 deletions(-) diff --git a/adminfront/src/features/auth/LoginPage.tsx b/adminfront/src/features/auth/LoginPage.tsx index 4367c9bf..233da28c 100644 --- a/adminfront/src/features/auth/LoginPage.tsx +++ b/adminfront/src/features/auth/LoginPage.tsx @@ -1,7 +1,5 @@ -import { useMutation } from "@tanstack/react-query"; -import type { AxiosError } from "axios"; -import { KeyRound, Lock, Mail, ShieldHalf } from "lucide-react"; -import { useState } from "react"; +import { ShieldHalf, LogIn, ExternalLink } from "lucide-react"; +import { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import { Button } from "../../components/ui/button"; import { @@ -11,32 +9,55 @@ import { CardHeader, CardTitle, } from "../../components/ui/card"; -import { Input } from "../../components/ui/input"; -import { Label } from "../../components/ui/label"; -import { login } from "../../lib/adminApi"; -import { t } from "../../lib/i18n"; function LoginPage() { const navigate = useNavigate(); - const [loginId, setLoginId] = useState(""); - const [password, setPassword] = useState(""); + const [isLoggingIn, setIsLoggingIn] = useState(false); - const loginMutation = useMutation({ - mutationFn: () => login({ loginId, password }), - onSuccess: (data) => { - window.localStorage.setItem("admin_session", data.sessionToken); - navigate("/"); - }, - }); + useEffect(() => { + // Listen for login success message from the popup + const handleMessage = (event: MessageEvent) => { + // Security check: In production, verify event.origin + if (event.data?.type === "LOGIN_SUCCESS" && event.data?.token) { + window.localStorage.setItem("admin_session", event.data.token); + setIsLoggingIn(false); + navigate("/"); + } + }; - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - loginMutation.mutate(); + window.addEventListener("message", handleMessage); + return () => window.removeEventListener("message", handleMessage); + }, [navigate]); + + const handleSSOLogin = () => { + const userfrontUrl = import.meta.env.VITE_USERFRONT_URL || "https://sso.hmac.kr"; + const loginUrl = `${userfrontUrl}/login?source=adminfront`; + + const width = 500; + const height = 700; + const left = window.screen.width / 2 - width / 2; + const top = window.screen.height / 2 - height / 2; + + const popup = window.open( + loginUrl, + "BaronSSOLogin", + `width=${width},height=${height},top=${top},left=${left},status=no,menubar=no,toolbar=no` + ); + + if (popup) { + setIsLoggingIn(true); + // Optional: Polling to detect if popup was closed without login + const timer = setInterval(() => { + if (popup.closed) { + clearInterval(timer); + setIsLoggingIn(false); + } + }, 1000); + } else { + alert("팝업 차단이 설정되어 있습니다. 팝업 허용 후 다시 시도해 주세요."); + } }; - const errorMsg = (loginMutation.error as AxiosError<{ error?: string }>)?.response - ?.data?.error; - return (
@@ -55,62 +76,46 @@ function LoginPage() { - - Sign in + + 관리자 로그인 - 관리자 계정 정보를 입력하여 로그인하세요. + Baron 통합 인증(SSO)을 통해 관리자 페이지에 접속합니다. - -
-
- -
- - setLoginId(e.target.value)} - required - /> -
-
-
- -
- - setPassword(e.target.value)} - required - /> -
-
- - {errorMsg && ( -
- {errorMsg} -
+ + - + + +

+ 관리자 전역 세션은 보안을 위해 15분간 유지됩니다.
+ 민감한 작업 시 재인증을 요구할 수 있습니다. +

+
+
+
+
+
+

인증 정보가 없거나 로그인이 되지 않는 경우
시스템 관리자에게 문의하세요. @@ -120,4 +125,4 @@ function LoginPage() { ); } -export default LoginPage; +export default LoginPage; \ No newline at end of file diff --git a/adminfront/src/lib/apiClient.ts b/adminfront/src/lib/apiClient.ts index c93d5766..a3c0f3eb 100644 --- a/adminfront/src/lib/apiClient.ts +++ b/adminfront/src/lib/apiClient.ts @@ -29,7 +29,10 @@ apiClient.interceptors.request.use((config) => { apiClient.interceptors.response.use( (response) => response, (error) => { - // TODO: 401/403 응답 시 로그인/재인증 플로우로 리다이렉션한다. + if (error.response?.status === 401) { + window.localStorage.removeItem("admin_session"); + window.location.href = "/login"; + } return Promise.reject(error); }, );