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:
Authorization: Bearer <token>header (preferred)end_user_access_tokencookie (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 balance400— 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.