azirid-auth
v0.1.4
Published
Backend JWT verification SDK for Azirid Identity. Validate access tokens locally using JWKS — no HTTP calls to the auth server on every request.
Downloads
395
Maintainers
Readme
azirid-auth
Backend JWT verification SDK for Azirid Identity.
Validate access tokens locally using JWKS — no HTTP call to the auth server on every request. Includes plug-and-play middleware for Express and NestJS, plus a low-level createVerifier() for any Node.js runtime.
Each Azirid app has its own per-app JWKS endpoint with a unique URL. If the token's signature verifies against your app's JWKS, it's guaranteed to belong to that app — no extra appId check needed.
Installation
npm install azirid-auth
# or
pnpm add azirid-auth
# or
yarn add azirid-authExpress
Middleware — protect all routes under a prefix
import express from "express";
import { aziridAuth } from "azirid-auth/express";
const app = express();
app.use(
"/api",
aziridAuth({
jwksUrl: process.env.AZIRID_JWKS_URL!,
}),
);
app.get("/api/me", (req, res) => {
// req.user is typed as AziridUser
res.json(req.user);
});Protect individual routes
import { aziridAuth } from "azirid-auth/express";
const auth = aziridAuth({
jwksUrl: process.env.AZIRID_JWKS_URL!,
});
router.get("/profile", auth, (req, res) => {
res.json({ id: req.user!.sub, email: req.user!.email });
});TypeScript — augmented req.user
The package automatically augments Express.Request, so req.user is typed as AziridUser with no extra setup.
import type { AziridUser } from "azirid-auth/express";
app.get("/me", auth, (req, res) => {
const user: AziridUser = req.user!;
res.json({ sub: user.sub, email: user.email });
});Custom token extractor or error handler
import { aziridAuth } from "azirid-auth/express";
app.use(
"/api",
aziridAuth({
jwksUrl: process.env.AZIRID_JWKS_URL!,
// Extract token from a cookie instead of the Authorization header
getToken: (req) => req.cookies?.access_token ?? null,
// Custom 401 response shape
onError: (err, _req, res) => {
res.status(401).json({ code: err.code, detail: err.message });
},
}),
);NestJS
1. Register the module globally
// app.module.ts
import { Module } from "@nestjs/common";
import { AziridAuthModule } from "azirid-auth/nestjs";
@Module({
imports: [
AziridAuthModule.forRoot({
jwksUrl: process.env.AZIRID_JWKS_URL!,
}),
],
})
export class AppModule {}2. Protect controllers with AziridAuthGuard
// users.controller.ts
import { Controller, Get, UseGuards } from "@nestjs/common";
import { AziridAuthGuard, CurrentUser, AziridUser } from "azirid-auth/nestjs";
@Controller("users")
export class UsersController {
@UseGuards(AziridAuthGuard)
@Get("me")
getMe(@CurrentUser() user: AziridUser) {
return { id: user.sub, email: user.email };
}
}3. Apply the guard globally (optional)
// main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { AziridAuthGuard } from "azirid-auth/nestjs";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(app.get(AziridAuthGuard));
await app.listen(3000);
}
bootstrap();@CurrentUser() decorator
Injects the verified AziridUser into the handler argument:
@Get("profile")
@UseGuards(AziridAuthGuard)
getProfile(@CurrentUser() user: AziridUser) {
return {
id: user.sub,
email: user.email,
workspaceId: user.workspaceId,
tenantId: user.tenantId,
custom: user.custom,
};
}Low-level: createVerifier()
Use this when you are not using Express or NestJS — e.g. Fastify, Hono, AWS Lambda, edge functions, etc.
import { createVerifier } from "azirid-auth";
const verify = createVerifier({
jwksUrl: process.env.AZIRID_JWKS_URL!,
});
// Inside a request handler:
const user = await verify(token); // throws AziridAuthError on failure
console.log(user.sub, user.email);Hono example
import { Hono } from "hono";
import { createVerifier, AziridAuthError } from "azirid-auth";
const app = new Hono();
const verify = createVerifier({
jwksUrl: process.env.AZIRID_JWKS_URL!,
});
app.use("/api/*", async (c, next) => {
const auth = c.req.header("Authorization");
if (!auth?.startsWith("Bearer ")) {
return c.json({ error: "TOKEN_MISSING" }, 401);
}
try {
const user = await verify(auth.slice(7));
c.set("user", user);
await next();
} catch (err) {
if (err instanceof AziridAuthError) {
return c.json({ error: err.code, message: err.message }, 401);
}
return c.json({ error: "INTERNAL_ERROR" }, 500);
}
});AWS Lambda (Node.js)
import { createVerifier, AziridAuthError } from "azirid-auth";
const verify = createVerifier({
jwksUrl: process.env.AZIRID_JWKS_URL!,
});
export const handler = async (event: AWSLambda.APIGatewayProxyEvent) => {
const token = event.headers.Authorization?.replace("Bearer ", "");
if (!token)
return {
statusCode: 401,
body: JSON.stringify({ error: "TOKEN_MISSING" }),
};
try {
const user = await verify(token);
return { statusCode: 200, body: JSON.stringify({ sub: user.sub }) };
} catch (err) {
return { statusCode: 401, body: JSON.stringify({ error: "Unauthorized" }) };
}
};VerifierOptions
| Option | Type | Required | Description |
| ---------------- | ------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| jwksUrl | string | ✅ | Per-app JWKS URL from the Azirid dashboard. e.g. https://api.azirid.com/v1/jwks/ax7k2m9p4q/.well-known/jwks.json |
| appId | string | — | Optional extra check: reject tokens issued for a different appId. Not needed with per-app JWKS URLs |
| tenantId | string | — | Restrict to a specific tenant |
| environment | "development" \| "production" | — | Restrict to a specific environment |
| audience | string | — | Expected aud claim |
| issuer | string | — | Expected iss claim |
| clockTolerance | number | — | Seconds of tolerance for expiration checks. Default: 5 |
AziridUser — token claims
interface AziridUser {
sub: string; // User ID
email: string; // User email
workspaceId: string; // Workspace that owns the app
appId: string; // App ID the token was issued for
tenantId?: string | null; // Tenant (multi-tenant apps)
environment?: "development" | "production"; // Token environment
sessionId: string; // Active session ID
custom?: Record<string, unknown>; // Custom claims set by your app
iat?: number; // Issued at (epoch seconds)
exp?: number; // Expires at (epoch seconds)
}Error handling — AziridAuthError
All verification failures throw an AziridAuthError with a machine-readable code:
| Code | Meaning |
| ---------------------- | ---------------------------------------------------- |
| TOKEN_MISSING | No token provided |
| TOKEN_INVALID | Signature invalid or malformed JWT |
| TOKEN_EXPIRED | Token has expired |
| APP_MISMATCH | Token was issued for a different appId |
| TENANT_MISMATCH | Token tenant doesn't match expected tenant |
| ENVIRONMENT_MISMATCH | Token environment doesn't match expected environment |
import { AziridAuthError } from "azirid-auth";
try {
const user = await verify(token);
} catch (err) {
if (err instanceof AziridAuthError) {
console.error(err.code, err.message);
}
}Environment variables
The only required config is the per-app JWKS URL (found in your Azirid dashboard under Integration):
AZIRID_JWKS_URL=https://api.azirid.com/v1/jwks/ax7k2m9p4q/.well-known/jwks.jsonThen:
createVerifier({
jwksUrl: process.env.AZIRID_JWKS_URL!,
});License
MIT © Azirid
