1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
'use client'
/**
*
* SAML 2.0 기반 SSO 로그인 요청을 시작하는 버튼 컴포넌트
*
*
*/
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { toast } from '@/hooks/use-toast'
import { Loader2, Shield } from 'lucide-react'
import React from 'react'
interface SAMLLoginButtonProps {
className?: string
children?: React.ReactNode
variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link' | 'samsung'
size?: 'default' | 'sm' | 'lg' | 'icon'
}
const isSsoStage = process.env.SAML_IDP_SSO_URL?.includes('stage');
export function SAMLLoginButton({
className,
children = "Knox SSO로 로그인하기",
variant = "outline",
size = "default"
}: SAMLLoginButtonProps) {
const [isLoading, setIsLoading] = useState(false)
const handleSAMLLogin = async () => {
try {
setIsLoading(true)
// 현재 페이지 경로를 RelayState로 설정 (로그인 후 이 페이지로 돌아옴)
const currentPath = window.location.pathname + window.location.search
const relayState = encodeURIComponent(currentPath)
console.log('Setting RelayState to:', currentPath)
// API 엔드포인트를 통해 SAML AuthnRequest URL 생성
const response = await fetch(`/api/auth/saml/authn-request?relayState=${relayState}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})
if (!response.ok) {
throw new Error('Failed to create SAML AuthnRequest')
}
const data = await response.json()
if (!data.success || !data.loginUrl) {
throw new Error(data.error || 'Failed to get SAML login URL')
}
console.log('SAML Login URL:', data.loginUrl)
// data URL인지 확인 (브라우저 보안 정책으로 차단될 수 있음)
if (data.loginUrl.startsWith('data:')) {
console.warn('⚠️ Data URL detected - this may be blocked by browser security policies')
toast({
title: '테스트 모드 감지',
description: 'Mock SAML IdP 모드가 활성화되어 있습니다. 프로덕션에서는 SAML_MOCKING_IDP=false로 설정하세요.',
variant: 'default',
})
// data URL 대신 Mock IdP 페이지로 직접 리다이렉트
const baseUrl = window.location.origin
const mockIdpUrl = `${baseUrl}/api/auth/saml/mock-idp`
console.log('🎭 Redirecting to Mock IdP instead:', mockIdpUrl)
window.location.href = mockIdpUrl
return
}
// 일반적인 URL로 리다이렉트
window.location.href = data.loginUrl
} catch (error) {
console.error('SAML Login Error:', error)
toast({
title: '로그인 오류',
description: 'SAML 로그인을 시작할 수 없습니다.',
variant: 'destructive',
})
setIsLoading(false)
}
}
return (
<Button
type="button"
variant={variant}
size={size}
className={className}
onClick={handleSAMLLogin}
disabled={isLoading}
>
{isLoading ? (
<Loader2 className="h-4 w-4 animate-spin mr-2" />
) : (
<Shield className="h-4 w-4 mr-2" />
)}
{children}
</Button>
)
}
// 간단한 Knox SSO 버튼 (props 없이)
export function KnoxSSOButton() {
return (
<SAMLLoginButton
className="w-full"
variant="outline"
>
{isSsoStage ? 'Knox SSO (STAGE)' : 'Knox SSO'}
</SAMLLoginButton>
)
}
|