Documentation
Build withFramework quickstartsExpress

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 express

Create the client

lib/nulvless.ts
import { createServerClient } from '@nulvless/nulvless-js';

export const nulvless = createServerClient(
  process.env.NULVLESS_URL!,
  process.env.NULVLESS_SECRET!,
);

Authenticate

middleware/auth.ts
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:

types/express.d.ts
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.

Next

On this page