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
|
// SAML Service Provider (SP) 메타데이터 유틸리티
export interface SPAssertionConsumerService {
binding: string
location: string
index: number
isDefault?: boolean
}
export interface SPMetadata {
entityId: string
// SPSSODescriptor 속성들
protocolSupportEnumeration: string[]
authnRequestsSigned: boolean
wantAssertionsSigned: boolean
// 서비스 엔드포인트들
assertionConsumerServices: SPAssertionConsumerService[]
// 편의를 위한 기본값들
defaultACS: SPAssertionConsumerService
callbackUrl: string
}
// SP 메타데이터 가져오기 (환경변수 기반)
export function getSPMetadata(): SPMetadata {
console.log('🔍 Loading SP metadata from environment variables...')
// 필수 환경변수 검증
const entityId = process.env.SAML_SP_ENTITY_ID
const callbackUrl = process.env.SAML_SP_CALLBACK_URL || `${process.env.NEXTAUTH_URL}/api/saml/callback`
if (!entityId) {
throw new Error('Required SAML environment variable is missing: SAML_SP_ENTITY_ID')
}
if (!callbackUrl) {
throw new Error('SAML_SP_CALLBACK_URL or NEXTAUTH_URL environment variable is required')
}
// AssertionConsumerService 구성
const assertionConsumerServices: SPAssertionConsumerService[] = [
// 기본 HTTP-POST 바인딩
{
binding: process.env.SAML_SP_ACS_BINDING_PRIMARY || 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
location: callbackUrl,
index: 0,
isDefault: true
}
]
// 보조 HTTP-Redirect 바인딩 (선택사항)
const secondaryBinding = process.env.SAML_SP_ACS_BINDING_SECONDARY
if (secondaryBinding) {
assertionConsumerServices.push({
binding: secondaryBinding,
location: callbackUrl,
index: 1,
isDefault: false
})
}
const metadata: SPMetadata = {
entityId,
protocolSupportEnumeration: [
process.env.SAML_SP_PROTOCOL_SUPPORT || 'urn:oasis:names:tc:SAML:2.0:protocol'
],
authnRequestsSigned: process.env.SAML_SP_AUTHN_REQUESTS_SIGNED === 'true',
wantAssertionsSigned: process.env.SAML_SP_WANT_ASSERTIONS_SIGNED === 'true',
assertionConsumerServices,
defaultACS: assertionConsumerServices[0],
callbackUrl
}
console.log('✅ SP metadata loaded from environment variables:', {
entityId,
callbackUrl,
authnRequestsSigned: metadata.authnRequestsSigned,
wantAssertionsSigned: metadata.wantAssertionsSigned,
acsCount: assertionConsumerServices.length
})
return metadata
}
|