openvibers/openvibe.live Commits

Live: the legacy token cookie is neither set nor read; ov_token is the session cookie (C-05) (#95)

OP OpenVibers committed 11 Oct 2026 · 3a1b580786 · 5 files, +38 −12

Compatibility register C-05, Live's half: the legacy `token` cookie is no longer set or read.

**Why it is safe:** Live set `token` and `ov_token` side by side, with the same Network access token and the same 7-day lifetime, at sign-in and at every refresh. So any browser holding a live `token` also holds `ov_token`, and `token` adds nothing. It survived only as a second name that every reader checked after `ov_token`.

**Changes:**
- **Setting:** `server/auth/routes.js` sets only `ov_token` at sign-in and refresh. Logout still clears a leftover `token`, so old browsers lose it.
- **Server:** `extractToken` (`server/auth/auth.js`) reads the bearer and `ov_token` only, from cookie-parser and from the raw Cookie header alike.
- **Browser:** `public/js/app.js` `getStoredAuthToken` and `public/js/call.js` `_getAuthToken` stop falling back to the cookie. Their localStorage fallbacks are untouched.
- **Test:** `test/legacy-token-cookie.test.js` checks that a lone `token` cookie authenticates nothing, that no route sets it, that logout clears it, and that neither browser file reads it.

Chat stops reading the cookie in its own PR, and Shared's middleware fallback follows with its next release.

`ov test`: 37 covering files pass, plus the new test.

Changes

ddiff --git a/public/js/app.js b/public/js/app.js
iindex 77495cbb..269c9b32 100644
---- a/public/js/app.js
++++ b/public/js/app.js
@@@ -185,7 +185,6 @@ function getStoredAuthToken() {
localStorage.getItem('token'),
localStorage.getItem('ov_token'),
(document.cookie.match(/(?:^|; )ov_token=([^;]+)/) || [])[1],
-- (document.cookie.match(/(?:^|; )token=([^;]+)/) || [])[1],
].filter(Boolean);
if (!candidates.length) return null;
ddiff --git a/public/js/call.js b/public/js/call.js
iindex 99735c85..493f9b58 100644
---- a/public/js/call.js
++++ b/public/js/call.js
@@@ -2753,8 +2753,6 @@ function _getAuthToken() {
try {
const openvibeMatch = document.cookie.match(/(?:^|;\s*)ov_token=([^;]+)/);
if (openvibeMatch) return decodeURIComponent(openvibeMatch[1]);
-- const legacyMatch = document.cookie.match(/(?:^|;\s*)token=([^;]+)/);
-- if (legacyMatch) return decodeURIComponent(legacyMatch[1]);
const stored = localStorage.getItem('token');
return stored || null;
} catch {
ddiff --git a/server/auth/auth.js b/server/auth/auth.js
iindex 9d628652..4f587463 100644
---- a/server/auth/auth.js
++++ b/server/auth/auth.js
@@@ -342,10 +342,9 @@ function extractToken(req) {
return authHeader.slice(7);
}
-- // Check both cookie names: 'token' (legacy openvibelive) and 'ov_token' (shared network)
++ // The Network session cookie. (Live's legacy 'token' cookie is no longer read: compatibility register C-05.)
if (req.cookies) {
if (req.cookies.ov_token) return req.cookies.ov_token;
-- if (req.cookies.token) return req.cookies.token;
}
// Raw Node/WebSocket upgrade requests do not go through cookie-parser,
@@@ -362,7 +361,6 @@ function extractToken(req) {
parsed[key] = decodeURIComponent(value);
}
if (parsed.ov_token) return parsed.ov_token;
-- if (parsed.token) return parsed.token;
}
return null;
ddiff --git a/server/auth/routes.js b/server/auth/routes.js
iindex c221378e..a5526752 100644
---- a/server/auth/routes.js
++++ b/server/auth/routes.js
@@@ -398,10 +398,8 @@ async function establishNetworkSession(req, res, tokenData) {
const isSecure = (process.env.BASE_URL || '').startsWith('https');
-- // Set access token cookie (readable by JS for API calls)
-- res.cookie('token', openvibeToolsToken, { httpOnly: false, path: '/', maxAge: 7 * 24 * 60 * 60 * 1000, sameSite: 'Lax', secure: isSecure });
--
-- // Also set ov_token (used by shared navbar/notification libs)
++ // The session cookie, readable by JS for API calls and by the shared navbar/notification libs. (Live's old
++ // `token` cookie, the same token under another name, is no longer set: compatibility register C-05.)
res.cookie('ov_token', openvibeToolsToken, { httpOnly: false, path: '/', maxAge: 7 * 24 * 60 * 60 * 1000, sameSite: 'Lax', secure: isSecure });
// Long-lived hint (outlives the token): this browser has signed in here, so a later visit
// with no session may try one silent sign-in through the network.
@@@ -581,7 +579,6 @@ router.post('/refresh', async (req, res) => {
}
const isSecure = (process.env.BASE_URL || '').startsWith('https');
-- res.cookie('token', tokenData.access_token, { httpOnly: false, path: '/', maxAge: 7 * 24 * 60 * 60 * 1000, sameSite: 'Lax', secure: isSecure });
res.cookie('ov_token', tokenData.access_token, { httpOnly: false, path: '/', maxAge: 7 * 24 * 60 * 60 * 1000, sameSite: 'Lax', secure: isSecure });
if (tokenData.refresh_token) {
res.cookie('ov_refresh', tokenData.refresh_token, { httpOnly: true, maxAge: 30 * 24 * 60 * 60 * 1000, sameSite: 'Lax', secure: isSecure, path: '/api/auth' });
@@@ -596,7 +593,7 @@ router.post('/refresh', async (req, res) => {
// ── Logout (clear all auth cookies) ──────────────────────────
function clearSessionCookies(res) {
const isSecure = (process.env.BASE_URL || '').startsWith('https');
-- res.clearCookie('token', { sameSite: 'Lax', secure: isSecure });
++ res.clearCookie('token', { sameSite: 'Lax', secure: isSecure }); // a leftover legacy cookie (no longer set, C-05)
res.clearCookie('ov_token', { sameSite: 'Lax', secure: isSecure });
res.clearCookie('ov_refresh', { path: '/api/auth', sameSite: 'Lax', secure: isSecure });
res.cookie('ov_sso_hint', 'guest', { ...HINT_COOKIE, secure: isSecure });
ddiff --git a/test/legacy-token-cookie.test.js b/test/legacy-token-cookie.test.js
nnew file mode 100644
iindex 00000000..f4321154
---- /dev/null
++++ b/test/legacy-token-cookie.test.js
@@@ -0,0 +1,34 @@
++/**
++ * Compatibility register C-05: Live's legacy `token` cookie (the same Network token as `ov_token`, under another name)
++ * is neither set nor read any more. `ov_token` is the session cookie; logout still clears a leftover `token`.
++ *
++ * node test/legacy-token-cookie.test.js
++ */
++'use strict';
++const assert = require('assert');
++const fs = require('fs');
++const path = require('path');
++
++process.env.NODE_ENV = 'test';
++const ROOT = path.join(__dirname, '..');
++const { extractToken } = require('../server/auth/auth');
++
++// The server reads the Network cookie only: cookie-parser's object and the raw header (WebSocket upgrades) alike.
++assert.strictEqual(extractToken({ headers: {}, cookies: { ov_token: 'net' } }), 'net');
++assert.strictEqual(extractToken({ headers: { cookie: 'ov_token=net' } }), 'net');
++assert.strictEqual(extractToken({ headers: {}, cookies: { token: 'legacy' } }), null, 'a lone legacy cookie authenticates nothing');
++assert.strictEqual(extractToken({ headers: { cookie: 'token=legacy' } }), null, 'nor through the raw Cookie header');
++assert.strictEqual(extractToken({ headers: {}, cookies: { token: 'legacy', ov_token: 'net' } }), 'net');
++
++// Sign-in and refresh set ov_token only; logout clears both names.
++const routes = fs.readFileSync(path.join(ROOT, 'server/auth/routes.js'), 'utf8');
++assert.ok(!/res\.cookie\(\s*'token'/.test(routes), 'no route sets the legacy token cookie');
++assert.ok(/res\.clearCookie\(\s*'token'/.test(routes), 'logout still clears a leftover legacy cookie');
++
++// The browser code reads ov_token, never the legacy cookie.
++for (const f of ['public/js/app.js', 'public/js/call.js']) {
++ const src = fs.readFileSync(path.join(ROOT, f), 'utf8');
++ assert.ok(!/\)token=\(\[\^;\]\+\)/.test(src), `${f} does not read the legacy token cookie`);
++}
++
++console.log('legacy token cookie: not set, not read; ov_token is the session cookie — all checks passed');