forked from baron/baron-sso
133 lines
4.9 KiB
TypeScript
133 lines
4.9 KiB
TypeScript
import { ExternalLink, LogIn, ShieldHalf } from "lucide-react";
|
|
import { useEffect, useState } from "react";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { Button } from "../../components/ui/button";
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "../../components/ui/card";
|
|
|
|
function LoginPage() {
|
|
const navigate = useNavigate();
|
|
const [isLoggingIn, setIsLoggingIn] = useState(false);
|
|
|
|
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("/");
|
|
}
|
|
};
|
|
|
|
window.addEventListener("message", handleMessage);
|
|
return () => window.removeEventListener("message", handleMessage);
|
|
}, [navigate]);
|
|
|
|
const handleSSOLogin = () => {
|
|
const userfrontUrl = import.meta.env.USERFRONT_URL || "https://sso.hmac.kr";
|
|
const callbackUrl = `${window.location.origin}/auth/callback`;
|
|
|
|
// 항상 redirect_uri를 포함하여 로그인이 성공하면 콜백 페이지로 오도록 함
|
|
const loginUrl = `${userfrontUrl}/signin?source=adminfront&redirect_uri=${encodeURIComponent(callbackUrl)}`;
|
|
|
|
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);
|
|
const timer = setInterval(() => {
|
|
if (popup.closed) {
|
|
clearInterval(timer);
|
|
setIsLoggingIn(false);
|
|
}
|
|
}, 1000);
|
|
} else {
|
|
alert("팝업 차단이 설정되어 있습니다. 팝업 허용 후 다시 시도해 주세요.");
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="flex min-h-screen items-center justify-center bg-background px-4 py-12 sm:px-6 lg:px-8 bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-primary/10 via-background to-background">
|
|
<div className="w-full max-w-md space-y-8">
|
|
<div className="flex flex-col items-center justify-center space-y-4 text-center">
|
|
<div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-primary/15 text-primary shadow-[0_20px_50px_rgba(54,211,153,0.3)]">
|
|
<ShieldHalf size={32} />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<h1 className="text-3xl font-bold tracking-tight">Baron SSO</h1>
|
|
<p className="text-sm text-muted-foreground uppercase tracking-[0.2em]">
|
|
Admin Control Plane
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Card className="border-primary/20 bg-card/50 backdrop-blur-xl shadow-2xl">
|
|
<CardHeader className="space-y-1">
|
|
<CardTitle className="text-2xl flex items-center gap-2">
|
|
<LogIn size={20} className="text-primary" />
|
|
관리자 로그인
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Baron 통합 인증(SSO)을 통해 관리자 페이지에 접속합니다.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="pt-4 pb-8 space-y-3">
|
|
<Button
|
|
onClick={handleSSOLogin}
|
|
className="w-full h-14 text-lg font-semibold flex gap-3 shadow-lg"
|
|
disabled={isLoggingIn}
|
|
>
|
|
{isLoggingIn ? (
|
|
<>
|
|
<div className="h-5 w-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
|
로그인 진행 중...
|
|
</>
|
|
) : (
|
|
<>
|
|
<ShieldHalf size={22} />
|
|
SSO 계정으로 로그인
|
|
<ExternalLink size={16} className="opacity-50" />
|
|
</>
|
|
)}
|
|
</Button>
|
|
|
|
<p className="mt-6 text-xs text-center text-muted-foreground leading-relaxed">
|
|
관리자 전역 세션은 보안을 위해 15분간 유지됩니다.
|
|
<br />
|
|
민감한 작업 시 재인증을 요구할 수 있습니다.
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<div className="flex justify-center gap-4">
|
|
<div className="h-1 w-1 rounded-full bg-primary/30" />
|
|
<div className="h-1 w-1 rounded-full bg-primary/30" />
|
|
<div className="h-1 w-1 rounded-full bg-primary/30" />
|
|
</div>
|
|
|
|
<p className="px-8 text-center text-sm text-muted-foreground">
|
|
인증 정보가 없거나 로그인이 되지 않는 경우
|
|
<br />
|
|
시스템 관리자에게 문의하세요.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default LoginPage;
|