Package Exports
- azirid-node
- azirid-node/express
- azirid-node/nestjs
Readme
azirid-node
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-node
# or
pnpm add azirid-node
# or
yarn add azirid-nodeExpress
Middleware — protect all routes under a prefix
import express from 'express'
import { aziridAuth } from 'azirid-node/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-node/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-node/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-node/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-node/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-node/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-node/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,
tenantRole: user.tenantRole,
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-node'
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-node'
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-node'
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 // Active tenant ID (always present)
tenantRole: string // Role in the active tenant: "OWNER" | "MEMBER"
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 |
JWKS_ERROR |
Failed to fetch or parse the JWKS endpoint |
import { AziridAuthError } from 'azirid-node'
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