import { createServer } from 'node:http'; import { randomBytes } from 'node:crypto'; import { initializeApp, getApps } from 'firebase-admin/app'; import { getAuth } from 'firebase-admin/auth'; const projectId = process.env.FIREBASE_PROJECT_ID; const authEmulatorHost = process.env.FIREBASE_AUTH_EMULATOR_HOST; const port = Number(process.env.QA_AUTH_BACKEND_PORT ?? 8189); if (!projectId?.startsWith('demo-') || !authEmulatorHost?.startsWith('127.0.0.1:')) { throw new Error('QA auth backend requires a demo project and loopback Auth emulator.'); } const app = getApps()[0] ?? initializeApp({ projectId }); const auth = getAuth(app); const sessions = new Set(); function json(response, status, body) { response.writeHead(status, { 'content-type': 'application/json' }); response.end(JSON.stringify(body)); } const server = createServer(async (request, response) => { const url = new URL(request.url ?? '/', 'http://127.0.0.1'); if (url.pathname === '/api/v1/auth/firebase/login' && request.method === 'POST') { let payload; try { payload = JSON.parse(await new Promise((resolve, reject) => { let body = ''; request.on('data', (chunk) => { body += chunk; if (body.length > 100_000) reject(new Error('too large')); }); request.on('end', () => resolve(body)); request.on('error', reject); })); } catch { console.log(JSON.stringify({ endpoint: url.pathname, status: 400, reason: 'invalid-json' })); return json(response, 400, { detail: 'Invalid request body.' }); } try { const decoded = await auth.verifyIdToken(payload?.id_token); if (decoded.aud !== projectId || decoded.iss !== `https://securetoken.google.com/${projectId}`) { throw new Error('Project claims mismatch.'); } const accessToken = `qa-session-${randomBytes(24).toString('hex')}`; const refreshToken = `qa-refresh-${randomBytes(24).toString('hex')}`; sessions.add(accessToken); sessions.add(refreshToken); console.log(JSON.stringify({ endpoint: url.pathname, status: 200, verified: true, projectId, uidPrefix: decoded.uid.slice(0, 8) })); return json(response, 200, { access_token: accessToken, refresh_token: refreshToken }); } catch { console.log(JSON.stringify({ endpoint: url.pathname, status: 401, verified: false, projectId })); return json(response, 401, { detail: 'Firebase token verification failed.' }); } } if (url.pathname === '/api/v1/auth/login' && request.method === 'POST') { console.log(JSON.stringify({ endpoint: url.pathname, status: 401, reason: 'email-auth-not-configured-in-local-fixture' })); return json(response, 401, { detail: 'Email authentication is not configured in this local QA fixture.' }); } if (url.pathname === '/api/v1/auth/sessions' && request.method === 'GET') { const token = request.headers.authorization?.replace(/^Bearer\s+/i, ''); const ok = Boolean(token && sessions.has(token)); console.log(JSON.stringify({ endpoint: url.pathname, status: ok ? 200 : 401, cookieBearerPresent: Boolean(token), sessionMatched: ok })); return ok ? json(response, 200, [{ id: 'qa-current-session', device_name: 'Isolated local browser', current: true, created_at: new Date().toISOString() }]) : json(response, 401, { detail: 'Authentication required.' }); } if (url.pathname.startsWith('/api/v1/auth/') && ['POST', 'DELETE'].includes(request.method ?? '')) { const token = request.headers.authorization?.replace(/^Bearer\s+/i, ''); const ok = Boolean(token && sessions.has(token)); console.log(JSON.stringify({ endpoint: url.pathname, status: ok ? 204 : 401, cookieBearerPresent: Boolean(token), sessionMatched: ok })); response.writeHead(ok ? 204 : 401); return response.end(); } console.log(JSON.stringify({ endpoint: url.pathname, status: 404 })); return json(response, 404, { detail: 'Local QA endpoint not found.' }); }); server.listen(port, '127.0.0.1', () => console.log(JSON.stringify({ listening: `127.0.0.1:${port}`, projectId, authEmulatorHost })));