JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 96
  • Score
    100M100P100Q76879F
  • License MIT

Backend JWT verification SDK for Azirid Identity. Validate access tokens locally using JWKS — no HTTP calls to the auth server on every request.

Package Exports

  • azirid-node
  • azirid-node/auth
  • azirid-node/billing
  • azirid-node/express
  • azirid-node/nestjs
  • azirid-node/referral
  • azirid-node/webhooks

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-node

Quick 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 plan

Express (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