Skip to main content

Node.js (non-React)

For Express / Fastify / Koa / NestJS / Hono backends. Same pattern as the Python guide: proxy auth flows, validate JWTs locally.

Setup

npm install express axios jose cookie-parser

Express example

import express from 'express';
import axios from 'axios';
import cookieParser from 'cookie-parser';
import { createRemoteJWKSet, jwtVerify } from 'jose';

const AUTH_URL = process.env.AUTH_URL ?? 'http://auth:4000';
const app = express();
app.use(express.json());
app.use(cookieParser());

// ── proxy auth flows ────────────────────────────────────────
app.post('/auth/login', async (req, res) => {
const upstream = await axios.post(`${AUTH_URL}/v1/auth/login`, req.body, {
validateStatus: () => true,
});
// forward Set-Cookie headers
const setCookies = upstream.headers['set-cookie'];
if (setCookies) res.setHeader('Set-Cookie', setCookies);
res.status(upstream.status).json(upstream.data);
});

// ── validate JWT on protected routes ────────────────────────
const jwks = createRemoteJWKSet(new URL(`${AUTH_URL}/.well-known/jwks.json`));

async function requireAuth(req: any, res: any, next: any) {
const token =
req.cookies?.identsphere_at ??
req.headers.authorization?.replace(/^Bearer /, '');
if (!token) return res.status(401).json({ error: 'no token' });
try {
const { payload } = await jwtVerify(token, jwks);
req.user = payload;
next();
} catch (e) {
res.status(401).json({ error: 'invalid token' });
}
}

app.get('/api/me', requireAuth, (req: any, res) => {
res.json({ user_id: req.user.sub, org_id: req.user.org });
});

app.listen(3000);

Fastify

Same shape; Fastify's plugin model lets you wrap requireAuth as a fastify-plugin for cleaner composition.

import Fastify from 'fastify';

const app = Fastify();
app.decorate('authenticate', requireAuth);
app.get('/api/me', { preHandler: app.authenticate }, async (req: any) => ({
user_id: req.user.sub,
}));

NestJS

Wrap as a CanActivate guard:

@Injectable()
export class AuthGuard implements CanActivate {
async canActivate(ctx: ExecutionContext): Promise<boolean> {
const req = ctx.switchToHttp().getRequest();
const token = req.cookies?.identsphere_at;
if (!token) return false;
const { payload } = await jwtVerify(token, jwks);
req.user = payload;
return true;
}
}

Calling auth endpoints from the backend

To make calls on behalf of the user, forward the cookies:

async function fetchProfile(req: express.Request) {
return axios.get(`${AUTH_URL}/v1/users/me`, {
headers: { Cookie: req.headers.cookie ?? '' },
});
}

Caveats

Same revocation lag as Pattern 2 (~JWKS cache TTL + access token TTL). Shorten IDENTSPHERE_ACCESS_EXPIRY_SECS if you need tighter revocation.

Full Express starter

A complete example will live in examples/express-nodejs-starter/ in the repo (coming v1.1).