intelliAuth() is an Express middleware that handles every step of inbound token validation: pulls the bearer token off the request, verifies its signature against the tenant's published key set, checks iss and aud, and surfaces the parsed claims as req.user and req.scopes. Routes that need a specific scope use the requireScope helper.
Install
Section titled “Install”import { intelliAuth } from '@intelliauth/node-sdk/express'The default export is the middleware factory.
Minimum setup
Section titled “Minimum setup”import express from 'express'import { intelliAuth } from '@intelliauth/node-sdk/express'
const app = express()
app.use(intelliAuth({ tenantUrl: process.env.INTELLIAUTH_TENANT_URL!, audience: 'https://api.cymmetri.com',}))
app.get('/me', (req, res) => { res.json({ user: req.user, scopes: req.scopes })})
app.listen(3000)tenantUrl and audience are required. The middleware fetches the tenant's JWKS document on first use, caches it, and refreshes on kid miss.
Options
Section titled “Options”| Option | Required | Default | Description |
|---|---|---|---|
tenantUrl | yes | — | Base URL of the IntelliAuth tenant. |
audience | yes | — | The expected aud claim. Reject tokens whose audience doesn't match. |
issuer | no | tenantUrl | Override the expected iss. Defaults to the tenant URL. |
tokenLocation | no | 'header' | Where to look for the token: 'header' (Authorization), 'cookie' (a named cookie), or a custom extractor function. |
cookieName | no | 'access_token' | If tokenLocation: 'cookie', which cookie to read. |
allowUnauthenticated | no | false | If true, pass through unauthenticated requests instead of returning 401. Useful for routes mounted later in the stack. |
clockTolerance | no | 30 | Seconds of clock skew to allow when validating iat / exp. |
algorithms | no | ['RS256'] | Allowed signing algorithms. Tighten if your tenant only signs with one. |
fetch | no | global fetch | Override the fetch used to fetch JWKS. |
req.user shape
Section titled “req.user shape”After successful validation, the middleware sets:
req.user = { sub: 'usr_01HZX...', email: 'finance.lead@cymmetri.com', email_verified: true, name: 'Finance Lead', // ...whatever claims the token carries}req.scopes = ['users:read', 'audit:read']req.token = { /* full parsed claims */ }Express types are extended via the package's module augmentation; if you don't see autocomplete, add a /// <reference /> line or check your tsconfig.json includes the package.
requireScope
Section titled “requireScope”A per-route guard. Returns a middleware that rejects with 403 if the token doesn't carry the required scope.
import { intelliAuth } from '@intelliauth/node-sdk/express'
app.post('/tenant-admin/users', intelliAuth.requireScope('users:write'), (req, res) => { /* ... */ },)For "any of these scopes":
intelliAuth.requireScope(['audit:read', 'audit:export'], { mode: 'any' })For "all of these":
intelliAuth.requireScope(['users:write', 'groups:write'], { mode: 'all' })mode: 'any' is the default for arrays.
requireUser
Section titled “requireUser”Some routes don't need a specific scope but do need some authenticated user. Use the bare middleware — it already 401s unauthenticated requests when allowUnauthenticated: false (the default). For more nuance:
intelliAuth.requireUser({ emailVerified: true, // also reject users whose email isn't verified mfaLevel: 'aal2', // also reject sessions below this AAL})Error responses
Section titled “Error responses”The middleware produces standardised error shapes:
{ "error": "unauthorized", "message": "Bearer token missing", "request_id": "req_01HZX..."}{ "error": "insufficient_scope", "message": "users:write required", "required_scope": "users:write", "request_id": "req_01HZX..."}For step-up scenarios, return your own 401 with error: "step_up_required" and the required AAL — the step-up topic covers the contract the React SDK expects.
Mixed authenticated + public routes
Section titled “Mixed authenticated + public routes”Two patterns:
// Pattern A: gate at router level.const adminRouter = express.Router()adminRouter.use(intelliAuth({ tenantUrl, audience }))adminRouter.use(intelliAuth.requireScope('admin:*'))app.use('/admin', adminRouter)
// Public routes outside the router stay open.app.get('/health', (req, res) => res.send('ok'))// Pattern B: opt-in per route.const auth = intelliAuth({ tenantUrl, audience, allowUnauthenticated: true })
app.get('/me', auth, intelliAuth.requireUser(), (req, res) => { /* ... */ })app.get('/health', (req, res) => res.send('ok'))Pick whichever fits your code's shape. Pattern A is cleaner when most of your surface is authenticated; Pattern B is cleaner when most is public.
Fastify, Koa, NestJS
Section titled “Fastify, Koa, NestJS”This package is Express-specific. For other frameworks, use the underlying verifyAccessToken() primitive exported from @intelliauth/node-sdk and wrap it in your framework's middleware idiom:
import { verifyAccessToken } from '@intelliauth/node-sdk'
const result = await verifyAccessToken(authorizationHeader, { tenantUrl, audience,})// result.valid, result.user, result.scopes, result.errorThis is the same primitive the Express middleware wraps; you get identical validation logic without the Express integration.
Concurrency
Section titled “Concurrency”The middleware is safe for high-concurrency use. JWKS caching is in-process; a kid miss triggers a single re-fetch shared across concurrent requests. There are no per-request token requests to the platform — validation is local.