Server SDK Reference

The server SDK (@reauth-dev/sdk/server) provides local JWT verification and balance operations. Token verification requires zero network calls — it derives the signing secret from your API key using HKDF-SHA256.

Setup

import { createServerClient } from '@reauth-dev/sdk/server';

const reauth = createServerClient({
  domain: 'yourdomain.com',
  apiKey: process.env.REAUTH_API_KEY!,  // Required (e.g., "sk_live_...")
  timeout: 10000, // Optional, default 10s (for network calls like getUserById)
});

Token Verification

authenticate(request)

The primary method — extracts token from headers and verifies it locally:

const result = await reauth.authenticate({
  headers: {
    authorization: req.headers.authorization,  // "Bearer <jwt>"
    cookie: req.headers.cookie,                // Or via cookie
  },
});

if (!result.valid || !result.user) {
  return { status: 401, error: result.error || 'Unauthorized' };
}

// Access user info from JWT claims (no network call!)
result.user.id;            // User ID
result.user.roles;         // ["admin", "editor"]
result.user.activeOrgId;   // Active organization ID
result.user.orgRole;       // "owner" | "member"
result.user.subscription;  // { status, planCode, planName, ... }

Token extraction order:

  1. Authorization: Bearer <token> header (preferred)
  2. end_user_access_token cookie (fallback)

verifyToken(token)

Verify a token string directly:

const result = await reauth.verifyToken(jwtString);
// Returns: AuthResult
// {
//   valid: boolean,
//   user: { id, roles, activeOrgId, orgRole, subscription } | null,
//   claims: DomainEndUserClaims | null,
//   error?: string,
// }

extractToken(request)

Extract token without verifying:

const token = reauth.extractToken({
  headers: {
    authorization: req.headers.authorization,
    cookie: req.headers.cookie,
  },
});
// Returns: string | null

Express Middleware Example

From the demo app (apps/demo_api/src/index.ts):

import express, { Request, Response, NextFunction } from 'express';
import { createServerClient } from '@reauth-dev/sdk/server';
import type { AuthResult } from '@reauth-dev/sdk';

type AuthUser = NonNullable<AuthResult['user']>;

declare global {
  namespace Express {
    interface Request {
      user?: AuthUser;
    }
  }
}

const reauth = createServerClient({
  domain: process.env.REAUTH_DOMAIN!,
  apiKey: process.env.REAUTH_API_KEY!,
});

async function authMiddleware(req: Request, res: Response, next: NextFunction) {
  const result = await reauth.authenticate({
    headers: {
      authorization: req.headers.authorization as string | undefined,
      cookie: req.headers.cookie,
    },
  });

  if (!result.valid || !result.user) {
    res.status(401).json({ error: result.error || 'Unauthorized' });
    return;
  }

  req.user = result.user;
  next();
}

app.get('/api/protected', authMiddleware, (req, res) => {
  res.json({ userId: req.user!.id, roles: req.user!.roles });
});

Next.js App Router Example

// app/api/protected/route.ts
import { createServerClient } from '@reauth-dev/sdk/server';
import { NextRequest, NextResponse } from 'next/server';

const reauth = createServerClient({
  domain: process.env.REAUTH_DOMAIN!,
  apiKey: process.env.REAUTH_API_KEY!,
});

export async function GET(request: NextRequest) {
  const result = await reauth.authenticate({
    headers: {
      authorization: request.headers.get('authorization') ?? undefined,
      cookie: request.headers.get('cookie') ?? undefined,
    },
  });

  if (!result.valid || !result.user) {
    return NextResponse.json(
      { error: result.error || 'Unauthorized' },
      { status: 401 },
    );
  }

  return NextResponse.json({ user: result.user });
}

User Management

getUserById(userId)

Fetch full user details from the reauth API. This is a network call — use it when you need info not in the JWT (email, frozen status, etc.).

const details = await reauth.getUserById('user-uuid');
// Returns: UserDetails | null
// {
//   id: string,
//   email: string,
//   roles: string[],
//   emailVerifiedAt: string | null,
//   lastLoginAt: string | null,
//   isFrozen: boolean,
//   isWhitelisted: boolean,
//   createdAt: string | null,
// }

Returns null if user not found. Throws on invalid API key.

Balance Operations

All balance operations use the Developer API (authenticated via API key, not user session).

getBalance(userId)

const { balance } = await reauth.getBalance('user-uuid');

charge(userId, opts)

Deduct credits from a user's balance. Idempotent via requestUuid.

const { newBalance } = await reauth.charge('user-uuid', {
  amount: 10,
  requestUuid: 'unique-operation-id', // Prevents double-charging on retry
  note: 'Used 10 credits for API call',  // Optional
});

Errors:

  • 402 — Insufficient balance
  • 400 — Invalid amount (must be positive)
  • 401 — Invalid API key

deposit(userId, opts)

Add credits to a user's balance. Idempotent via requestUuid.

const { newBalance } = await reauth.deposit('user-uuid', {
  amount: 100,
  requestUuid: 'welcome-bonus-user123',
  note: 'Welcome bonus',
});

getTransactions(userId, opts?)

const { transactions } = await reauth.getTransactions('user-uuid', {
  limit: 50,
  offset: 0,
});
// Returns: { transactions: BalanceTransaction[] }
// Each: { id, amountDelta, reason, balanceAfter, createdAt }

Idempotency Pattern

Always use unique requestUuid values for charge and deposit operations. This prevents double-processing if your request is retried:

import { v4 as uuid } from 'uuid';

// Good — unique ID per logical operation
const operationId = uuid();
await reauth.charge(userId, {
  amount: 10,
  requestUuid: operationId,
  note: 'Generate report',
});

// Good — deterministic ID tied to the operation
await reauth.charge(userId, {
  amount: 10,
  requestUuid: `report-${reportId}`,
  note: `Generate report ${reportId}`,
});

// If the same requestUuid is sent again, the charge is NOT applied twice.
// The original result is returned.

Webhook Verification

For webhook signature verification, use the dedicated webhooks entrypoint:

import { verifyWebhookSignature } from '@reauth-dev/sdk/webhooks';

See webhooks.md for details.