Express
Middleware that authenticates, and middleware that authorizes.
Two pieces of middleware cover almost everything: one that rejects a bad token, one that rejects a caller without the role. The Node page explains what they call underneath.
Install
npm install @nulvless/nulvless-js expressCreate the client
import { createServerClient } from '@nulvless/nulvless-js';
export const nulvless = createServerClient(
process.env.NULVLESS_URL!,
process.env.NULVLESS_SECRET!,
);Authenticate
import type { NextFunction, Request, Response } from 'express';
import { nulvless } from '../lib/nulvless';
export async function requireAuth(req: Request, res: Response, next: NextFunction) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) return res.status(401).end();
const { data, error } = await nulvless.auth.verify(undefined, {
headers: { Authorization: `Bearer ${token}` },
});
if (error) return res.status(401).end();
req.session = data.session;
req.userId = data.user.id;
next();
}Type the request
So req.userId is not an error in TypeScript:
import type { Session } from '@nulvless/nulvless-js';
declare global {
namespace Express {
interface Request {
session?: Session;
userId?: string;
}
}
}Authorize
Same call, with the question included — no second round trip.
export function requireRole(roles: string[]) {
return async (req: Request, res: Response, next: NextFunction) => {
const token = req.headers.authorization?.replace('Bearer ', '');
const { error } = await nulvless.auth.verify(
{ resource: req.params.org, roles },
{ headers: { Authorization: `Bearer ${token}` } },
);
if (error) return res.status(403).end();
next();
};
}
app.delete('/orgs/:org/keys/:id', requireRole(['owner', 'admin']), handler);requireRole verifies the token too, so it stands alone. Stack it after
requireAuth only when the handler also wants req.userId.
Act on the user's behalf
import { forwardUserHeaders } from '@nulvless/nulvless-js';
app.post('/orgs/:org/invites', requireAuth, async (req, res) => {
const { data, error } = await nulvless.auth.grants.invite(
{ invites: [{ email: req.body.email, role_id: req.body.role_id }], resource: req.params.org },
{ headers: forwardUserHeaders(req) },
);
if (error) return res.status(400).json({ error });
res.json(data);
});An Express Request is a Node IncomingMessage, so it goes straight in.
Require a fresh confirmation
app.delete('/keys/:id', requireAuth, async (req, res) => {
const { error } = await nulvless.auth.sessions.revoke({
id: req.params.id,
nonce: req.headers['nlv-sudo-nonce'] as string,
});
if (error) return res.status(403).json({ error });
res.status(204).end();
});That header carries the code the user typed after auth.sessions.sudo() sent
it. Spent this way it authorizes only this call, and never elevates the session.
See Sudo mode.