- 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.
93 lines
3.6 KiB
JavaScript
93 lines
3.6 KiB
JavaScript
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);
|
|
});
|
|
});
|