105 lines
2.8 KiB
JavaScript
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;
|
|
}
|