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,92 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import PocketBase from 'pocketbase';
|
||||
import { loadYrkaSession, extractPbCookieValue, tokenExpiresAtMs } from '../yrka-auth.js';
|
||||
import { makeJwt, makePbCookie, startServer, stopServer, urlOf, TEST_RECORD } from './helpers.js';
|
||||
|
||||
test('extractPbCookieValue handles prefixed and bare values', () => {
|
||||
assert.equal(extractPbCookieValue('pb_auth=abc123; Path=/; HttpOnly'), 'abc123');
|
||||
assert.equal(extractPbCookieValue('bare-value'), 'bare-value');
|
||||
});
|
||||
|
||||
test('tokenExpiresAtMs reads exp from a JWT payload', () => {
|
||||
const expSec = Math.floor(Date.now() / 1000) + 60;
|
||||
const token = makeJwt({ expiresInSec: 60 });
|
||||
assert.equal(tokenExpiresAtMs(token), expSec * 1000);
|
||||
assert.equal(tokenExpiresAtMs('garbage'), 0);
|
||||
assert.equal(tokenExpiresAtMs(''), 0);
|
||||
});
|
||||
|
||||
test('loadYrkaSession returns null without a cookie', async () => {
|
||||
assert.equal(await loadYrkaSession(''), null);
|
||||
assert.equal(await loadYrkaSession(undefined), null);
|
||||
});
|
||||
|
||||
test('loadYrkaSession returns null on malformed cookie values', async () => {
|
||||
let constructed = false;
|
||||
const pb = await loadYrkaSession('not-a-valid-envelope', {
|
||||
pbUrl: 'http://127.0.0.1:9',
|
||||
createPb: (url) => {
|
||||
constructed = true;
|
||||
return new PocketBase(url);
|
||||
},
|
||||
});
|
||||
assert.equal(pb, null);
|
||||
assert.equal(constructed, true, 'a PocketBase client was constructed before parsing failed');
|
||||
});
|
||||
|
||||
// The remaining tests exercise the real SDK against a fake PocketBase server,
|
||||
// so the cookie envelope and auth-refresh handling match production exactly.
|
||||
|
||||
async function withFakePb(refreshStatus, fn) {
|
||||
const server = await startServer((req, res) => {
|
||||
if (req.method === 'POST' && req.url.startsWith('/api/collections/users/auth-refresh')) {
|
||||
res.writeHead(refreshStatus, { 'content-type': 'application/json' });
|
||||
if (refreshStatus === 200) {
|
||||
res.end(JSON.stringify({ token: makeJwt({ expiresInSec: 3600 }), record: TEST_RECORD }));
|
||||
} else {
|
||||
res.end(JSON.stringify({ code: 401, message: 'The request is not authenticated.' }));
|
||||
}
|
||||
return;
|
||||
}
|
||||
res.writeHead(404, { 'content-type': 'application/json' });
|
||||
res.end('{}');
|
||||
});
|
||||
try {
|
||||
await fn(urlOf(server));
|
||||
} finally {
|
||||
await stopServer(server);
|
||||
}
|
||||
}
|
||||
|
||||
test('loadYrkaSession accepts a valid, not-nearly-expired token without refreshing', async () => {
|
||||
await withFakePb(200, async (pbUrl) => {
|
||||
const cookie = makePbCookie(makeJwt({ expiresInSec: 3600 }));
|
||||
const yrka = await loadYrkaSession(cookie, { pbUrl });
|
||||
assert.ok(yrka);
|
||||
assert.equal(yrka.refreshed, false);
|
||||
assert.ok(yrka.pb.authStore.token);
|
||||
assert.equal(yrka.pb.authStore.record.id, TEST_RECORD.id);
|
||||
assert.equal(yrka.cookie, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
test('loadYrkaSession refreshes proactively when expiry is within the buffer', async () => {
|
||||
await withFakePb(200, async (pbUrl) => {
|
||||
const oldToken = makeJwt({ expiresInSec: 60 }); // inside the 5-minute buffer
|
||||
const cookie = makePbCookie(oldToken);
|
||||
const yrka = await loadYrkaSession(cookie, { pbUrl });
|
||||
assert.ok(yrka, 'session recovered');
|
||||
assert.equal(yrka.refreshed, true);
|
||||
assert.notEqual(yrka.pb.authStore.token, oldToken);
|
||||
assert.ok(yrka.cookie, 'refreshed cookie value is returned for persistence');
|
||||
});
|
||||
});
|
||||
|
||||
test('loadYrkaSession returns null when the token is expired and refresh fails', async () => {
|
||||
await withFakePb(401, async (pbUrl) => {
|
||||
const cookie = makePbCookie(makeJwt({ expiresInSec: -3600 }));
|
||||
const yrka = await loadYrkaSession(cookie, { pbUrl });
|
||||
assert.equal(yrka, null);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user