Node.js quickstart
Node 18+, ESM, TypeScript types included. Framework integration: Express.
npm install @latchvector/sso
The SDK wraps every endpoint with typed methods: authentication, and the full management surface (users, organizations, roles, applications, API clients, webhooks, audit, bulk import, data export/erase, MFA & devices). Reach for the API reference only to look up an exact request/response shape.
1. Protect an API
The Express integration (@latchvector/sso/express) guards routes with
middleware — requireAuth, requirePermission, ssoErrorHandler. Tokens are
verified locally; your API does not call the SSO service on every request.
import express from 'express';
import { TokenVerifier } from '@latchvector/sso';
import { requireAuth, requirePermission, ssoErrorHandler } from '@latchvector/sso/express';
// Build once at startup — caches the discovery document and signing keys.
const verifier = new TokenVerifier({
issuer: 'https://sso.yourdomain.com',
audience: 'https://api.yourcompany.com', // your registered identifier
});
const app = express();
app.get('/invoices', requireAuth(verifier), (req, res) => {
res.json({ ownerId: req.principal!.uid });
});
app.post('/invoices/:id/approve',
requireAuth(verifier), requirePermission('invoice.approve'), handler);
app.use(ssoErrorHandler()); // register last
2. Log a user in
import { SsoClient } from '@latchvector/sso';
const sso = new SsoClient({ issuer: 'https://sso.yourdomain.com',
audience: 'https://api.yourcompany.com' });
const result = await sso.login(email, password);
if (result.status === 'authenticated') {
const { accessToken, refreshToken } = result.tokens;
} else if (result.status === 'mfa_required') {
await sso.verifyMfa(result.mfaToken, code);
}
Rotate with sso.refresh(refreshToken) and always persist the new refresh
token it returns. See Sessions & refresh.
3. Manage everything — one client
ManagementClient wraps the entire management surface. Each resource is a
typed group, so the whole product is reachable from code — you rarely write a raw
request.
import { ManagementClient } from '@latchvector/sso';
const mgmt = new ManagementClient({ issuer, token: () => currentAccessToken });
await mgmt.users.create({ organizationId, email, fullName, roleId });
await mgmt.organizations.create({ name, slug, parentId });
await mgmt.roles.create({ organizationId, name, scope: 'SUBTREE', permissionCodes });
await mgmt.applications.create({ organizationId, identifier, name });
await mgmt.clients.register({ name, orgId, applicationId, scopes }); // machine-to-machine
await mgmt.webhooks.register({ organizationId, applicationId, url });
await mgmt.audit.search({ organizationId, q: 'role.revoked' });
await mgmt.import.validate(payload); // bulk migrate
await mgmt.privacy.exportUser(userId); // export / erase
await mgmt.mfa.begin(); // the caller's own MFA
await mgmt.devices.list(); // the caller's devices
await mgmt.sandbox.provision({ /* … */ }); // platform operators
Anything not covered by a typed group — including endpoints added after this SDK
shipped — is reachable via mgmt.request(method, path, { query, body }), with the
same auth, retries, and typed errors.
The npm README has the full recipes (multitenancy with Prisma, go-live checks). The exact request/response shapes live in the API reference.