Olympus Docs
IntegrateBackends

Express.js integration

Authenticate Express.js routes against Olympus

Express + Olympus uses standard OAuth2 introspection (or JWT validation if you've configured JWT tokens).

Setup

bun add express express-session passport openid-client

OIDC strategy (server-side login)

import express from "express";
import session from "express-session";
import passport from "passport";
import { Issuer, Strategy } from "openid-client";

const app = express();
app.use(session({ secret: process.env.SESSION_SECRET!, resave: false, saveUninitialized: false }));
app.use(passport.session());

(async () => {
  const issuer = await Issuer.discover(process.env.OLYMPUS_ISSUER!);
  const client = new issuer.Client({
    client_id: process.env.OLYMPUS_CLIENT_ID!,
    client_secret: process.env.OLYMPUS_CLIENT_SECRET!,
    redirect_uris: [process.env.OLYMPUS_REDIRECT_URI!],
    response_types: ["code"],
  });

  passport.use("olympus", new Strategy(
    { client, usePKCE: "S256" },
    (tokenSet, userinfo, done) => {
      done(null, { id: tokenSet.claims().sub, ...userinfo });
    }
  ));

  passport.serializeUser((user, done) => done(null, user));
  passport.deserializeUser((obj, done) => done(null, obj));

  app.get("/auth/login", passport.authenticate("olympus"));
  app.get("/auth/callback", passport.authenticate("olympus", {
    successRedirect: "/",
    failureRedirect: "/login-failed",
  }));
})();

Protect routes

function requireAuth(req, res, next) {
  if (!req.user) return res.redirect("/auth/login");
  next();
}

app.get("/dashboard", requireAuth, (req, res) => {
  res.send(`Hello ${req.user.email}`);
});

API token introspection

For API endpoints consumed by SPAs/mobile/M2M:

import express from "express";

app.use("/api", async (req, res, next) => {
  const token = req.headers.authorization?.replace("Bearer ", "");
  if (!token) return res.status(401).json({ error: "missing_token" });

  const response = await fetch(`${process.env.OLYMPUS_ISSUER}/admin/oauth2/introspect`, {
    method: "POST",
    headers: {
      "content-type": "application/x-www-form-urlencoded",
      authorization: `Basic ${Buffer.from(`${process.env.HYDRA_ADMIN_USER}:${process.env.HYDRA_ADMIN_PASS}`).toString("base64")}`,
    },
    body: new URLSearchParams({ token }),
  });
  const info = await response.json();
  if (!info.active) return res.status(401).json({ error: "inactive" });

  req.tokenInfo = info;
  next();
});

Logout

app.post("/auth/logout", (req, res) => {
  req.logout(() => {
    res.redirect(`${process.env.OLYMPUS_ISSUER}/oauth2/sessions/logout`);
  });
});

On this page