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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
import { NextRequest, NextResponse } from 'next/server'
// Mock IdP 엔드포인트 - SAML Response HTML 폼 반환
export async function GET(request: NextRequest) {
try {
// RelayState 파라미터 추출
const url = new URL(request.url)
const relayState = url.searchParams.get('RelayState') || 'mock_test'
console.log('🎭 Mock IdP endpoint accessed', { relayState });
// Mock SAML Response 데이터 (실제 형태와 일치하도록 문자열 형태)
const mockSAMLResponseData = {
nameID: "testuser@samsung.com",
nameIDFormat: "urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress",
attributes: {
email: "testuser@samsung.com",
name: "홍길동",
}
};
// Mock XML SAML Response 생성
const mockXML = `
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
ID="_mock_response_${Date.now()}"
Version="2.0"
IssueInstant="${new Date().toISOString()}">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">MockIdP</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_mock_assertion_${Date.now()}"
Version="2.0"
IssueInstant="${new Date().toISOString()}">
<saml:Issuer>MockIdP</saml:Issuer>
<saml:Subject>
<saml:NameID Format="${mockSAMLResponseData.nameIDFormat}">${mockSAMLResponseData.nameID}</saml:NameID>
</saml:Subject>
<saml:AttributeStatement>
<saml:Attribute Name="email">
<saml:AttributeValue>${mockSAMLResponseData.attributes.email}</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="name">
<saml:AttributeValue>${mockSAMLResponseData.attributes.name}</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>
`.trim();
// Base64 인코딩
const encodedSAMLResponse = Buffer.from(mockXML, 'utf-8').toString('base64');
console.log("🎭 Mock SAML Response created:", {
nameID: mockSAMLResponseData.nameID,
email: mockSAMLResponseData.attributes.email,
name: mockSAMLResponseData.attributes.name,
encodedLength: encodedSAMLResponse.length
});
// 콜백 URL로 POST 요청을 시뮬레이션하는 HTML 폼 반환
const callbackUrl = `${process.env.NEXTAUTH_URL}/api/saml/callback`; // process.env.SAML_SP_CALLBACK_URL
const mockFormHTML = `
<!DOCTYPE html>
<html>
<head>
<title>Mock SAML IdP</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; background-color: #f5f5f5; }
.container { max-width: 600px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.header { color: #e94560; text-align: center; margin-bottom: 20px; }
.info { background: #e3f2fd; padding: 15px; border-radius: 4px; margin-bottom: 20px; }
.button { background: #1976d2; color: white; padding: 12px 24px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; width: 100%; }
.button:hover { background: #1565c0; }
.details { font-size: 14px; color: #666; margin-top: 15px; }
.countdown { font-weight: bold; color: #1976d2; }
</style>
</head>
<body>
<div class="container">
<h1 class="header">🎭 Mock SAML IdP</h1>
<div class="info">
<strong>테스트 모드:</strong> 실제 IdP 대신 Mock 응답을 사용합니다.<br>
<em>실제 데이터 형태와 일치하도록 attributes를 문자열로 전송합니다.</em>
</div>
<form id="mockForm" method="POST" action="${callbackUrl}">
<input type="hidden" name="SAMLResponse" value="${encodedSAMLResponse}" />
<input type="hidden" name="RelayState" value="${relayState}" />
<button type="submit" class="button">Continue with Mock Login</button>
</form>
<div class="details">
<p><strong>테스트 사용자 정보:</strong></p>
<ul>
<li>이메일: ${mockSAMLResponseData.attributes.email}</li>
<li>이름: ${mockSAMLResponseData.attributes.name}</li>
</ul>
<p><span class="countdown" id="countdown">5</span>초 후 자동으로 로그인을 진행합니다...</p>
<p><em>프로덕션 환경에서는 SAML_MOCKING_IDP=false로 설정하세요.</em></p>
</div>
</div>
<script>
let countdown = 5;
const countdownEl = document.getElementById('countdown');
const timer = setInterval(() => {
countdown--;
countdownEl.textContent = countdown;
if (countdown <= 0) {
clearInterval(timer);
console.log('🎭 Auto-submitting mock SAML form...');
document.getElementById('mockForm').submit();
}
}, 1000);
// 사용자가 버튼을 클릭하면 타이머 취소
document.getElementById('mockForm').addEventListener('submit', () => {
clearInterval(timer);
});
</script>
</body>
</html>
`;
return new NextResponse(mockFormHTML, {
headers: {
'Content-Type': 'text/html; charset=utf-8',
'Cache-Control': 'no-cache, no-store, must-revalidate'
}
});
} catch (error) {
console.error('💥 Mock IdP error:', error);
return NextResponse.json({
error: 'Mock IdP failed',
details: error instanceof Error ? error.message : 'Unknown error'
}, { status: 500 });
}
}
|