SDKsNode.js SDK
Verify Token
Verify HelloJohn JWTs in Node.js — local JWKS verification, API-based verification, and extracting claims from access tokens.
Verify Token
Verify HelloJohn access tokens in your Node.js backend to authenticate API requests.
Local Verification (Recommended)
Verify the JWT signature locally using the HelloJohn JWKS endpoint. No network call to HelloJohn per request:
import { HelloJohn } from "@hellojohn/node";
const hj = new HelloJohn({
tenantId: process.env.HELLOJOHN_TENANT_ID!,
secretKey: process.env.HELLOJOHN_SECRET_KEY!,
});
// Returns the decoded payload or throws
const payload = await hj.verifyToken(token);
console.log(payload.sub); // User IDverifyToken Options
const payload = await hj.verifyToken(token, {
// Optional: validate additional claims
audience: process.env.HELLOJOHN_TENANT_ID, // Default: your tenant ID
clockSkewSeconds: 30, // Allow 30s clock drift
});Return Value:
interface TokenPayload {
sub: string; // User ID
sid: string; // Session ID
iss: string; // Issuer: "https://api.hellojohn.dev"
aud: string; // Audience: your tenant ID
iat: number; // Issued at (Unix timestamp)
exp: number; // Expiry (Unix timestamp)
email?: string; // User email (if included in claims)
org_id?: string; // Active organization ID
org_role?: string; // User's role in active org
[key: string]: unknown; // Custom claims
}API-Based Verification
For instant revocation (verifies against HelloJohn's session store):
const { valid, payload, error } = await hj.verifyTokenRemote(token);
if (!valid) {
// Token is expired, revoked, or invalid
throw new Error(error?.message ?? "Unauthorized");
}
console.log(payload.sub); // User IDThis makes one API call to HelloJohn per request. Use for high-security endpoints where you need to detect revoked sessions immediately.
Express Middleware
A reusable middleware function:
import { HelloJohn } from "@hellojohn/node";
import type { Request, Response, NextFunction } from "express";
const hj = new HelloJohn({
tenantId: process.env.HELLOJOHN_TENANT_ID!,
secretKey: process.env.HELLOJOHN_SECRET_KEY!,
});
export async function requireAuth(
req: Request,
res: Response,
next: NextFunction
) {
const token = req.headers.authorization?.split(" ")[1];
if (!token) {
return res.status(401).json({ error: "Missing token" });
}
try {
const payload = await hj.verifyToken(token);
req.auth = { userId: payload.sub, sessionId: payload.sid };
next();
} catch {
res.status(401).json({ error: "Invalid token" });
}
}
// Usage
app.get("/api/protected", requireAuth, (req, res) => {
res.json({ userId: req.auth.userId });
});Extracting Claims
HelloJohn tokens include custom claims configured for your tenant:
const payload = await hj.verifyToken(token);
// Standard claims
const userId = payload.sub;
const sessionId = payload.sid;
const expiresAt = new Date(payload.exp * 1000);
// Organization claims
const orgId = payload.org_id;
const orgRole = payload.org_role;
// Custom claims (set in your tenant config)
const userRole = payload.role as string;
const plan = payload.plan as string;Configure custom claims in your tenant settings or via the Admin API.
Without the SDK
Using jose directly:
import * as jose from "jose";
const JWKS = jose.createRemoteJWKSet(
new URL(`https://api.hellojohn.dev/v1/jwks/${process.env.HELLOJOHN_TENANT_ID}`)
);
async function verifyToken(token: string) {
const { payload } = await jose.jwtVerify(token, JWKS, {
issuer: "https://api.hellojohn.dev",
audience: process.env.HELLOJOHN_TENANT_ID,
algorithms: ["EdDSA"],
});
return payload;
}