HelloJohn / docs
Sdks

Node.js SDK

Use @hellojohn/node to verify tokens, manage users, and perform admin operations from your Node.js backend.

Node.js SDK

@hellojohn/node is the server-side SDK for Node.js. Use it to verify JWTs, manage users, create tenants, and perform any admin operation from your backend.

Installation

npm install @hellojohn/node

Setup

import { createHelloJohnAdminClient } from '@hellojohn/node';

const hj = createHelloJohnAdminClient({
  secretKey: process.env.HJ_SECRET_KEY!,   // sk_live_... or sk_test_...
  // Optional: override API URL (for self-hosted)
  apiUrl: process.env.HJ_API_URL,
});

Store the secret key in an environment variable. Never commit it to version control or expose it to client-side code.

Token verification

The most common use case — verify a JWT from the Authorization header:

import { verifyToken } from '@hellojohn/node';

// Express middleware
app.use(async (req, res, next) => {
  const token = req.headers.authorization?.replace('Bearer ', '');
  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }

  try {
    const payload = await verifyToken(token, {
      secretKey: process.env.HJ_SECRET_KEY!,
    });
    req.user = payload;
    next();
  } catch (err) {
    res.status(401).json({ error: 'Invalid token' });
  }
});

The payload object:

interface TokenPayload {
  sub: string;           // user ID
  email: string;
  email_verified: boolean;
  tenant_id: string;
  organization_id: string | null;
  role: string;          // "admin" | "member" | custom
  custom_claims: Record<string, unknown>;
  iat: number;           // issued at (Unix timestamp)
  exp: number;           // expires at (Unix timestamp)
}

User management

// List users
const { users, meta } = await hj.users.list({
  tenantId: 'tnt_01H...',
  limit: 50,
});

// Get a user
const user = await hj.users.get('usr_01H...');

// Create a user
const newUser = await hj.users.create({
  email: 'alice@example.com',
  password: 'strongpassword',
  displayName: 'Alice',
  tenantId: 'tnt_01H...',
  role: 'member',
});

// Update a user
const updated = await hj.users.update('usr_01H...', {
  displayName: 'Alice Smith',
  role: 'admin',
  publicMetadata: { plan: 'pro' },
});

// Disable a user
await hj.users.disable('usr_01H...');

// Delete a user
await hj.users.delete('usr_01H...');

// Force a password reset
await hj.users.sendPasswordResetEmail('usr_01H...');

Session management

// List a user's active sessions
const sessions = await hj.sessions.list({ userId: 'usr_01H...' });

// Revoke a specific session
await hj.sessions.revoke('sess_01H...');

// Revoke all sessions for a user (force re-login)
await hj.sessions.revokeAll({ userId: 'usr_01H...' });

Tenant management

// Create a tenant
const tenant = await hj.tenants.create({
  name: 'Acme Corp',
  slug: 'acme-corp',
  metadata: { plan: 'enterprise' },
});

// Get a tenant
const tenant = await hj.tenants.get('tnt_01H...');

// Update a tenant
await hj.tenants.update('tnt_01H...', {
  name: 'Acme Corporation',
  config: {
    allowedAuthMethods: ['email_password', 'google'],
    mfaRequired: true,
  },
});

// Delete a tenant
await hj.tenants.delete('tnt_01H...');

Organization management

// Create an organization
const org = await hj.organizations.create({
  tenantId: 'tnt_01H...',
  name: 'Engineering Team',
  slug: 'engineering',
});

// Add a member
await hj.organizations.addMember({
  orgId: 'org_01H...',
  userId: 'usr_01H...',
  role: 'member',
});

// Remove a member
await hj.organizations.removeMember({
  orgId: 'org_01H...',
  userId: 'usr_01H...',
});

API keys

// Create an API key
const { apiKey, secret } = await hj.apiKeys.create({
  name: 'CI/CD Pipeline',
  scopes: ['users:read', 'tenants:read'],
  expiresAt: new Date('2025-01-01'),
});

// The secret is only shown once — store it securely
console.log('API Key Secret:', secret);

// Revoke an API key
await hj.apiKeys.revoke('key_01H...');

Audit logs

// Query audit logs
const logs = await hj.auditLogs.list({
  type: 'user.login_failed',
  from: new Date(Date.now() - 24 * 60 * 60 * 1000),
  limit: 100,
});

for (const entry of logs.data) {
  console.log(`${entry.createdAt} — ${entry.type} — ${entry.actor.email}`);
}

Webhooks

// Register a webhook
const webhook = await hj.webhooks.create({
  url: 'https://yourapp.com/webhooks/hellojohn',
  events: ['user.created', 'user.login', 'mfa.enrolled'],
  secret: 'your-signing-secret',
});

// Verify a webhook payload (in your webhook handler)
import { verifyWebhookSignature } from '@hellojohn/node';

app.post('/webhooks/hellojohn', express.raw({ type: 'application/json' }), (req, res) => {
  const isValid = verifyWebhookSignature({
    payload: req.body,
    signature: req.headers['x-hellojohn-signature'] as string,
    timestamp: req.headers['x-hellojohn-timestamp'] as string,
    secret: process.env.WEBHOOK_SECRET!,
  });

  if (!isValid) {
    return res.status(400).json({ error: 'Invalid signature' });
  }

  const event = JSON.parse(req.body.toString());
  console.log('Received event:', event.type);
  res.json({ received: true });
});

Framework helpers

Express

import { requireAuth, getUser } from '@hellojohn/node/express';

// Protect a route
app.get('/api/me', requireAuth(), (req, res) => {
  const user = getUser(req);
  res.json({ user });
});

// Require a specific role
app.delete('/api/users/:id', requireAuth({ role: 'admin' }), async (req, res) => {
  await hj.users.delete(req.params.id);
  res.json({ deleted: true });
});

Fastify

import { fastifyHelloJohn } from '@hellojohn/node/fastify';

await fastify.register(fastifyHelloJohn, {
  secretKey: process.env.HJ_SECRET_KEY!,
});

fastify.get('/api/me', { preHandler: [fastify.authenticate] }, async (request) => {
  return { user: request.user };
});

Error handling

import { HelloJohnError } from '@hellojohn/node';

try {
  await hj.users.get('usr_nonexistent');
} catch (err) {
  if (err instanceof HelloJohnError) {
    console.log(err.code);      // "user_not_found"
    console.log(err.message);   // "User not found"
    console.log(err.status);    // 404
  }
}

Common error codes:

CodeStatusDescription
user_not_found404User does not exist
tenant_not_found404Tenant does not exist
invalid_token401JWT is invalid or expired
token_expired401JWT has expired
insufficient_permissions403API key lacks required scope
rate_limited429Too many requests
validation_error400Invalid request parameters

On this page