Auth for Node.js
Token verification, Express middleware, and management API client for backend applications.
Installation
npm install @usetransactional/auth-nodepnpm add @usetransactional/auth-nodeyarn add @usetransactional/auth-nodeQuick Start
import { verifyToken } from "@usetransactional/auth-node";
const payload = await verifyToken(token, "auth.yourdomain.com");
console.log("Authenticated user:", payload.sub);Token Verification
The SDK provides several utilities for working with JWTs issued by Transactional Auth.
verifyToken
Verifies a JWT against your domain's JWKS endpoint and returns the decoded payload. Throws if the token is invalid, expired, or the signature does not match.
import { verifyToken } from "@usetransactional/auth-node";
const payload = await verifyToken(token, "auth.yourdomain.com", {
audience: "https://api.yourdomain.com",
issuer: "https://auth.yourdomain.com/", // defaults to domain if omitted
});
console.log(payload.sub); // user ID
console.log(payload.email); // user email
console.log(payload.permissions); // assigned permissionsParameters:
token— the raw JWT stringdomain— your Transactional Auth domain (used to fetch JWKS)options(optional):audience— expected audience claimissuer— expected issuer claim (defaults to the domain)
decodeToken
Decodes a JWT without verifying its signature. Use this only for inspection purposes — never trust the output for authorization decisions.
import { decodeToken } from "@usetransactional/auth-node";
const decoded = decodeToken(token);
console.log(decoded.header); // { alg: 'RS256', typ: 'JWT', kid: '...' }
console.log(decoded.payload); // { sub: '...', email: '...', ... }isTokenExpired
Returns true if the token's exp claim is in the past.
import { isTokenExpired } from "@usetransactional/auth-node";
if (isTokenExpired(token)) {
console.log("Token has expired, request a new one");
}clearJwksCache
The SDK caches JWKS keys in memory to avoid repeated network requests. Call this to force a refresh on the next verification.
import { clearJwksCache } from "@usetransactional/auth-node";
clearJwksCache();Full Example
import {
verifyToken,
decodeToken,
isTokenExpired,
clearJwksCache,
} from "@usetransactional/auth-node";
const domain = "auth.yourdomain.com";
// Quick expiry check before making a network call
if (isTokenExpired(token)) {
throw new Error("Token expired");
}
// Inspect claims without verification
const { payload: inspected } = decodeToken(token);
console.log("Token issued at:", new Date(inspected.iat * 1000));
// Full cryptographic verification
try {
const payload = await verifyToken(token, domain, {
audience: "https://api.yourdomain.com",
});
console.log("User ID:", payload.sub);
console.log("Email:", payload.email);
} catch (err) {
console.error("Token verification failed:", err.message);
clearJwksCache(); // clear cache if keys may have rotated
}Express Middleware
createAuthMiddleware
Creates an Express middleware that verifies the bearer token on every request and attaches the decoded payload to req.auth.
import { createAuthMiddleware } from "@usetransactional/auth-node";
const auth = createAuthMiddleware({
domain: "auth.yourdomain.com", // required
audience: "https://api.yourdomain.com",
algorithms: ["RS256"], // default: ['RS256']
credentialsRequired: true, // default: true — rejects requests without a token
getToken: (req) => {
// optional custom token extractor
return req.headers["x-custom-token"] as string;
},
});Options:
domain(required) — your Transactional Auth domainaudience— expected audience claimalgorithms— accepted signing algorithms (default:['RS256'])credentialsRequired— iftrue, requests without a token are rejected with 401 (default:true)getToken— custom function to extract the token from the request. By default, the middleware reads theAuthorization: Bearer <token>header.
Full Express App Example
import express from "express";
import { createAuthMiddleware } from "@usetransactional/auth-node";
const app = express();
app.use(express.json());
const auth = createAuthMiddleware({
domain: "auth.yourdomain.com",
audience: "https://api.yourdomain.com",
});
// Protect all /api routes
app.use("/api", auth);
app.get("/api/profile", (req, res) => {
// req.auth contains the decoded token payload
res.json({
userId: req.auth.sub,
email: req.auth.email,
permissions: req.auth.permissions,
});
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});Permission Guards
Use these middleware functions after createAuthMiddleware to enforce fine-grained access control on individual routes.
requirePermissions
Checks that the authenticated user has all of the specified permissions.
import {
createAuthMiddleware,
requirePermissions,
} from "@usetransactional/auth-node";
const auth = createAuthMiddleware({ domain: "auth.yourdomain.com" });
app.delete(
"/api/users/:id",
auth,
requirePermissions("users:delete"),
(req, res) => {
res.json({ message: "User deleted" });
}
);
app.post(
"/api/admin/settings",
auth,
requirePermissions("admin:read", "admin:write"),
(req, res) => {
res.json({ message: "Settings updated" });
}
);requireRoles
Checks that the authenticated user has any of the specified roles.
import {
createAuthMiddleware,
requireRoles,
} from "@usetransactional/auth-node";
const auth = createAuthMiddleware({ domain: "auth.yourdomain.com" });
app.get("/api/admin/dashboard", auth, requireRoles("admin", "owner"), (req, res) => {
res.json({ message: "Welcome to the admin dashboard" });
});requireScopes
Checks that the token includes all of the specified OAuth scopes.
import {
createAuthMiddleware,
requireScopes,
} from "@usetransactional/auth-node";
const auth = createAuthMiddleware({ domain: "auth.yourdomain.com" });
app.get(
"/api/emails",
auth,
requireScopes("read:emails"),
(req, res) => {
res.json({ emails: [] });
}
);optionalAuth
Works like createAuthMiddleware but does not reject requests without a token. If a valid token is present, req.auth is populated; otherwise req.auth is undefined.
import { optionalAuth } from "@usetransactional/auth-node";
const maybeAuth = optionalAuth({
domain: "auth.yourdomain.com",
audience: "https://api.yourdomain.com",
});
app.get("/api/articles", maybeAuth, (req, res) => {
if (req.auth) {
// Authenticated — return personalized content
res.json({ articles: getArticlesForUser(req.auth.sub) });
} else {
// Anonymous — return public content
res.json({ articles: getPublicArticles() });
}
});Management API Client
The TransactionalAuthClient provides a typed client for managing users, applications, connections, and roles programmatically.
import { TransactionalAuthClient } from "@usetransactional/auth-node";
const client = new TransactionalAuthClient({
domain: "auth.yourdomain.com",
clientId: "your-client-id",
clientSecret: "your-client-secret",
});Users
// List users with pagination and filtering
const { data: users, total } = await client.getUsers({
page: 1,
perPage: 25,
search: "jane",
});
// Get a single user by ID
const user = await client.getUser("user_abc123");
// Get a user by email
const user = await client.getUserByEmail("jane@example.com");
// Create a new user
const newUser = await client.createUser({
email: "jane@example.com",
name: "Jane Doe",
password: "securepassword123",
emailVerified: false,
});
// Update a user
const updated = await client.updateUser("user_abc123", {
name: "Jane Smith",
});
// Delete a user
await client.deleteUser("user_abc123");
// Block and unblock
await client.blockUser("user_abc123");
await client.unblockUser("user_abc123");
// Send verification email
await client.sendVerificationEmail("user_abc123");
// Change password
await client.changePassword("user_abc123", "newSecurePassword456");Applications
// List all applications
const apps = await client.getApplications();
// Get a single application
const app = await client.getApplication("app_xyz789");Connections
// List all connections (identity providers)
const connections = await client.getConnections();
// Get a single connection
const connection = await client.getConnection("con_abc123");Roles
// List all roles
const roles = await client.getRoles();
// Assign a role to a user
await client.assignRoleToUser("user_abc123", "role_admin");
// Remove a role from a user
await client.removeRoleFromUser("user_abc123", "role_admin");
// Get all roles for a user
const userRoles = await client.getUserRoles("user_abc123");Full Management Example
import { TransactionalAuthClient } from "@usetransactional/auth-node";
const client = new TransactionalAuthClient({
domain: "auth.yourdomain.com",
clientId: process.env.AUTH_CLIENT_ID!,
clientSecret: process.env.AUTH_CLIENT_SECRET!,
});
async function setupNewUser(email: string, name: string) {
// Create the user
const user = await client.createUser({
email,
name,
password: crypto.randomUUID(), // temporary password
emailVerified: false,
});
// Assign the default role
const roles = await client.getRoles();
const memberRole = roles.find((r) => r.name === "member");
if (memberRole) {
await client.assignRoleToUser(user.id, memberRole.id);
}
// Send verification email
await client.sendVerificationEmail(user.id);
console.log(`Created user ${user.id} and sent verification email`);
return user;
}Complete Express Example
import express from "express";
import {
createAuthMiddleware,
optionalAuth,
requirePermissions,
requireRoles,
TransactionalAuthClient,
} from "@usetransactional/auth-node";
const app = express();
app.use(express.json());
const AUTH_DOMAIN = "auth.yourdomain.com";
const AUDIENCE = "https://api.yourdomain.com";
// Auth middleware instances
const auth = createAuthMiddleware({ domain: AUTH_DOMAIN, audience: AUDIENCE });
const maybeAuth = optionalAuth({ domain: AUTH_DOMAIN, audience: AUDIENCE });
// Management client
const mgmt = new TransactionalAuthClient({
domain: AUTH_DOMAIN,
clientId: process.env.AUTH_CLIENT_ID!,
clientSecret: process.env.AUTH_CLIENT_SECRET!,
});
// --- Public route with optional auth ---
app.get("/api/articles", maybeAuth, (req, res) => {
if (req.auth) {
res.json({ message: `Welcome back, ${req.auth.email}` });
} else {
res.json({ message: "Welcome, guest" });
}
});
// --- Protected route ---
app.get("/api/profile", auth, (req, res) => {
res.json({
userId: req.auth.sub,
email: req.auth.email,
});
});
// --- Permission-gated route ---
app.delete(
"/api/users/:id",
auth,
requirePermissions("users:delete"),
async (req, res) => {
await mgmt.deleteUser(req.params.id);
res.json({ message: "User deleted" });
}
);
// --- Role-gated route ---
app.get(
"/api/admin/users",
auth,
requireRoles("admin", "owner"),
async (req, res) => {
const { data: users } = await mgmt.getUsers({ page: 1, perPage: 50 });
res.json({ users });
}
);
// --- Error handler ---
app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
if (err.name === "UnauthorizedError") {
return res.status(401).json({ error: "Invalid or missing token" });
}
if (err.name === "ForbiddenError") {
return res.status(403).json({ error: "Insufficient permissions" });
}
res.status(500).json({ error: "Internal server error" });
});
app.listen(3000, () => {
console.log("API server running on port 3000");
});TypeScript Types
The SDK exports the following types for use in your application:
import type {
DecodedToken,
User,
CreateUserData,
UpdateUserData,
ListUsersParams,
PaginatedResponse,
Application,
Connection,
Role,
} from "@usetransactional/auth-node";DecodedToken
interface DecodedToken {
sub: string; // User ID
email: string; // User email
name?: string; // Display name
picture?: string; // Avatar URL
email_verified: boolean;
permissions?: string[];
roles?: string[];
scope?: string;
org_id?: string; // Organization ID
org_name?: string; // Organization name
org_role?: string; // Organization role
iss: string; // Issuer
aud: string | string[];// Audience
iat: number; // Issued at (unix timestamp)
exp: number; // Expiration (unix timestamp)
}User
interface User {
id: string;
email: string;
name: string | null;
picture: string | null;
emailVerified: boolean;
blocked: boolean;
createdAt: string;
updatedAt: string;
lastLogin: string | null;
}CreateUserData
interface CreateUserData {
email: string;
name?: string;
password: string;
emailVerified?: boolean;
blocked?: boolean;
}UpdateUserData
interface UpdateUserData {
email?: string;
name?: string;
picture?: string;
emailVerified?: boolean;
blocked?: boolean;
}ListUsersParams
interface ListUsersParams {
page?: number;
perPage?: number;
search?: string;
sort?: string;
order?: "asc" | "desc";
}PaginatedResponse
interface PaginatedResponse<T> {
data: T[];
total: number;
page: number;
perPage: number;
totalPages: number;
}Application
interface Application {
id: string;
name: string;
clientId: string;
type: "spa" | "regular_web" | "machine_to_machine";
callbacks: string[];
allowedOrigins: string[];
createdAt: string;
}Connection
interface Connection {
id: string;
name: string;
strategy: string;
enabled: boolean;
createdAt: string;
}Role
interface Role {
id: string;
name: string;
description: string | null;
permissions: string[];
createdAt: string;
}On This Page
- Installation
- Quick Start
- Token Verification
- verifyToken
- decodeToken
- isTokenExpired
- clearJwksCache
- Full Example
- Express Middleware
- createAuthMiddleware
- Full Express App Example
- Permission Guards
- requirePermissions
- requireRoles
- requireScopes
- optionalAuth
- Management API Client
- Users
- Applications
- Connections
- Roles
- Full Management Example
- Complete Express Example
- TypeScript Types
- DecodedToken
- User
- CreateUserData
- UpdateUserData
- ListUsersParams
- PaginatedResponse
- Application
- Connection
- Role