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/nodeSetup
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:
| Code | Status | Description |
|---|---|---|
user_not_found | 404 | User does not exist |
tenant_not_found | 404 | Tenant does not exist |
invalid_token | 401 | JWT is invalid or expired |
token_expired | 401 | JWT has expired |
insufficient_permissions | 403 | API key lacks required scope |
rate_limited | 429 | Too many requests |
validation_error | 400 | Invalid request parameters |