Express.js Middleware
Verify Twingate Identity Firewall JWTs in an Express.js application.
This guide shows how to verify Twingate Identity Firewall JWTs in an Express.js application using the jose library. For architecture details and the full JWT reference, see the Identity Firewall for Web Apps overview.
Prerequisites
- Node.js (LTS recommended)
- An Express.js application
Configure the Gateway to Inject the JWT
The Gateway injects no headers by default. On the Web App Resource, add a request header so the Gateway sends the signed JWT on every request. The recommended convention is a Bearer token in the Authorization header:
| Header Key | Value Template |
|---|---|
Authorization | Bearer {{jwt}} |
The header name and format are configurable at the Gateway/Resource level. This guide reads the JWT from Authorization using the Bearer scheme. See Request Headers for the full list of options and template variables.
Install Dependencies
npm install josejose is a standards-compliant JWT library that runs in Node.js, Deno, and edge runtimes.
Add the Middleware
Save this file as twingate-middleware.js in your project.
import { createRemoteJWKSet, jwtVerify } from "jose";
export function twingateAuth(jwksUrl) { const jwks = createRemoteJWKSet(new URL(jwksUrl));
return async (req, res, next) => { const auth = req.headers.authorization ?? "";
if (!auth) { req.twingateIdentity = null; return next(); }
const parts = auth.split(" "); if (parts.length !== 2 || parts[0].toLowerCase() !== "bearer") { return res.status(401).json({ error: "Malformed Authorization header" }); }
try { const { payload } = await jwtVerify(parts[1], jwks, { algorithms: ["ES256"], requiredClaims: ["exp", "iat"], clockTolerance: "30s", }); req.twingateIdentity = payload; return next(); } catch { return res.status(401).json({ error: "Invalid token" }); } };}How It Works
JWKS Setup
createRemoteJWKSet fetches and caches the Gateway’s public keys automatically. You create it once at middleware initialization, and jose handles key rotation and refresh behind the scenes.
Request Handling
When no Authorization header is present, the middleware sets req.twingateIdentity = null and calls next(). This lets health checks and public routes work without authentication.
Token Verification
The middleware parses the Bearer token, verifies the ES256 signature against the JWKS, requires exp/iat claims, and allows 30 seconds of clock tolerance. On success, the full JWT payload is attached to req.twingateIdentity. On failure, a 401 JSON response is returned.
Use Identity in Your App
Reading Identity
A /me route that reads req.twingateIdentity and returns the user info:
app.get("/me", (req, res) => { if (!req.twingateIdentity) { return res.status(401).json({ error: "Unauthorized" }); } const { user, device, resource } = req.twingateIdentity; res.json({ user, device, resource });});Group-Based Authorization
An /admin route that checks Group membership before granting access:
app.get("/admin", (req, res) => { if (!req.twingateIdentity) { return res.status(401).json({ error: "Unauthorized" }); } const { groups } = req.twingateIdentity.user; if (!groups.includes("admin")) { return res.status(403).json({ error: "Forbidden" }); } res.json({ message: "Welcome, admin" });});Configuration
| Variable | Description |
|---|---|
TWINGATE_JWKS_URL | Your Twingate tenant’s JWKS endpoint: https://<your-tenant>.twingate.com/api/v1/jwk/ec |
Wire it up with a one-liner:
app.use(twingateAuth(process.env.TWINGATE_JWKS_URL));Next Steps
- JWT Payload Reference for the full token structure
- Request Headers for configuring which headers the Gateway injects
- Django, Next.js, and Next.js + Auth.js guides for other frameworks
Last updated 3 hours ago