@wocha/express
v0.1.0
Published
Express.js middleware for Wocha authentication and authorisation
Maintainers
Readme
@wocha/express
Express.js middleware for Wocha authentication and authorisation. Validates JWT access tokens against your Wocha issuer JWKS, attaches session context to req.wocha, checks SpiceDB permissions, and verifies webhook signatures.
Install
npm install @wocha/express express
# or: pnpm add / yarn addPeer dependency: express ^4.18 or ^5.0.
Quick start
Environment variables
WOCHA_ISSUER_URL=https://my-tenant.auth.wocha.ai
WOCHA_CLIENT_ID=your-client-id
WOCHA_CLIENT_SECRET=your-client-secret
# Optional:
WOCHA_AUDIENCE=your-api-audience
WOCHA_API_URL=https://my-tenant.api.wocha.ai
WOCHA_COOKIE_SECRET=optional-separate-cookie-secretProtect API routes
import express from "express";
import { wochaAuth } from "@wocha/express";
const app = express();
app.use(express.json());
const auth = wochaAuth({
issuerUrl: process.env.WOCHA_ISSUER_URL!,
clientId: process.env.WOCHA_CLIENT_ID!,
clientSecret: process.env.WOCHA_CLIENT_SECRET!,
audience: process.env.WOCHA_AUDIENCE,
});
// Attach req.wocha on every request (user may be null)
app.use(auth.middleware());
app.get("/health", (_req, res) => {
res.json({ status: "ok" });
});
app.get("/api/profile", auth.requireAuth(), (req, res) => {
res.json(req.wocha.user);
});
app.delete("/api/posts/:id", auth.requirePermission("post", "delete"), (req, res) => {
res.status(204).send();
});
app.get("/api/org-dashboard", auth.requireOrg(), (req, res) => {
res.json({ org: req.wocha.org });
});Webhook receiver
Register the webhook route before express.json() and use a raw body parser:
import express from "express";
import { verifyWebhook } from "@wocha/express";
const app = express();
app.post(
"/webhooks/wocha",
express.raw({ type: "application/json" }),
verifyWebhook(process.env.WOCHA_WEBHOOK_SECRET!),
(req, res) => {
const event = req.wochaEvent;
console.log(event?.event_type, event?.data);
res.json({ received: true });
},
);
app.use(express.json());req.wocha context
After auth.middleware() runs, each request exposes:
| Field | Type | Description |
|-------|------|-------------|
| user | GreetUser \| null | Authenticated user claims |
| session | GreetSession \| null | Access token and expiry |
| org | GreetOrganisation \| null | Active organisation |
| permissions | string[] | Permissions embedded in the access token |
| featureFlags | Record<string, unknown> | Feature flags from the access token |
| isAuthenticated | boolean | Whether a valid session was resolved |
Session resolution
The middleware resolves authentication from:
Authorization: Bearerheader — validates the JWT signature against JWKS (viajose)- Encrypted session cookie — compatible with
@wocha/remix/@wocha/nextjsBFF cookies; refreshes tokens automatically when near expiry
API reference
wochaAuth(config)
Returns an auth instance with:
middleware()— populatereq.wocha(non-blocking)requireAuth()— 401 when unauthenticatedrequirePermission(resourceType, permission, options?)— SpiceDB check via Customer API; 403 when deniedrequireOrg(orgId?)— ensure org membership; 403 when denied
verifyWebhook(secret, options?)
Express middleware that verifies X-Wocha-Signature and sets req.wochaEvent.
Token format
Wocha issues JWT access tokens signed via JWKS. This middleware validates them locally — no callback to Wocha is needed. For advanced patterns like tiered validation (local JWT for reads, introspection for mutations), see the Resource server guide.
Related packages
@wocha/sdk— Management API client@wocha/nextjs— Next.js App Router adapter@wocha/remix— Remix / React Router BFF adapter
License
Apache-2.0
