백채널 로그아웃 기능 추가

This commit is contained in:
2026-05-06 13:45:02 +09:00
parent 14bda89d10
commit 74a41e4475
3 changed files with 41 additions and 30 deletions

69
app.js
View File

@@ -33,7 +33,9 @@ app.set('views', path.join(__dirname, 'views'));
app.use(express.urlencoded({ extended: false }));
const sessionStore = new session.MemoryStore();
const sessionMiddleware = session({
store: sessionStore,
name: 'baron.demo.sid',
secret: process.env.SESSION_SECRET || 'demo-session-secret',
resave: true,
@@ -306,6 +308,8 @@ async function setupOIDC() {
const redirectUri = process.env.OIDC_REDIRECT_URI || 'http://localhost:3000/callback';
const backchannelJwksUrl = deriveBackchannelJwksUrl();
const backchannelJwks = createRemoteJWKSet(new URL(backchannelJwksUrl));
const sessionValidationEnabled =
String(process.env.BARON_SESSION_VALIDATION_ENABLED || 'true').toLowerCase() !== 'false';
console.log(`OIDC Issuer 조회: ${issuerUrl}`);
console.log(`백채널 로그아웃 JWKS 주소: ${backchannelJwksUrl}`);
@@ -322,40 +326,45 @@ async function setupOIDC() {
clientId,
redirectUri,
authorizationEndpoint,
sessionValidationEnabled,
});
app.use(async (req, res, next) => {
const skipPaths = new Set(['/login', '/callback', '/logout', '/backchannel-logout']);
if (skipPaths.has(req.path)) {
return next();
}
const accessToken = req.session?.user?.tokenset?.access_token;
if (!accessToken) {
return next();
}
try {
const validation = await validateBaronSession(accessToken);
if (validation.ok) {
if (validation.profile) {
req.session.user.userinfo = validation.profile;
}
if (sessionValidationEnabled) {
app.use(async (req, res, next) => {
const skipPaths = new Set(['/login', '/callback', '/logout', '/backchannel-logout']);
if (skipPaths.has(req.path)) {
return next();
}
console.warn('[세션 검증] Baron 세션이 유효하지 않아 로컬 세션을 정리합니다.', {
path: req.path,
reason: validation.reason,
});
await destroyDemoSession(req, res);
return res.redirect('/');
} catch (error) {
console.error('[세션 검증] Baron 세션 확인 실패로 로컬 세션을 정리합니다.', error);
await destroyDemoSession(req, res);
return res.redirect('/');
}
});
const accessToken = req.session?.user?.tokenset?.access_token;
if (!accessToken) {
return next();
}
try {
const validation = await validateBaronSession(accessToken);
if (validation.ok) {
if (validation.profile) {
req.session.user.userinfo = validation.profile;
}
return next();
}
console.warn('[세션 검증] Baron 세션이 유효하지 않아 로컬 세션을 정리합니다.', {
path: req.path,
reason: validation.reason,
});
await destroyDemoSession(req, res);
return res.redirect('/');
} catch (error) {
console.error('[세션 검증] Baron 세션 확인 실패로 로컬 세션을 정리합니다.', error);
await destroyDemoSession(req, res);
return res.redirect('/');
}
});
} else {
console.log('[시스템] Baron 세션 재검증 미들웨어를 비활성화했습니다. 백채널 로그아웃 테스트 전용 모드입니다.');
}
app.get('/', (req, res) => {
res.render('index', { user: req.session.user });
@@ -480,7 +489,7 @@ async function setupOIDC() {
jwks: backchannelJwks,
});
const result = await destroySessionsForLogout(sessionMiddleware.store, claims);
const result = await destroySessionsForLogout(sessionStore, claims);
console.log('[백채널 로그아웃] 처리 완료', {
sid: claims.sid || '(없음)',
sub: claims.sub || '(없음)',