feat: YOUreka Tools express app — landing page + fpagent enrollment wizard
- tools/: express host (hardcoded config, YOUreka Auth cookie session, OpenFGA domain filtering with 5-min cache, a2loft proxies) - /fingerprint wizard: domain picker, two-finger capture via fpagent Socket.IO, OTP-validated lookup/save, domain refresh control - node --test suite (27 tests, fake portal/PocketBase/a2loft upstreams) - Dockerfile (node:22-alpine, non-root, healthcheck) - README rewritten; node_modules gitignored Field-verified end to end: portal login, permitted-domain filtering, real U.are.U 4500 captures, cancel-preserves-capture, a2loft save.
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
// Shared test helpers: unsigned JWTs, real PocketBase cookie envelopes, and
|
||||
// throwaway http servers for fake upstreams.
|
||||
import http from 'node:http';
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
export function b64url(obj) {
|
||||
return Buffer.from(JSON.stringify(obj)).toString('base64url');
|
||||
}
|
||||
|
||||
/** Structurally valid (unsigned) JWT with a controllable exp. */
|
||||
export function makeJwt({ expiresInSec = 3600 } = {}) {
|
||||
const payload = {
|
||||
exp: Math.floor(Date.now() / 1000) + expiresInSec,
|
||||
id: 'u1',
|
||||
collectionId: 'c1',
|
||||
collectionName: 'users',
|
||||
};
|
||||
return `${b64url({ alg: 'HS256', typ: 'JWT' })}.${b64url(payload)}.sig`;
|
||||
}
|
||||
|
||||
export const TEST_RECORD = {
|
||||
id: 'u1',
|
||||
collectionId: 'c1',
|
||||
collectionName: 'users',
|
||||
phone: '+2348068287222',
|
||||
};
|
||||
|
||||
/** Build the real PocketBase cookie envelope (value only) via the actual SDK. */
|
||||
export function makePbCookie(token, record = TEST_RECORD) {
|
||||
const pb = new PocketBase('http://127.0.0.1:9'); // never contacted for this
|
||||
pb.authStore.save(token, record);
|
||||
const setCookie = pb.authStore.exportToCookie({}, 'pb_auth');
|
||||
const m = /pb_auth=([^;]+)/.exec(setCookie);
|
||||
if (!m) throw new Error('could not build pb cookie envelope');
|
||||
return m[1];
|
||||
}
|
||||
|
||||
export function startServer(handler) {
|
||||
return new Promise((resolve) => {
|
||||
const server = http.createServer(handler);
|
||||
server.listen(0, '127.0.0.1', () => resolve(server));
|
||||
});
|
||||
}
|
||||
|
||||
export const urlOf = (server) => `http://127.0.0.1:${server.address().port}`;
|
||||
|
||||
export function stopServer(server) {
|
||||
return new Promise((resolve) => server.close(resolve));
|
||||
}
|
||||
|
||||
/** Read the `name=value` pair out of a Set-Cookie response header list. */
|
||||
export function cookieValue(res, name) {
|
||||
for (const c of res.headers.getSetCookie()) {
|
||||
const m = new RegExp(`(?:^|;\\s*)${name}=([^;]*)`).exec(c);
|
||||
if (m) return m[1];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Scriptable fetch double that records every call. */
|
||||
export function fakeFetch(script) {
|
||||
const calls = [];
|
||||
const fn = async (url, init) => {
|
||||
const body = init?.body ? JSON.parse(init.body) : undefined;
|
||||
const out = await script({ url, body, init });
|
||||
calls.push({ url, body });
|
||||
if (out instanceof Error) throw out;
|
||||
return { status: out.status ?? 200, json: async () => out.body, text: async () => JSON.stringify(out.body ?? {}) };
|
||||
};
|
||||
fn.calls = calls;
|
||||
return fn;
|
||||
}
|
||||
Reference in New Issue
Block a user