미인증 진입 시 중간 화면 없이 곧장 Keycloak 로그인 폼으로 리다이렉트
- requireAuth: /login 거치지 않고 loginRedirect로 직접 Keycloak으로 - /login 라우트도 oidcReady면 즉시 리다이렉트 - OIDC discovery 실패 시에만 안내 페이지 폴백, /api/*는 401 유지 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
12
src/auth.js
12
src/auth.js
@@ -4,6 +4,11 @@ import { config } from "./config.js";
|
|||||||
|
|
||||||
let client;
|
let client;
|
||||||
|
|
||||||
|
// OIDC 클라이언트가 준비됐는지(discovery 성공) 여부
|
||||||
|
export function oidcReady() {
|
||||||
|
return !!client;
|
||||||
|
}
|
||||||
|
|
||||||
export async function initOidc() {
|
export async function initOidc() {
|
||||||
const issuer = await Issuer.discover(config.oidc.issuer);
|
const issuer = await Issuer.discover(config.oidc.issuer);
|
||||||
client = new issuer.Client({
|
client = new issuer.Client({
|
||||||
@@ -47,7 +52,10 @@ export async function handleCallback(req) {
|
|||||||
// 로그인 필수 가드
|
// 로그인 필수 가드
|
||||||
export function requireAuth(req, res, next) {
|
export function requireAuth(req, res, next) {
|
||||||
if (req.session.user) return next();
|
if (req.session.user) return next();
|
||||||
// API 요청은 401, 페이지 요청은 로그인으로
|
// API 요청은 401
|
||||||
if (req.path.startsWith("/api/")) return res.status(401).json({ error: "unauthorized" });
|
if (req.path.startsWith("/api/")) return res.status(401).json({ error: "unauthorized" });
|
||||||
return res.redirect("/login");
|
// 페이지 요청은 중간 화면 없이 곧장 Keycloak 로그인 폼으로 보낸다.
|
||||||
|
if (oidcReady()) return loginRedirect(req, res);
|
||||||
|
// OIDC 미준비(discovery 실패)면 안내 페이지로 폴백
|
||||||
|
return res.redirect("/login?error=" + encodeURIComponent("로그인 서비스를 사용할 수 없습니다. 잠시 후 다시 시도해 주세요."));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { fileURLToPath } from "url";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import { config } from "./config.js";
|
import { config } from "./config.js";
|
||||||
import { siteGroups } from "./sites.js";
|
import { siteGroups } from "./sites.js";
|
||||||
import { initOidc, loginRedirect, handleCallback, requireAuth } from "./auth.js";
|
import { initOidc, loginRedirect, handleCallback, requireAuth, oidcReady } from "./auth.js";
|
||||||
import * as db from "./db.js";
|
import * as db from "./db.js";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
@@ -36,9 +36,14 @@ app.use((req, res, next) => {
|
|||||||
app.get("/healthz", (_req, res) => res.json({ ok: true }));
|
app.get("/healthz", (_req, res) => res.json({ ok: true }));
|
||||||
|
|
||||||
// --- 인증 ---
|
// --- 인증 ---
|
||||||
|
// SSO 세션이 없으면 중간 페이지 없이 바로 Keycloak(커스텀 테마) 로그인으로 보낸다.
|
||||||
|
// (OIDC 초기화 실패 등으로 로그인 불가하면 안내용 login.ejs 로 폴백)
|
||||||
app.get("/login", (req, res) => {
|
app.get("/login", (req, res) => {
|
||||||
if (req.session.user) return res.redirect("/");
|
if (req.session.user) return res.redirect("/");
|
||||||
res.render("login");
|
if (req.query.error) return res.render("login", { error: req.query.error });
|
||||||
|
// OIDC 준비됐으면 중간 화면 없이 곧장 Keycloak 로그인 폼으로
|
||||||
|
if (oidcReady()) return loginRedirect(req, res);
|
||||||
|
return res.render("login", { error: "로그인 서비스를 사용할 수 없습니다. 잠시 후 다시 시도해 주세요." });
|
||||||
});
|
});
|
||||||
app.get("/auth/login", (req, res) => loginRedirect(req, res));
|
app.get("/auth/login", (req, res) => loginRedirect(req, res));
|
||||||
app.get("/auth/callback", async (req, res) => {
|
app.get("/auth/callback", async (req, res) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user