Quickstarts · express
Express — Hub login with `openid-client`
Add OIDC to an Express 4 / 5 app using the official Node OIDC library. ~40 lines of code.
- express
- node
- oidc
Tested against:framework: Express 5.0openidClient: openid-client@5.6
Prereqs
- Node 20+
- Express 4 or 5
Step 1 — Register a confidential client
hub clients create \
--name "My Express app" \
--redirect-uri "http://localhost:3000/auth/callback" \
--grant-types authorization_code,refresh_token \
--scopes "openid email profile"Step 2 — Install
npm install express express-session openid-clientStep 3 — Wire it
server.mjs:
import express from "express";
import session from "express-session";
import { Issuer, generators } from "openid-client";
const app = express();
app.use(session({ secret: "dev", resave: false, saveUninitialized: false }));
const issuer = await Issuer.discover("https://hub.thoryn.org");
const client = new issuer.Client({
client_id: process.env.THORYN_CLIENT_ID,
client_secret: process.env.THORYN_CLIENT_SECRET,
redirect_uris: ["http://localhost:3000/auth/callback"],
response_types: ["code"],
});
app.get("/login", (req, res) => {
const codeVerifier = generators.codeVerifier();
const codeChallenge = generators.codeChallenge(codeVerifier);
req.session.codeVerifier = codeVerifier;
res.redirect(client.authorizationUrl({
scope: "openid email profile",
code_challenge: codeChallenge,
code_challenge_method: "S256",
}));
});
app.get("/auth/callback", async (req, res) => {
const params = client.callbackParams(req);
const tokenSet = await client.callback(
"http://localhost:3000/auth/callback",
params,
{ code_verifier: req.session.codeVerifier },
);
req.session.user = tokenSet.claims();
res.redirect("/");
});
app.get("/", (req, res) => {
if (!req.session.user) return res.send(`<a href="/login">Sign in</a>`);
res.send(`<p>Hello, ${req.session.user.name}</p>`);
});
app.listen(3000);Step 4 — Run it
THORYN_CLIENT_ID=... THORYN_CLIENT_SECRET=... node server.mjsWhat's next
- Hub — How it works
- Move sessions out of
express-session(memory) into Redis for production
Troubleshooting
code_verifier mismatch: check that the same session is reused across/loginand/auth/callback. If you're behind a reverse proxy, fixtrust proxy.Issuer.discoverhangs: corporate proxies sometimes block OIDC discovery. SetHTTP_PROXY/HTTPS_PROXY.