Bind each customer to a Verae userId for hop tracing
Some checks are pending
offline / test (push) Waiting to run

Signup registers/binds a Verae central user and stores veraeUserId. Public access stays the zappier API key. Chain JWTs stay server-side behind tokenRef. Authz, billing, and jobs.watch carry veraeUserId.
This commit is contained in:
George Lambert 2026-09-11 16:18:06 -04:00
parent 1b199ca4d4
commit 345aeeead9
79 changed files with 703 additions and 95 deletions

View file

@ -7,6 +7,7 @@ import { createHash, randomUUID } from 'node:crypto';
import { config } from '../config.js';
import { AppError } from '../errors.js';
import { createDebugger } from '../debug/logger.js';
import { stableVeraeUserId } from '../lib/identity.js';
const log = createDebugger('http');
@ -39,7 +40,7 @@ async function mockLogin({ username, password }) {
token: `mock-jwt-${username}`,
expiresAt,
user: {
id: randomUUID(),
id: stableVeraeUserId(username),
username,
role: username.includes('admin') ? 'admin' : 'user',
},
@ -53,13 +54,24 @@ async function mockValidate(token) {
const username = token.replace('mock-jwt-', '');
return {
valid: true,
userId: randomUUID(),
userId: stableVeraeUserId(username),
username,
role: 'user',
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
};
}
async function mockCreateUser({ username, password, role = 'user' }) {
if (!username || !password) {
throw new AppError('Invalid input', { status: 400, code: 'VALIDATION_ERROR' });
}
return {
id: stableVeraeUserId(username),
username,
role,
};
}
async function mockCreateTimestamp({ data, hashAlg, sha256, publicMetadata, privateMetadata }) {
if (!data && !sha256) {
throw new AppError('Invalid input data', { status: 400, code: 'VALIDATION_ERROR' });
@ -228,6 +240,28 @@ export const veraeClient = {
return request('/auth/validate', { token });
},
/**
* Admin-only Verae user register. JWT is not returned to callers of bind.
* @param {string} adminToken
* @param {{ username: string, password: string, role?: string }} body
*/
async createUser(adminToken, body) {
if (config.mockVerae) return mockCreateUser(body);
return request('/auth/users', { method: 'POST', token: adminToken, body });
},
/**
* Login (or create via admin) and return the stable user id never the JWT.
* @param {{ username: string, password: string }} credentials
*/
async bindUser(credentials) {
const login = await this.login(credentials);
return {
veraeUserId: login.user?.id || stableVeraeUserId(credentials.username),
veraeUsername: login.user?.username || credentials.username,
};
},
/**
* @param {string} token
* @param {{ data: string, hashAlg?: string }} body