// 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 }