로직 수행 로그 출력화
This commit is contained in:
103
app.js
103
app.js
@@ -100,6 +100,12 @@ function removeSessionBinding(sessionId) {
|
||||
removeSessionBindingFromMap(sidToSessionIds, existing.sid, sessionId);
|
||||
removeSessionBindingFromMap(subToSessionIds, existing.sub, sessionId);
|
||||
sessionIdToBinding.delete(sessionId);
|
||||
|
||||
console.log('[세션 매핑] 제거 완료', {
|
||||
sessionId,
|
||||
sid: existing.sid || '(없음)',
|
||||
sub: existing.sub || '(없음)',
|
||||
});
|
||||
}
|
||||
|
||||
function registerSessionBinding(sessionId, claims) {
|
||||
@@ -111,10 +117,12 @@ function registerSessionBinding(sessionId, claims) {
|
||||
addSessionBinding(sidToSessionIds, sid, sessionId);
|
||||
addSessionBinding(subToSessionIds, sub, sessionId);
|
||||
|
||||
console.log('[Session Binding] Registered', {
|
||||
console.log('[세션 매핑] 등록 완료', {
|
||||
sessionId,
|
||||
sid: sid || '(none)',
|
||||
sub: sub || '(none)',
|
||||
sid: sid || '(없음)',
|
||||
sub: sub || '(없음)',
|
||||
sidSessionCount: sid ? sidToSessionIds.get(sid)?.size || 0 : 0,
|
||||
subSessionCount: sub ? subToSessionIds.get(sub)?.size || 0 : 0,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -196,6 +204,7 @@ async function validateBaronSession(accessToken) {
|
||||
|
||||
function destroyDemoSession(req, res) {
|
||||
const sessionId = req.sessionID;
|
||||
console.log('[로컬 로그아웃] 현재 세션 정리 시작', { sessionId });
|
||||
removeSessionBinding(sessionId);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
@@ -203,6 +212,7 @@ function destroyDemoSession(req, res) {
|
||||
if (res) {
|
||||
res.clearCookie('baron.demo.sid');
|
||||
}
|
||||
console.log('[로컬 로그아웃] 현재 세션 정리 완료', { sessionId });
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
@@ -214,7 +224,7 @@ async function verifyBackchannelLogoutToken({
|
||||
expectedAudience,
|
||||
jwks,
|
||||
}) {
|
||||
const { payload } = await jwtVerify(logoutToken, jwks, {
|
||||
const { payload, protectedHeader } = await jwtVerify(logoutToken, jwks, {
|
||||
issuer: expectedIssuer,
|
||||
audience: expectedAudience,
|
||||
});
|
||||
@@ -245,6 +255,16 @@ async function verifyBackchannelLogoutToken({
|
||||
throw new Error('logout_token replay detected');
|
||||
}
|
||||
|
||||
console.log('[백채널 로그아웃] 토큰 검증 성공', {
|
||||
alg: protectedHeader.alg,
|
||||
kid: protectedHeader.kid || '(없음)',
|
||||
iss: payload.iss,
|
||||
aud: payload.aud,
|
||||
sid: sid || '(없음)',
|
||||
sub: sub || '(없음)',
|
||||
jti,
|
||||
});
|
||||
|
||||
return {
|
||||
sid,
|
||||
sub,
|
||||
@@ -257,13 +277,20 @@ async function destroySessionsForLogout(store, claims) {
|
||||
const sessionIds = getSessionIdsForLogoutClaims(claims);
|
||||
let destroyedCount = 0;
|
||||
|
||||
console.log('[백채널 로그아웃] 세션 탐색 결과', {
|
||||
sid: claims.sid || '(없음)',
|
||||
sub: claims.sub || '(없음)',
|
||||
matchedSessionIds: sessionIds,
|
||||
});
|
||||
|
||||
for (const sessionId of sessionIds) {
|
||||
removeSessionBinding(sessionId);
|
||||
try {
|
||||
await destroySessionById(store, sessionId);
|
||||
destroyedCount += 1;
|
||||
console.log('[백채널 로그아웃] 세션 파기 완료', { sessionId });
|
||||
} catch (error) {
|
||||
console.error('[Backchannel Logout] Failed to destroy session', {
|
||||
console.error('[백채널 로그아웃] 세션 파기 실패', {
|
||||
sessionId,
|
||||
error: error.message,
|
||||
});
|
||||
@@ -280,10 +307,22 @@ async function setupOIDC() {
|
||||
const backchannelJwksUrl = deriveBackchannelJwksUrl();
|
||||
const backchannelJwks = createRemoteJWKSet(new URL(backchannelJwksUrl));
|
||||
|
||||
console.log(`Discovering issuer: ${issuerUrl}`);
|
||||
console.log(`Back-channel logout JWKS: ${backchannelJwksUrl}`);
|
||||
console.log(`OIDC Issuer 조회: ${issuerUrl}`);
|
||||
console.log(`백채널 로그아웃 JWKS 주소: ${backchannelJwksUrl}`);
|
||||
const issuer = await discovery(new URL(issuerUrl), clientId);
|
||||
issuer.token_endpoint_auth_method = 'none';
|
||||
const authorizationEndpoint =
|
||||
(typeof issuer.serverMetadata === 'function'
|
||||
? issuer.serverMetadata()?.authorization_endpoint
|
||||
: undefined) ||
|
||||
issuer.authorization_server_metadata?.authorization_endpoint ||
|
||||
issuer.authorization_endpoint ||
|
||||
'(확인 불가)';
|
||||
console.log('[시스템] OIDC 설정 완료', {
|
||||
clientId,
|
||||
redirectUri,
|
||||
authorizationEndpoint,
|
||||
});
|
||||
|
||||
app.use(async (req, res, next) => {
|
||||
const skipPaths = new Set(['/login', '/callback', '/logout', '/backchannel-logout']);
|
||||
@@ -305,14 +344,14 @@ async function setupOIDC() {
|
||||
return next();
|
||||
}
|
||||
|
||||
console.warn('[Session Validation] Baron session is no longer valid', {
|
||||
console.warn('[세션 검증] Baron 세션이 유효하지 않아 로컬 세션을 정리합니다.', {
|
||||
path: req.path,
|
||||
reason: validation.reason,
|
||||
});
|
||||
await destroyDemoSession(req, res);
|
||||
return res.redirect('/');
|
||||
} catch (error) {
|
||||
console.error('[Session Validation] Failed to validate Baron session', error);
|
||||
console.error('[세션 검증] Baron 세션 확인 실패로 로컬 세션을 정리합니다.', error);
|
||||
await destroyDemoSession(req, res);
|
||||
return res.redirect('/');
|
||||
}
|
||||
@@ -323,15 +362,15 @@ async function setupOIDC() {
|
||||
});
|
||||
|
||||
app.get('/login', async (req, res) => {
|
||||
console.log(`\n[Login Start] Session: ${req.sessionID}`);
|
||||
console.log(`\n[로그인 시작] 세션 ID: ${req.sessionID}`);
|
||||
|
||||
if (!req.session.state) {
|
||||
req.session.code_verifier = randomPKCECodeVerifier();
|
||||
req.session.state = randomState();
|
||||
req.session.nonce = randomNonce();
|
||||
console.log(`[Login] New state generated: ${req.session.state}`);
|
||||
console.log('[로그인] 신규 state/nonce/code_verifier 생성 완료');
|
||||
} else {
|
||||
console.log(`[Login] Re-using existing state: ${req.session.state}`);
|
||||
console.log('[로그인] 기존 state 재사용');
|
||||
}
|
||||
|
||||
const code_challenge = await calculatePKCECodeChallenge(req.session.code_verifier);
|
||||
@@ -350,14 +389,17 @@ async function setupOIDC() {
|
||||
state: req.session.state,
|
||||
});
|
||||
|
||||
console.log('[로그인] Baron 인증 화면으로 이동', { url: url.href });
|
||||
res.redirect(url.href);
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/callback', async (req, res) => {
|
||||
console.log(`\n[Callback Start] Session: ${req.sessionID}`);
|
||||
console.log(`[Callback Info] State from URL: ${req.query.state}`);
|
||||
console.log(`[Callback Info] State in Session: ${req.session.state}`);
|
||||
console.log(`\n[콜백 시작] 세션 ID: ${req.sessionID}`);
|
||||
console.log('[콜백] URL state / 세션 state', {
|
||||
urlState: req.query.state,
|
||||
sessionState: req.session.state,
|
||||
});
|
||||
|
||||
if (!req.session.state || !req.session.code_verifier) {
|
||||
if (req.session.user) {
|
||||
@@ -382,7 +424,7 @@ async function setupOIDC() {
|
||||
},
|
||||
);
|
||||
|
||||
console.log('[Callback Success] Token exchanged');
|
||||
console.log('[콜백] Authorization Code -> Token 교환 성공');
|
||||
|
||||
const tokenClaims = tokenset.claims();
|
||||
const userinfo = await fetchUserInfo(
|
||||
@@ -398,9 +440,15 @@ async function setupOIDC() {
|
||||
delete req.session.code_verifier;
|
||||
delete req.session.nonce;
|
||||
|
||||
console.log('[콜백] 사용자 세션 생성 완료', {
|
||||
sessionId: req.sessionID,
|
||||
sid: tokenClaims.sid || '(없음)',
|
||||
sub: tokenClaims.sub || '(없음)',
|
||||
});
|
||||
|
||||
req.session.save(() => res.redirect('/profile'));
|
||||
} catch (err) {
|
||||
console.error('[Callback Error]', err);
|
||||
console.error('[콜백] 인증 처리 실패', err);
|
||||
res.status(500).render('error', {
|
||||
message: 'Authentication Failed',
|
||||
detail: err.message,
|
||||
@@ -413,7 +461,14 @@ async function setupOIDC() {
|
||||
? req.body.logout_token.trim()
|
||||
: '';
|
||||
|
||||
console.log('\n[백채널 로그아웃] 요청 수신', {
|
||||
hasLogoutToken: logoutToken !== '',
|
||||
contentType: req.headers['content-type'] || '(없음)',
|
||||
userAgent: req.headers['user-agent'] || '(없음)',
|
||||
});
|
||||
|
||||
if (!logoutToken) {
|
||||
console.warn('[백채널 로그아웃] logout_token 누락');
|
||||
return res.status(400).json({ error: 'logout_token is required' });
|
||||
}
|
||||
|
||||
@@ -426,9 +481,9 @@ async function setupOIDC() {
|
||||
});
|
||||
|
||||
const result = await destroySessionsForLogout(sessionMiddleware.store, claims);
|
||||
console.log('[Backchannel Logout] Processed', {
|
||||
sid: claims.sid || '(none)',
|
||||
sub: claims.sub || '(none)',
|
||||
console.log('[백채널 로그아웃] 처리 완료', {
|
||||
sid: claims.sid || '(없음)',
|
||||
sub: claims.sub || '(없음)',
|
||||
destroyedCount: result.destroyedCount,
|
||||
sessionIds: result.sessionIds,
|
||||
});
|
||||
@@ -438,7 +493,7 @@ async function setupOIDC() {
|
||||
destroyedSessionCount: result.destroyedCount,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[Backchannel Logout] Verification failed', error);
|
||||
console.error('[백채널 로그아웃] 검증 또는 세션 정리 실패', error);
|
||||
return res.status(400).json({
|
||||
error: 'invalid logout token',
|
||||
detail: error.message,
|
||||
@@ -448,8 +503,10 @@ async function setupOIDC() {
|
||||
|
||||
app.get('/profile', (req, res) => {
|
||||
if (!req.session.user) {
|
||||
console.log('[프로필] 비로그인 상태로 접근하여 루트로 이동');
|
||||
return res.redirect('/');
|
||||
}
|
||||
console.log('[프로필] 로그인 세션으로 접근', { sessionId: req.sessionID });
|
||||
res.render('profile', { user: req.session.user });
|
||||
});
|
||||
|
||||
@@ -460,11 +517,11 @@ async function setupOIDC() {
|
||||
});
|
||||
|
||||
app.listen(port, '0.0.0.0', () => {
|
||||
console.log(`Demo app listening at http://localhost:${port}`);
|
||||
console.log(`[시스템] 데모 앱이 실행 중입니다. http://localhost:${port}`);
|
||||
});
|
||||
}
|
||||
|
||||
setupOIDC().catch((err) => {
|
||||
console.error('OIDC setup failed:', err);
|
||||
console.error('[시스템] OIDC 초기화 실패', err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user