openvibers/openvibe.network Commits

CORS: every first-party domain the service manifests declare is an allowed origin (exact https origins, never a wildcard). openvibe.blog, openvibe.wiki and openvibe.codes were missing from the hard-coded list, so the shared navbar's /api/auth/me was refused there and signed-in people saw 'Sign In'

OP OpenVibers committed 24 Sept 2026 · 90cd58e731 · 4 files, +48 −1


Changes

ddiff --git a/package.json b/package.json
iindex c80bfd37..d9c6a420 100644
---- a/package.json
++++ b/package.json
@@@ -6,7 +6,7 @@
"scripts": {
"start": "node server/index.js",
"dev": "NODE_ENV=development node server/index.js",
-- "test": "node test/registry-bootstrap.test.js && node test/nginx-generator-live.test.js && node test/admin-refresh.test.js && node test/history-sso.test.js && node test/sso-check.test.js && node test/fedcm.test.js && node test/domains.test.js && node test/chrome.test.js && node test/avatar.test.js && node test/roadmap-baseline.test.js && node test/identity-subjects.test.js && node test/principals.test.js && node test/modules.test.js && node test/module-events.test.js && node test/registry-ecosystem.test.js && node test/registry-exposure.test.js && node test/registry-topics.test.js && node test/service-principal.test.js && node test/oauth-pkce.test.js && node test/developer-projects.test.js && node test/developer-defaults.test.js && node test/public-cors.test.js && node test/observability.test.js && node test/status.test.js && node test/events-consumer.test.js && node test/security-session.test.js && node test/security-redirects.test.js && node test/analytics-privacy.test.js && node test/data-ownership.test.js && node test/unknown-paths.test.js && node test/oidc-discovery.test.js && node test/refresh-tokens.test.js && node test/provider-secrets.test.js",
++ "test": "node test/registry-bootstrap.test.js && node test/nginx-generator-live.test.js && node test/admin-refresh.test.js && node test/history-sso.test.js && node test/sso-check.test.js && node test/fedcm.test.js && node test/domains.test.js && node test/chrome.test.js && node test/avatar.test.js && node test/roadmap-baseline.test.js && node test/identity-subjects.test.js && node test/principals.test.js && node test/modules.test.js && node test/module-events.test.js && node test/registry-ecosystem.test.js && node test/registry-exposure.test.js && node test/registry-topics.test.js && node test/service-principal.test.js && node test/oauth-pkce.test.js && node test/developer-projects.test.js && node test/developer-defaults.test.js && node test/public-cors.test.js && node test/observability.test.js && node test/status.test.js && node test/events-consumer.test.js && node test/security-session.test.js && node test/security-redirects.test.js && node test/analytics-privacy.test.js && node test/data-ownership.test.js && node test/unknown-paths.test.js && node test/oidc-discovery.test.js && node test/refresh-tokens.test.js && node test/provider-secrets.test.js && node test/first-party-origins.test.js",
"reset-db": "node server/reset-db.js",
"grant-admin": "node server/grant-admin.js"
},
ddiff --git a/server/first-party-origins.js b/server/first-party-origins.js
nnew file mode 100644
iindex 00000000..d5e99d86
---- /dev/null
++++ b/server/first-party-origins.js
@@@ -0,0 +1,29 @@
++'use strict';
++/**
++ * Every first-party origin the service manifests declare (openvibe-contracts manifests/services/*.json
++ * `domains`): openvibe.blog, openvibe.wiki, openvibe.codes, search.openvibe.network, … The CORS
++ * allow-list includes them, so a newly launched site's shared navbar can ask /api/auth/me, the
++ * notifications and the wallet without anyone editing a hard-coded list (a site that was missing
++ * from it showed "Sign In" to signed-in people: the navbar's cross-origin call was refused).
++ * Only exact https origins from the manifests: never a wildcard, so tenant subdomains of a hosting
++ * product can never become credentialed origins.
++ */
++const contracts = require('openvibe-contracts');
++
++const HOST_RE = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/;
++let cached = null;
++
++function manifestOrigins() {
++ if (cached) return cached;
++ const out = new Set();
++ for (const m of (contracts.services && contracts.services.manifests) || []) {
++ for (const d of m.domains || []) {
++ const host = String(d || '').trim().toLowerCase();
++ if (HOST_RE.test(host)) out.add(`https://${host}`);
++ }
++ }
++ cached = out;
++ return out;
++}
++
++module.exports = { manifestOrigins };
ddiff --git a/server/index.js b/server/index.js
iindex 339f8c7d..81f189aa 100644
---- a/server/index.js
++++ b/server/index.js
@@@ -216,6 +216,9 @@ function buildAllowedOriginsSet() {
}
}
++ // Every first-party domain the service manifests declare (openvibe.blog, openvibe.wiki, …)
++ for (const o of require('./first-party-origins').manifestOrigins()) origins.add(o);
++
// Hard-coded OpenVibe defaults as baseline (survive registry reset/failure)
for (const o of [
'https://openvibe.network',
ddiff --git a/test/first-party-origins.test.js b/test/first-party-origins.test.js
nnew file mode 100644
iindex 00000000..70fdb92b
---- /dev/null
++++ b/test/first-party-origins.test.js
@@@ -0,0 +1,15 @@
++'use strict';
++// The CORS allow-list takes every first-party domain from the service manifests, so a new site's
++// shared navbar can reach /api/auth/me (openvibe.blog once showed "Sign In" to signed-in people).
++// node test/first-party-origins.test.js
++const assert = require('assert');
++const { manifestOrigins } = require('../server/first-party-origins');
++
++const o = manifestOrigins();
++for (const want of ['https://openvibe.blog', 'https://openvibe.wiki', 'https://openvibe.codes', 'https://search.openvibe.network', 'https://openvibe.live', 'https://my.openvibe.network']) {
++ assert.ok(o.has(want), `${want} is a first-party origin`);
++}
++for (const origin of o) assert.ok(/^https:\/\/[a-z0-9.-]+$/.test(origin), `${origin} is an exact https origin`);
++assert.ok(![...o].some((x) => x.includes('*')), 'never a wildcard');
++assert.ok(!o.has('https://evil.example'));
++console.log('first-party-origins: all checks passed');