HelloJohn / docs
SDKsNode.js SDK

Express.js

Integrate HelloJohn authentication into Express.js apps — middleware, protected routes, and request augmentation.

Express.js

Use HelloJohn with Express.js to protect routes, verify tokens, and access user data in request handlers.


Installation

npm install @hellojohn/node

Setup

import express from "express";
import { HelloJohn } from "@hellojohn/node";

const app = express();
const hj = new HelloJohn({
  tenantId: process.env.HELLOJOHN_TENANT_ID!,
  secretKey: process.env.HELLOJOHN_SECRET_KEY!,
});

app.use(express.json());

Authentication Middleware

import type { Request, Response, NextFunction } from "express";

// Extend the Request type
declare global {
  namespace Express {
    interface Request {
      auth?: {
        userId: string;
        sessionId: string;
        orgId: string | null;
        orgRole: string | null;
      };
    }
  }
}

async function requireAuth(req: Request, res: Response, next: NextFunction) {
  const token = req.headers.authorization?.replace("Bearer ", "");

  if (!token) {
    return res.status(401).json({ error: "Missing Authorization header" });
  }

  try {
    const payload = await hj.verifyToken(token);
    req.auth = {
      userId: payload.sub as string,
      sessionId: payload.sid as string,
      orgId: (payload.org_id as string) ?? null,
      orgRole: (payload.org_role as string) ?? null,
    };
    next();
  } catch {
    res.status(401).json({ error: "Invalid or expired token" });
  }
}

Protected Routes

// Public routes
app.get("/api/health", (req, res) => res.json({ ok: true }));

// Protected routes
app.get("/api/me", requireAuth, async (req, res) => {
  const user = await hj.users.get(req.auth!.userId);
  res.json(user);
});

app.post("/api/posts", requireAuth, async (req, res) => {
  const post = await db.post.create({
    data: {
      ...req.body,
      authorId: req.auth!.userId,
    },
  });
  res.status(201).json(post);
});

Role-Based Middleware

function requireRole(role: string) {
  return (req: Request, res: Response, next: NextFunction) => {
    if (!req.auth) {
      return res.status(401).json({ error: "Unauthorized" });
    }
    if (req.auth.orgRole !== role && req.auth.orgRole !== "owner") {
      return res.status(403).json({ error: "Insufficient permissions" });
    }
    next();
  };
}

// Admin-only route
app.delete(
  "/api/users/:id",
  requireAuth,
  requireRole("admin"),
  async (req, res) => {
    await hj.users.delete(req.params.id);
    res.status(204).send();
  }
);

Router-Level Auth

Apply middleware to a group of routes:

import { Router } from "express";

const apiRouter = Router();
apiRouter.use(requireAuth); // All routes in this router require auth

apiRouter.get("/profile", async (req, res) => {
  const user = await hj.users.get(req.auth!.userId);
  res.json(user);
});

apiRouter.get("/orders", async (req, res) => {
  const orders = await db.order.findMany({
    where: { userId: req.auth!.userId },
  });
  res.json(orders);
});

app.use("/api", apiRouter);

Using the Admin SDK

Perform server-side user management:

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

// Update a user
await hj.users.update(userId, { firstName: "Jane" });

// Revoke all sessions
await hj.sessions.revokeAll(userId);

// List org members
const members = await hj.organizations.listMembers(orgId);

Error Handling

app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  if (err.name === "HelloJohnError") {
    return res.status(401).json({ error: err.message });
  }
  console.error(err);
  res.status(500).json({ error: "Internal server error" });
});

On this page