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'
Changes
@@@ -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"},nnew file mode 100644@@@ -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 };@@@ -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',nnew file mode 100644@@@ -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');
@@ -6,7 +6,7 @@
@@ -6,7 +6,7 @@
"scripts": {
"scripts": {
"start": "node server/index.js",
"start": "node server/index.js",
"dev": "NODE_ENV=development 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",
"reset-db": "node server/reset-db.js",
"grant-admin": "node server/grant-admin.js"
"grant-admin": "node server/grant-admin.js"
},
},
new file mode 100644
new file mode 100644
@@ -0,0 +1,29 @@
@@ -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 };
@@ -216,6 +216,9 @@ function buildAllowedOriginsSet() {
@@ -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)
// Hard-coded OpenVibe defaults as baseline (survive registry reset/failure)
for (const o of [
for (const o of [
'https://openvibe.network',
'https://openvibe.network',
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -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');