openvibers/openvibe.media Commits

Sign-out everywhere deliveries verify: POST /internal/events is mounted before express.json(), which consumed the raw body its signature is over (every delivery answered 401). The test holds the order

OP OpenVibers committed 25 Sept 2026 · c140b32e5f · 2 files, +7 −2


Changes

ddiff --git a/server/index.js b/server/index.js
iindex 16801d2..4adf1a4 100644
---- a/server/index.js
++++ b/server/index.js
@@@ -62,6 +62,9 @@ if (drill.enabled) app.use(drill.readOnly);
// Object API v2 content uploads read the raw request body, so they sit ahead of the body parsers.
app.put('/api/v2/:app/objects/:id/content', ...require('./objects/routes').contentHandlers);
app.put('/api/v2/:app/objects/:id/multipart/:uploadId/parts/:n', ...require('./objects/routes').partHandlers);
++// Network's sign-out-everywhere cutoffs (server/revocations.js): signed, loopback-only. Before the JSON
++// parser, which would consume the raw body the signature is over.
++app.post('/internal/events', ...require('./revocations').handler());
app.use(express.json({ limit: '2mb' }));
// ── Visitor sign-in (OAuth client `media` on the Network; same module as Community/Tools) ──
@@@ -93,8 +96,6 @@ app.get('/manifest.webmanifest', (_req, res) => res.type('application/manifest+j
const pastes = require('./pastes/routes');
app.post('/internal/avatar-ingest', internalOnly, require('./avatars/ingest').createIngestHandler({ db: require('./db/database'), config, screenshotsDir: pastes.SCREENSHOTS_DIR, generateSlug: pastes.generateSlug }));
}
--// Network's sign-out-everywhere cutoffs (server/revocations.js): signed, loopback-only, like the rest of /internal.
--app.post('/internal/events', ...require('./revocations').handler());
const userAuth = require('./user-auth');
const userAuthConfig = {
baseUrl: config.publicUrl, networkUrl: config.network.url, networkInternalUrl: config.network.internalUrl,
ddiff --git a/test/revocations.test.js b/test/revocations.test.js
iindex b98fd31..73e327a 100644
---- a/test/revocations.test.js
++++ b/test/revocations.test.js
@@@ -22,7 +22,11 @@ const ev = (over = {}) => ({ event_id: 'evt_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3', event_t
(async () => {
const app = express();
++ // As in server/index.js: mounted before the JSON parser (the signature is over the raw body).
++ const src = fs.readFileSync(path.join(__dirname, '../server/index.js'), 'utf8');
++ assert.ok(src.indexOf("app.post('/internal/events'") < src.indexOf("app.use(express.json("), 'the events route comes before express.json()');
app.post('/internal/events', ...revocations.handler());
++ app.use(express.json());
const srv = http.createServer(app);
await new Promise((r) => srv.listen(0, '127.0.0.1', r));
const url = `http://127.0.0.1:${srv.address().port}/internal/events`;