openvibers/openvibe.tools Commits

The gateway keeps retrying Network's public key every 30 s until it loads, instead of only at boot and on a signed-in request (#91)

OP OpenVibers committed 20 days ago · 113974c9d5 · 2 files, +47 −4

Changes

ddiff --git a/apps/gateway/server/auth/routes.js b/apps/gateway/server/auth/routes.js
iindex a90f8a9..0a34fb9 100644
---- a/apps/gateway/server/auth/routes.js
++++ b/apps/gateway/server/auth/routes.js
@@@ -132,10 +132,11 @@ function createAuthClient(config) {
});
let lastFetch = 0;
++ const gapMs = config.keyRetryMs || 30_000;
async function ensureKey() {
if (client.publicKey) return client.publicKey;
// Don't hammer the Network if it's down — retry at most every 30s
-- if (Date.now() - lastFetch < 30_000) return null;
++ if (Date.now() - lastFetch < gapMs) return null;
lastFetch = Date.now();
for (const base of [config.networkInternalUrl, config.networkUrl]) {
if (!base) continue;
@@@ -162,10 +163,18 @@ function createAuthClient(config) {
return client.verifyToken(token);
}
-- // Warm the key cache at boot (non-fatal if Network is down)
-- ensureKey().catch(() => {});
++ // Warm the key cache at boot, and keep trying every 30 s until it loads (non-fatal if Network is down). After the
++ // 2026-10-23 reboot the gateway started before Network, its one boot fetch failed, and /status showed it without a
++ // key for six hours: only a signed-in request would have fetched it again.
++ let retry = null;
++ const warm = () => ensureKey().catch(() => null).then((key) => {
++ if (key) return;
++ retry = setTimeout(warm, gapMs);
++ if (retry.unref) retry.unref();
++ });
++ warm();
-- return { client, ensureKey, verify };
++ return { client, ensureKey, verify, stop: () => { if (retry) clearTimeout(retry); retry = null; } };
}
/** Token from Authorization header or the shared ov_token cookie. */
ddiff --git a/apps/gateway/test/auth-key-retry.test.js b/apps/gateway/test/auth-key-retry.test.js
nnew file mode 100644
iindex 0000000..3e0ca3a
---- /dev/null
++++ b/apps/gateway/test/auth-key-retry.test.js
@@@ -0,0 +1,34 @@
++'use strict';
++// The gateway's Network key (createAuthClient): loaded at boot, and when Network is not answering yet, retried by
++// itself every keyRetryMs (30 s in production) until it loads. After the 2026-10-23 reboot the gateway started before
++// Network and showed no key on /status for six hours, because only a signed-in request fetched it again.
++const assert = require('assert');
++const http = require('http');
++const { createAuthClient } = require('../server/auth/routes');
++
++(async () => {
++ console.log = () => {}; console.warn = () => {};
++ let up = false; let fetches = 0;
++ const server = http.createServer((req, res) => {
++ fetches++;
++ if (!up || req.url !== '/api/.well-known/jwks') { res.writeHead(503); return res.end(); }
++ res.writeHead(200, { 'Content-Type': 'application/json' });
++ res.end(JSON.stringify({ public_key: '-----BEGIN PUBLIC KEY-----\nTEST\n-----END PUBLIC KEY-----\n', algorithm: 'RS256' }));
++ });
++ await new Promise((r) => server.listen(0, '127.0.0.1', r));
++ const base = `http://127.0.0.1:${server.address().port}`;
++ const auth = createAuthClient({ oauth: { clientId: 'tools', clientSecret: '', redirectUri: `${base}/cb` }, networkUrl: '', networkInternalUrl: base, keyRetryMs: 100 });
++ await new Promise((r) => setTimeout(r, 50));
++ assert.strictEqual(auth.client.publicKey, null, 'Network down at boot: no key yet');
++ assert.strictEqual(fetches, 1);
++ up = true;
++ await new Promise((r) => setTimeout(r, 250));
++ assert.ok(auth.client.publicKey && auth.client.publicKey.includes('TEST'), 'loaded by the retry, with no request asking for it');
++ const n = fetches;
++ await new Promise((r) => setTimeout(r, 250));
++ assert.strictEqual(fetches, n, 'no more fetches once it is loaded');
++ auth.stop();
++ server.close();
++ process.stdout.write('auth-key-retry: ok\n');
++ process.exit(0);
++})().catch((e) => { process.stderr.write(`${e.stack}\n`); process.exit(1); });