From abd9f950bbd95b9ad713a26d3fd8a7e0282b7c51 Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Fri, 20 Jun 2025 11:47:15 +0000 Subject: (김준회) SAML 2.0 SSO (Knox Portal) 추가 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/saml/callback/route.ts | 145 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 app/api/saml/callback/route.ts (limited to 'app/api/saml/callback/route.ts') diff --git a/app/api/saml/callback/route.ts b/app/api/saml/callback/route.ts new file mode 100644 index 00000000..d13edc4a --- /dev/null +++ b/app/api/saml/callback/route.ts @@ -0,0 +1,145 @@ +import { NextRequest, NextResponse } from 'next/server' +import { validateSAMLResponse, mapSAMLProfileToUser } from '../../auth/[...nextauth]/saml/utils' + +// IdP가 인증처리 후 POST 요청을 콜백 URL로 날려준다. +// 이 라우트가 그 요청을 받아준다. +// GET 요청시 SP 메타데이터를 반환해주는데, 이건 필요 없으면 지우면 된다. + +export async function POST(request: NextRequest) { + try { + console.log('🔐 SAML Callback received at /api/saml/callback') + + // FormData에서 SAML Response 추출 + const formData = await request.formData() + const samlResponse = formData.get('SAMLResponse') as string + const relayState = formData.get('RelayState') as string + + console.log('📨 SAML Response received:', { + hasResponse: !!samlResponse, + relayState: relayState || 'none', + responseLength: samlResponse?.length || 0 + }) + + // 🔍 SAML Response 디코딩 및 분석 + if (samlResponse) { + try { + console.log('🔍 SAML Response 분석:') + console.log('1️⃣ 원본 SAMLResponse (일부):', samlResponse.substring(0, 100) + '...') + + try { + // Base64 디코딩 시도 + const base64Decoded = Buffer.from(samlResponse, 'base64').toString('utf-8') + console.log('2️⃣ Base64 디코딩된 XML:') + console.log('───────────────────────────────────') + console.log(base64Decoded) + console.log('───────────────────────────────────') + + // XML 구조 분석 + const xmlLines = base64Decoded.split('\n').filter(line => line.trim()) + console.log('3️⃣ XML 구조 요약:') + xmlLines.forEach((line, index) => { + const trimmed = line.trim() + if (trimmed.includes('