This repository has been archived on 2026-10-08. You can view files and clone it, but cannot push or open issues or pull requests.

105 lines
2.8 KiB
JavaScript

import { Router } from "express";
import { Issuer, generators } from "openid-client";
import config from "../config.js";
import { getLogger } from "../logger.js";
const logger = getLogger("OIDC");
let client;
export async function createOidcClient() {
const { issuer: issuerUrl, clientId, clientSecret, redirectUri } =
config.auth.oidc;
if (!issuerUrl || !clientId || !redirectUri) {
logger.warn(
"OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_REDIRECT_URI not set — OIDC disabled.",
);
return null;
}
const issuer = await Issuer.discover(issuerUrl);
client = new issuer.Client({
client_id: clientId,
client_secret: clientSecret || undefined,
redirect_uris: [redirectUri],
response_types: ["code"],
});
return client;
}
export function createOidcRouter(clientUrl) {
const router = Router();
router.get("/auth/login", async (req, res, next) => {
if (!client) {
return res.status(503).send("OIDC not configured");
}
try {
const nonce = generators.nonce();
const state = generators.state();
const code_verifier = generators.codeVerifier();
const code_challenge = generators.codeChallenge(code_verifier);
req.session.oidc = { nonce, state, code_verifier };
const authorizationUrl = client.authorizationUrl({
scope: "openid profile email",
state,
nonce,
code_challenge,
code_challenge_method: "S256",
});
await new Promise((resolve, reject) => {
req.session.save((err) => (err ? reject(err) : resolve()));
});
res.redirect(authorizationUrl);
} catch (e) {
next(e);
}
});
router.get("/auth/callback", async (req, res, next) => {
if (!client) {
return res.status(503).send("OIDC not configured");
}
try {
const params = client.callbackParams(req);
const expectedState = req.session.oidc?.state;
const checks = {
state: expectedState,
nonce: req.session.oidc?.nonce,
code_verifier: req.session.oidc?.code_verifier,
};
const redirectUri = config.auth.oidc.redirectUri;
const tokenSet = await client.callback(redirectUri, params, checks);
const claims = tokenSet.claims();
req.session.user = {
sub: claims.sub,
email: claims.email,
name: claims.name,
};
delete req.session.oidc;
await new Promise((resolve, reject) => {
req.session.save((err) => (err ? reject(err) : resolve()));
});
res.redirect(clientUrl || "/");
} catch (e) {
logger.error("callback error:", e);
next(e);
}
});
router.post("/auth/logout", (req, res) => {
req.session.destroy(() => {
res.json({ ok: true });
});
});
router.get("/auth/logout", (req, res) => {
req.session.destroy(() => {
res.redirect(clientUrl || "/");
});
});
return router;
}