import test from 'node:test'; import assert from 'node:assert/strict'; import { createApp } from '../server.js'; import { makeJwt, makePbCookie, startServer, stopServer, urlOf, cookieValue, } from './helpers.js'; // Fake YOUreka portal: web-auth session + openfga verify. async function startFakePortal({ cookieStatus = 'Activated', allowed = true, authErrorMessage, firstName = 'Ada' } = {}) { return startServer((req, res) => { let raw = ''; req.on('data', (c) => (raw += c)); req.on('end', () => { const json = (status, body) => { res.writeHead(status, { 'content-type': 'application/json' }); res.end(JSON.stringify(body)); }; if (req.method === 'POST' && req.url === '/api/web/auth') { const body = JSON.parse(raw || '{}'); if (!body.returnUrl) return json(200, { ok: false, message: 'returnUrl is required' }); if (authErrorMessage) { return json(200, { ok: false, message: authErrorMessage }); } return json(200, { ok: true, sessionId: 's1', loginUrl: 'https://portal.example/login?return=1' }); } if (req.method === 'GET' && req.url.startsWith('/api/web/auth/s1/cookie')) { return json(200, { ok: true, sessionId: 's1', status: cookieStatus, cookie: makePbCookie(makeJwt({ expiresInSec: 3600 })), phone: '+2348068287222', firstName, lastName: 'Obi', }); } if (req.method === 'POST' && req.url === '/api/openfga/verify') { return json(200, { ok: true, allowed }); } json(404, { ok: false }); }); }); } // Fake PocketBase: auth-refresh always succeeds with a fresh token. async function startFakePb() { return startServer((req, res) => { res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ token: makeJwt({ expiresInSec: 3600 }), record: { id: 'u1', collectionId: 'c1', collectionName: 'users', phone: '+2348068287222' } })); }); } // Fake a2loft: records requests, echoes a staff profile. async function startFakeA2loft() { const calls = []; const server = await startServer((req, res) => { let raw = ''; req.on('data', (c) => (raw += c)); req.on('end', () => { calls.push({ method: req.method, url: req.url, body: raw ? JSON.parse(raw) : null }); const staff = { ok: true, data: { name: 'Ada Obi', reference: 'UC/0231', domainReference: 'dom-1', domainRankDescription: 'Director', fingerPrintCaptureCount: 0, }, }; res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify(req.url.includes('/fingerprints') ? { ok: true, message: 'saved' } : staff)); }); }); return { server, calls, url: null, stop: () => stopServer(server) }; } async function bootApp(appOverrides = {}, portalOpts) { const portal = await startFakePortal(portalOpts); const pb = await startFakePb(); const a2loft = await startFakeA2loft(); a2loft.url = urlOf(a2loft.server); const app = createApp({ portalUrl: urlOf(portal), pbUrl: urlOf(pb), a2loftBase: a2loft.url, ...appOverrides, }); const httpServer = await new Promise((resolve) => { const s = app.listen(0, '127.0.0.1', () => resolve(s)); }); const base = urlOf(httpServer); const stop = async () => { await new Promise((r) => httpServer.close(r)); await stopServer(portal); await stopServer(pb); await a2loft.stop(); }; return { base, a2loft, stop }; } const manual = { redirect: 'manual' }; async function login(base) { // exercises /auth/login → portal, then /auth/callback → cookies const loginRes = await fetch(`${base}/auth/login`, manual); assert.equal(loginRes.status, 302); const callbackRes = await fetch(`${base}/auth/callback?sessionId=s1`, manual); assert.equal(callbackRes.status, 302); assert.equal(callbackRes.headers.get('location'), '/fingerprint'); const session = cookieValue(callbackRes, 'session'); const profile = cookieValue(callbackRes, 'user_profile'); assert.ok(session, 'session cookie set'); assert.ok(profile, 'profile cookie set'); assert.match(profile, /2348068287222/); return `session=${session}; user_profile=${profile}`; } test('landing page links both tools', async () => { const { base, stop } = await bootApp(); try { const res = await fetch(`${base}/`); const html = await res.text(); assert.equal(res.status, 200); assert.match(html, /href="\/cropper"/); assert.match(html, /href="\/fingerprint"/); } finally { await stop(); } }); test('anonymous /fingerprint redirects to login; session APIs 401', async () => { const { base, stop } = await bootApp(); try { const page = await fetch(`${base}/fingerprint`, manual); assert.equal(page.status, 302); assert.equal(page.headers.get('location'), '/auth/login'); const api = await fetch(`${base}/auth/session`); assert.equal(api.status, 401); } finally { await stop(); } }); test('auth callback rejects unredeemable sessions without setting cookies', async () => { const { base, stop } = await bootApp({}, { cookieStatus: 'TimedOut' }); try { const res = await fetch(`${base}/auth/callback?sessionId=s1`, manual); assert.equal(res.status, 401); assert.equal(cookieValue(res, 'session'), undefined); const html = await res.text(); assert.match(html, /timed out/i); } finally { await stop(); } }); test('session endpoint returns token, profile, agent URL and permitted domains', async () => { const { base, stop } = await bootApp(); try { const cookies = await login(base); const res = await fetch(`${base}/auth/session`, { headers: { cookie: cookies } }); assert.equal(res.status, 200); const body = await res.json(); assert.equal(body.ok, true); assert.match(body.token, /\./); // JWT-ish assert.equal(body.phone, '+2348068287222'); assert.equal(body.firstName, 'Ada'); assert.equal(body.agentUrl, 'http://127.0.0.1:9000'); assert.equal(body.domains.length, 5, 'all catalog domains allowed by the fake portal'); assert.equal(body.domains[0].code, 'uc'); assert.ok(body.domainsCheckedAt > 0); assert.equal(body.domainCacheTtlMs, 5 * 60 * 1000); } finally { await stop(); } }); test('domain refresh refuses while the cache is fresh, succeeds after the TTL', async () => { const { base, stop } = await bootApp({ ttlMs: 30 }); try { const cookies = await login(base); const first = await fetch(`${base}/auth/session`, { headers: { cookie: cookies } }); assert.equal(first.status, 200); // populates the permission cache const fresh = await fetch(`${base}/auth/domains/refresh`, { method: 'POST', headers: { cookie: cookies } }); assert.equal(fresh.status, 409); const body = await fresh.json(); assert.equal(body.ok, false); await new Promise((r) => setTimeout(r, 40)); const stale = await fetch(`${base}/auth/domains/refresh`, { method: 'POST', headers: { cookie: cookies } }); assert.equal(stale.status, 200); const body2 = await stale.json(); assert.equal(body2.ok, true); assert.ok(body2.domainsCheckedAt > 0); } finally { await stop(); } }); test('staff lookup proxy injects the session phone and forwards a2loft responses', async () => { const { base, a2loft, stop } = await bootApp(); try { const cookies = await login(base); const res = await fetch(`${base}/api/uc/staff/UC%2F0231?otp=4455`, { method: 'POST', headers: { cookie: cookies }, }); assert.equal(res.status, 200); const body = await res.json(); assert.equal(body.ok, true); assert.equal(body.data.name, 'Ada Obi'); assert.equal(a2loft.calls.length, 1); const call = a2loft.calls[0]; assert.equal(call.method, 'GET'); const u = new URL(call.url, 'http://x'); assert.equal(decodeURIComponent(u.pathname), '/identity/uc/UC/0231'); assert.equal(u.searchParams.get('phone'), '+2348068287222'); assert.equal(u.searchParams.get('otp'), '4455'); } finally { await stop(); } }); test('fingerprint save proxy forwards the body unchanged', async () => { const { base, a2loft, stop } = await bootApp(); try { const cookies = await login(base); const payload = { fingerprints: [{ finger: 'right-thumb', fingerprint: 'QUJD' }] }; const res = await fetch(`${base}/api/uc/staff/UC0231/fingerprints?otp=99`, { method: 'POST', headers: { cookie: cookies, 'content-type': 'application/json' }, body: JSON.stringify(payload), }); assert.equal(res.status, 200); assert.deepEqual(await res.json(), { ok: true, message: 'saved' }); const call = a2loft.calls[0]; assert.equal(call.method, 'POST'); assert.ok(call.url.startsWith('/identity/uc/fingerprints/UC0231?'), `save URL shape, got: ${call.url}`); assert.deepEqual(call.body, payload); } finally { await stop(); } }); test('proxy validation: auth, otp, and domain are enforced before contacting a2loft', async () => { const { base, a2loft, stop } = await bootApp(); try { const cookies = await login(base); const anon = await fetch(`${base}/api/uc/staff/REF?otp=1`, { method: 'POST' }); assert.equal(anon.status, 401); const noOtp = await fetch(`${base}/api/uc/staff/REF`, { method: 'POST', headers: { cookie: cookies }, }); assert.equal(noOtp.status, 400); const badDomain = await fetch(`${base}/api/nope/staff/REF?otp=1`, { method: 'POST', headers: { cookie: cookies }, }); assert.equal(badDomain.status, 400); assert.equal(a2loft.calls.length, 0, 'a2loft never contacted for rejected calls'); } finally { await stop(); } }); test('upstream error text is HTML-escaped on the error page', async () => { const { base, stop } = await bootApp({}, { authErrorMessage: 'portal said no' }); try { const res = await fetch(`${base}/auth/login`, manual); assert.equal(res.status, 502); const html = await res.text(); assert.ok(!html.includes(''), 'script tag must not be interpolated raw'); assert.match(html, /<script>alert\(1\)<\/script>/); } finally { await stop(); } }); test('pages receive the session injected server-side (no client fetch, no-store)', async () => { const { base, stop } = await bootApp(); try { const cookies = await login(base); const res = await fetch(`${base}/fingerprint`, { headers: { cookie: cookies } }); assert.equal(res.status, 200); assert.match(res.headers.get('cache-control') || '', /no-store/); const html = await res.text(); assert.match(html, /window\.__SESSION__ = \{/); assert.match(html, /2348068287222/); assert.match(html, /"domains"/, 'permitted domains injected too'); } finally { await stop(); } }); test('injected session values cannot break out of the script tag', async () => { const { base, stop } = await bootApp({}, { firstName: 'Ada' }); try { const cookies = await login(base); const html = await (await fetch(`${base}/fingerprint`, { headers: { cookie: cookies } })).text(); const injected = html.slice(html.indexOf('window.__SESSION__'), html.indexOf('')); const payload = injected.slice(injected.indexOf('= ') + 2, injected.indexOf(';')); assert.ok(!payload.includes(''), 'raw must not appear inside the injected JSON'); assert.match(payload, /\\u003c/, 'markup is escaped in the payload'); } finally { await stop(); } }); test('landing header is server-rendered for the session', async () => { const { base, stop } = await bootApp(); try { const anon = await (await fetch(`${base}/`)).text(); assert.match(anon, /href="\/auth\/login">Sign in'), 'header marker is replaced'); // tools live in the header nav and in the mobile side nav assert.match(anon, /cds-header-nav-item href="\/cropper"/); assert.match(anon, /cds-header-nav-item href="\/fingerprint"/); assert.match(anon, /cds-side-nav-link href="\/cropper"/); assert.match(anon, /cds-side-nav-divider/); assert.match(anon, /cds-side-nav-link href="\/auth\/logout"/); assert.match(anon, /youreka-logo\.png/); assert.match(anon, /header-menu-button/); const cookies = await login(base); const authed = await (await fetch(`${base}/`, { headers: { cookie: cookies } })).text(); assert.match(authed, /Ada/); assert.match(authed, /href="\/auth\/logout">Sign out