@bongos/core 1.24.95 → 1.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +97 -57
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +6 -0
- package/clients/bongos-client/index.cjs +6 -0
- package/clients/bongos-client/index.d.ts +9 -0
- package/clients/bongos-client/index.mjs +6 -0
- package/docs/api/openapi.json +179 -3
- package/docs/api-reference.md +6 -3
- package/docs/architecture.md +3 -1
- package/docs/copy-inventory.md +7 -7
- package/docs/copy-registry.json +7 -7
- package/docs/module-api-changelog.md +6 -0
- package/docs/page-inventory.json +2 -0
- package/docs/page-readings.json +6 -6
- package/migrations/core_298_card_step_up.sql +16 -0
- package/modules/autonomy/db.js +48 -0
- package/modules/autonomy/routes/autonomy.js +15 -0
- package/modules/economy/module.json +1 -1
- package/modules/economy/processor-stripe.js +292 -0
- package/modules/economy/routes/balances.js +4 -3
- package/modules/economy/routes/project-account.js +12 -1
- package/modules/economy/tests/processor_stripe.mjs +135 -0
- package/modules/government/catalog.js +1 -1
- package/modules/government/routes/government.js +2 -2
- package/modules/hall-ui/public/home-city-draw.js +24 -2
- package/modules/hall-ui/public/home-city.css +15 -0
- package/modules/hall-ui/public/home-city.js +3 -1
- package/modules/hall-ui/public/home-runner.js +248 -0
- package/modules/hall-ui/public/index.html +8 -5
- package/modules/hall-ui/public/visor.css +27 -0
- package/modules/hall-ui/public/visor.js +65 -2
- package/modules/hall-ui/records/home-runner.md +19 -0
- package/modules/hall-ui/records/visor.md +4 -0
- package/modules/render-deploy/routes/act.js +1 -1
- package/modules/render-deploy/routes/app.js +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +17 -0
- package/scripts/bongos/fitness.js +1 -1
- package/scripts/bongos/government-walk.js +2 -2
- package/scripts/hall-preview/server.js +4 -0
- package/src/bongos/passkeys.js +30 -0
- package/src/bongos/routes/auth.js +2 -2
- package/src/bongos/routes/builders.js +2 -1
- package/src/bongos/routes/module-payouts.js +4 -2
- package/src/bongos/routes/my-cash-outs.js +5 -3
- package/src/bongos/routes/my-passkeys.js +70 -0
- package/src/bongos/routes/payout-approvals.js +5 -3
- package/src/bongos/step-up.js +42 -0
- package/src/module-api.js +5 -1
- package/tests/autonomy_runner_goal_stats.mjs +92 -0
- package/tests/government_act_alone_buildings.mjs +2 -1
- package/tests/government_treasury.mjs +2 -1
- package/tests/hall_visor.mjs +20 -0
- package/tests/home_runner.mjs +115 -0
- package/tests/module_api.mjs +1 -0
- package/tests/my_passkeys.mjs +47 -1
- package/tests/rank_change_broadcast_port.mjs +2 -0
package/tests/my_passkeys.mjs
CHANGED
|
@@ -14,7 +14,12 @@ const build = require('../src/bongos/routes/my-passkeys');
|
|
|
14
14
|
|
|
15
15
|
const creds = [];
|
|
16
16
|
const consumed = [];
|
|
17
|
+
const stamped = [];
|
|
18
|
+
let counterOk = true;
|
|
17
19
|
const store = {
|
|
20
|
+
STEP_UP_WINDOW_SECONDS: 600,
|
|
21
|
+
recordUse: async () => (counterOk ? { ok: true } : { ok: false, reason: 'sign_count_regressed' }),
|
|
22
|
+
markSessionCardVerified: async (sid, b) => { stamped.push([sid, b]); return true; },
|
|
18
23
|
listCredentials: async (b) => creds.filter((c) => c.builder_id === b),
|
|
19
24
|
getOrCreateUserHandle: async () => 'aGFuZGxl',
|
|
20
25
|
createChallenge: async () => ({ challenge: 'Y2hhbGw' }),
|
|
@@ -26,6 +31,12 @@ const store = {
|
|
|
26
31
|
};
|
|
27
32
|
let verifyOk = true;
|
|
28
33
|
const webauthn = {
|
|
34
|
+
generateAuthenticationOptions: async (o) => ({ rpId: o.rpID, allowCredentials: o.allowCredentials, userVerification: o.userVerification }),
|
|
35
|
+
verifyAuthenticationResponse: async (o) => {
|
|
36
|
+
assert.equal(o.requireUserVerification, true);
|
|
37
|
+
if (!(await o.expectedChallenge('Y2hhbGw'))) throw new Error('challenge');
|
|
38
|
+
return { verified: true, authenticationInfo: { newCounter: 5 } };
|
|
39
|
+
},
|
|
29
40
|
generateRegistrationOptions: async (o) => ({ rp: { id: o.rpID }, excludeCredentials: o.excludeCredentials }),
|
|
30
41
|
verifyRegistrationResponse: async (o) => {
|
|
31
42
|
assert.equal(o.requireUserVerification, true);
|
|
@@ -37,7 +48,7 @@ const webauthn = {
|
|
|
37
48
|
const app = express();
|
|
38
49
|
app.use(express.json());
|
|
39
50
|
app.use((req, res, next) => { res.fail = (code, { status = 400, message } = {}) => res.status(status).json({ error: code, message }); next(); });
|
|
40
|
-
app.use((req, res, next) => { req.builder = { id: '7', github_login: 'me' }; next(); });
|
|
51
|
+
app.use((req, res, next) => { req.builder = { id: '7', github_login: 'me' }; req.gdsSession = { session_id: '42' }; next(); });
|
|
41
52
|
app.use('/', build({ store, webauthn, requireBuilder: (q, r, n) => n() }));
|
|
42
53
|
const server = http.createServer(app).listen(0);
|
|
43
54
|
const port = server.address().port;
|
|
@@ -104,5 +115,40 @@ r = await new Promise((resolve) => {
|
|
|
104
115
|
});
|
|
105
116
|
assert.equal(r.status, 503); ok('unconfigured non-local host refused');
|
|
106
117
|
|
|
118
|
+
// --- card check (task 1004747) ---
|
|
119
|
+
creds.length = 0;
|
|
120
|
+
r = await call('POST', '/me/passkeys/authenticate/options', {});
|
|
121
|
+
assert.equal(r.status, 409); ok('card check refused when no card is registered');
|
|
122
|
+
creds.push({ credential_id: 'cardA', builder_id: '7', public_key: Buffer.from([1]), sign_count: 1, transports: ['nfc'] });
|
|
123
|
+
creds.push({ credential_id: 'cardB', builder_id: '8', public_key: Buffer.from([1]), sign_count: 1, transports: [] });
|
|
124
|
+
r = await call('POST', '/me/passkeys/authenticate/options', {});
|
|
125
|
+
assert.equal(r.status, 200); assert.equal(r.body.options.userVerification, 'required');
|
|
126
|
+
assert.deepEqual(r.body.options.allowCredentials.map((c) => c.id), ['cardA']); ok('authenticate options list only the caller cards');
|
|
127
|
+
consumed.length = 0;
|
|
128
|
+
r = await call('POST', '/me/passkeys/authenticate/verify', { response: { id: 'cardA' } });
|
|
129
|
+
assert.equal(r.status, 200); assert.deepEqual(stamped[0], ['42', '7']);
|
|
130
|
+
assert.equal(consumed[0][1].expectPurpose, 'authenticate'); assert.equal(consumed[0][1].expectBuilderId, '7'); ok('verify stamps the caller session with an authenticate-bound challenge');
|
|
131
|
+
stamped.length = 0;
|
|
132
|
+
r = await call('POST', '/me/passkeys/authenticate/verify', { response: { id: 'cardB' } });
|
|
133
|
+
assert.equal(r.status, 400); assert.equal(stamped.length, 0); ok('another builder card stamps nothing');
|
|
134
|
+
r = await call('POST', '/me/passkeys/authenticate/verify', { response: { id: 'nope' } });
|
|
135
|
+
assert.equal(r.status, 400); assert.equal(stamped.length, 0); ok('unknown card stamps nothing');
|
|
136
|
+
counterOk = false;
|
|
137
|
+
r = await call('POST', '/me/passkeys/authenticate/verify', { response: { id: 'cardA' } });
|
|
138
|
+
assert.equal(r.status, 400); assert.equal(stamped.length, 0); ok('a counter that did not advance stamps nothing');
|
|
139
|
+
counterOk = true;
|
|
140
|
+
|
|
141
|
+
// --- the gate itself ---
|
|
142
|
+
const { buildRequireCardStepUp } = require('../src/bongos/step-up');
|
|
143
|
+
const gate = (st) => buildRequireCardStepUp({ store: { stepUpStatus: async () => { if (st instanceof Error) throw st; return st; } } });
|
|
144
|
+
const run = async (mw, req) => { let out = 'next'; const res = { fail: (c, o) => { out = typeof o === 'number' ? [c, o] : [c, o.status]; } }; await mw(req, res, () => {}); return out; };
|
|
145
|
+
const sess = { gdsSession: { session_id: '1' }, builder: { id: '7' } };
|
|
146
|
+
assert.equal(await run(gate({ required: false, fresh: false }), sess), 'next'); ok('gate: requirement off passes');
|
|
147
|
+
assert.equal(await run(gate({ required: true, fresh: true }), sess), 'next'); ok('gate: fresh tap passes');
|
|
148
|
+
assert.deepEqual(await run(gate({ required: true, fresh: false }), sess), ['card_required', 403]); ok('gate: required and stale refused');
|
|
149
|
+
assert.deepEqual(await run(gate(null), sess), ['card_required', 403]); ok('gate: unknown session refused');
|
|
150
|
+
assert.deepEqual(await run(gate(new Error('db down')), sess), ['card_check_failed', 503]); ok('gate: fails closed on error');
|
|
151
|
+
assert.deepEqual(await run(gate({ required: false, fresh: false }), {}), ['unauthenticated', 401]); ok('gate: no session refused');
|
|
152
|
+
|
|
107
153
|
server.close();
|
|
108
154
|
console.log(`${n} passed`);
|
|
@@ -32,12 +32,14 @@ for (const fn of ['execFile', 'spawn', 'exec', 'fork']) {
|
|
|
32
32
|
cp[fn] = (...args) => { spawned.push([fn, args[0], args[1]]); throw new Error(`test: ${fn} must not be called`); };
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
require('../src/bongos/passkeys').stepUpStatus = async () => ({ required: false, fresh: false }); // task 1004747: the step-up gate reads the DB; these stubs have none
|
|
35
36
|
const authPath = require.resolve('../src/bongos/auth.js');
|
|
36
37
|
require.cache[authPath] = {
|
|
37
38
|
id: authPath, filename: authPath, loaded: true,
|
|
38
39
|
exports: {
|
|
39
40
|
requireBuilder: (req, _res, next) => {
|
|
40
41
|
req.builder = { id: '1', rank: 'archon', github_login: 'archon1', display_name: 'Archon One' };
|
|
42
|
+
req.gdsSession = { session_id: '1' };
|
|
41
43
|
next();
|
|
42
44
|
},
|
|
43
45
|
requireRank: () => (_req, _res, next) => next(),
|