azirid-node
v0.6.0
Published
Backend JWT verification SDK for Azirid Identity. Validate access tokens locally using JWKS — no HTTP calls to the auth server on every request.
Maintainers
Readme
azirid-node
Backend SDK for Azirid Identity.
JWT verification via JWKS, billing management, webhook verification, and referral tracking. Includes plug-and-play middleware for Express and NestJS.
Installation
npm install azirid-node
# or
pnpm add azirid-node
# or
yarn add azirid-nodeQuick Start
// Auth — JWT verification
import { createVerifier } from 'azirid-node/auth'
// Billing — payment intents & transfer proofs
import { createBillingClient } from 'azirid-node/billing'
// Referrals — referral tracking & rewards
import { createReferralClient } from 'azirid-node/referral'
// Webhooks — verify incoming webhook signatures
import { createWebhookVerifier } from 'azirid-node/webhooks'Backward compatibility:
import { createVerifier, createBillingClient, createReferralClient } from 'azirid-node'still works — the root entry re-exports everything.
Auth (azirid-node/auth)
Validate access tokens locally using JWKS — no HTTP call to the auth server on every request.
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.
createVerifier()
import { createVerifier } from 'azirid-node/auth'
const verify = createVerifier({
jwksUrl: process.env.AZIRID_JWKS_URL!,
})
const user = await verify(token) // throws AziridAuthError on failure
console.log(user.sub, user.email)VerifierOptions
| Option | Type | Required | Description |
| ---------------- | ------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| jwksUrl | string | Yes | 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 |
| PLAN_REQUIRED | User doesn't have a qualifying plan |
| PLAN_INACTIVE | Subscription is not active |
import { AziridAuthError } from 'azirid-node/auth'
try {
const user = await verify(token)
} catch (err) {
if (err instanceof AziridAuthError) {
console.error(err.code, err.message)
}
}requiresPlan() — subscription guard
import { createVerifier, requiresPlan } from 'azirid-node/auth'
const verify = createVerifier({ jwksUrl: '...' })
const guard = requiresPlan('pro', 'enterprise')
const user = await verify(token)
guard(user) // throws AziridAuthError if user doesn't have a qualifying planExpress (azirid-node/express)
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 })
})Custom token extractor or error handler
app.use(
'/api',
aziridAuth({
jwksUrl: process.env.AZIRID_JWKS_URL!,
getToken: (req) => req.cookies?.access_token ?? null,
onError: (err, _req, res) => {
res.status(401).json({ code: err.code, detail: err.message })
},
}),
)NestJS (azirid-node/nestjs)
1. Register the module globally
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
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)
import { NestFactory } from '@nestjs/core'
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()Billing (azirid-node/billing)
Server-side client for managing payment intents and transfer proofs via the Azirid Billing API.
import { createBillingClient } from 'azirid-node/billing'
const billing = createBillingClient({
secretKey: process.env.AZIRID_SECRET_KEY!,
})| Option | Type | Required | Description |
| ----------- | -------- | -------- | --------------------------------------------------------------------------- |
| secretKey | string | Yes | Your app's secret key (sk_...) from the Azirid dashboard |
| apiUrl | string | — | API base URL. Defaults to https://api.azirid.com. Override for local dev. |
TypeScript — BillingClient type
import { createBillingClient, type BillingClient } from 'azirid-node/billing'
class BillingService {
private readonly billing: BillingClient
constructor() {
this.billing = createBillingClient({
secretKey: process.env.AZIRID_SECRET_KEY!,
})
}
}Payment Intents
// Direct mode — total amount in cents
const intent = await billing.intents.create({
amount: 2999,
currency: 'USD',
description: 'One-time payment',
})
// Tax breakdown mode — subtotal + taxRate
const intent2 = await billing.intents.create({
subtotal: 1000,
taxRate: 15,
currency: 'USD',
description: 'Invoice #123',
reference: 'INV-2026-001',
})
// List, get, cancel
const intents = await billing.intents.list()
const single = await billing.intents.get('intent-id')
const canceled = await billing.intents.cancel('intent-id')Transfer Proofs
await billing.transfers.review('proof-id', {
approved: true,
})Referrals (azirid-node/referral)
Server-side client for managing referral tracking and rewards via the Azirid Referrals API.
import { createReferralClient } from 'azirid-node/referral'
const referral = createReferralClient({
secretKey: process.env.AZIRID_SECRET_KEY!,
})| Option | Type | Required | Description |
| ----------- | -------- | -------- | --------------------------------------------------------------------------- |
| secretKey | string | Yes | Your app's secret key (sk_...) from the Azirid dashboard |
| apiUrl | string | — | API base URL. Defaults to https://api.azirid.com. Override for local dev. |
TypeScript — ReferralClient type
import { createReferralClient, type ReferralClient } from 'azirid-node/referral'
class MyReferralService {
private readonly referral: ReferralClient
constructor() {
this.referral = createReferralClient({
secretKey: process.env.AZIRID_SECRET_KEY!,
})
}
}Get user referral info
const info = await referral.getUserInfo('user-id')
// → { referralCode, totalReferrals, completedReferrals, pendingReferrals }Get user referral stats
const stats = await referral.getUserStats('user-id')
// → { referralCode, totalReferrals, completedReferrals, pendingReferrals,
// expiredReferrals, totalRewardAmount, rewardCurrency, maxRewardsPerUser, rewardsEarned }Process a referral signup
const result = await referral.processSignup('new-user-id', 'REFERRAL_CODE')
// → { id, referrerId, referredId, status, rewardStatus, ... }Complete a referral
const completed = await referral.complete('referral-id')Validate a referral code
const validation = await referral.validateCode('REFERRAL_CODE')
// → { valid: true, isActive: true }Webhooks (azirid-node/webhooks)
Verify incoming webhook payloads from Azirid using HMAC-SHA256 signatures.
When Azirid sends a webhook to your endpoint, the payload is sent as JSON in the body and a signature is included in the x-webhook-signature header. Use createWebhookVerifier to validate the signature and safely extract the event data.
createWebhookVerifier()
import { createWebhookVerifier } from 'azirid-node/webhooks'
const verifier = createWebhookVerifier({
secret: process.env.AZIRID_WEBHOOK_SECRET!, // whsec_xxx from dashboard
})| Option | Type | Required | Description |
| ------------- | -------- | -------- | -------------------------------------------------------------------- |
| secret | string | Yes | Webhook endpoint secret (whsec_...) from the Azirid dashboard |
| toleranceMs | number | — | Max age for the timestamp header. Default: 300000 (5 minutes) |
Verify a webhook
import express from 'express'
import { createWebhookVerifier, WebhookVerificationError } from 'azirid-node/webhooks'
const app = express()
const verifier = createWebhookVerifier({
secret: process.env.AZIRID_WEBHOOK_SECRET!,
})
// Important: use raw body for signature verification
app.post('/webhooks/azirid', express.raw({ type: 'application/json' }), (req, res) => {
try {
const event = verifier.verify(req.body, req.headers)
switch (event.type) {
case 'payment_intent.completed':
const { id, amount, currency, paidAt } = event.data
console.log(`Payment ${id} completed: ${amount} ${currency} at ${paidAt}`)
// Save to your database
break
}
res.json({ received: true })
} catch (err) {
if (err instanceof WebhookVerificationError) {
console.error('Webhook verification failed:', err.message)
return res.status(400).json({ error: err.message })
}
res.status(500).json({ error: 'Internal error' })
}
})WebhookEvent
interface WebhookEvent {
id: string // Unique delivery ID
type: string // Event type (e.g. "payment_intent.completed")
timestamp: number // Unix timestamp (ms) when the webhook was sent
data: Record<string, unknown> // Event payload
}Available events
| Event | Description |
| --------------------------- | ------------------------------------------------ |
| payment_intent.completed | A payment intent was successfully paid |
| user.created | A new user was created |
| user.updated | A user's profile was updated |
| session.created | A new session was created (user logged in) |
| auth.login.failed | A login attempt failed |
Double-check with intents.get()
After receiving a payment_intent.completed webhook, you can optionally verify the intent status via the Billing API:
import { createBillingClient } from 'azirid-node/billing'
const billing = createBillingClient({ secretKey: process.env.AZIRID_SECRET_KEY! })
// Inside your webhook handler
const intent = await billing.intents.get(event.data.id as string)
if (intent.status === 'COMPLETED') {
// Confirmed — safe to fulfill the order
}Backward compatibility
All exports are available from both sub-path entries and the root entry point:
// Sub-path entries (recommended — better tree-shaking)
import { createVerifier } from 'azirid-node/auth'
import { createBillingClient } from 'azirid-node/billing'
import { createReferralClient } from 'azirid-node/referral'
import { createWebhookVerifier } from 'azirid-node/webhooks'
// Root entry (still works)
import { createVerifier, createBillingClient, createReferralClient, createWebhookVerifier } from 'azirid-node'Environment variables
AZIRID_JWKS_URL=https://api.azirid.com/v1/jwks/ax7k2m9p4q/.well-known/jwks.json
AZIRID_SECRET_KEY=sk_live_...License
MIT © Azirid
