openvibers/openvibe.live Commits
Live: the legacy token cookie is neither set nor read; ov_token is the session cookie (C-05) (#95)
Changes
@@@ -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;@@@ -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 {@@@ -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;@@@ -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 });nnew file mode 100644@@@ -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');
@@ -185,7 +185,6 @@ function getStoredAuthToken() {
@@ -185,7 +185,6 @@ function getStoredAuthToken() {
localStorage.getItem('token'),
localStorage.getItem('token'),
localStorage.getItem('ov_token'),
localStorage.getItem('ov_token'),
(document.cookie.match(/(?:^|; )ov_token=([^;]+)/) || [])[1],
(document.cookie.match(/(?:^|; )ov_token=([^;]+)/) || [])[1],
- (document.cookie.match(/(?:^|; )token=([^;]+)/) || [])[1],
].filter(Boolean);
].filter(Boolean);
if (!candidates.length) return null;
if (!candidates.length) return null;
@@ -2753,8 +2753,6 @@ function _getAuthToken() {
@@ -2753,8 +2753,6 @@ function _getAuthToken() {
try {
try {
const openvibeMatch = document.cookie.match(/(?:^|;\s*)ov_token=([^;]+)/);
const openvibeMatch = document.cookie.match(/(?:^|;\s*)ov_token=([^;]+)/);
if (openvibeMatch) return decodeURIComponent(openvibeMatch[1]);
if (openvibeMatch) return decodeURIComponent(openvibeMatch[1]);
- const legacyMatch = document.cookie.match(/(?:^|;\s*)token=([^;]+)/);
- if (legacyMatch) return decodeURIComponent(legacyMatch[1]);
const stored = localStorage.getItem('token');
const stored = localStorage.getItem('token');
return stored || null;
return stored || null;
} catch {
} catch {
@@ -342,10 +342,9 @@ function extractToken(req) {
@@ -342,10 +342,9 @@ function extractToken(req) {
return authHeader.slice(7);
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) {
if (req.cookies.ov_token) return req.cookies.ov_token;
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,
// Raw Node/WebSocket upgrade requests do not go through cookie-parser,
@@ -362,7 +361,6 @@ function extractToken(req) {
@@ -362,7 +361,6 @@ function extractToken(req) {
parsed[key] = decodeURIComponent(value);
parsed[key] = decodeURIComponent(value);
}
}
if (parsed.ov_token) return parsed.ov_token;
if (parsed.ov_token) return parsed.ov_token;
- if (parsed.token) return parsed.token;
}
}
return null;
return null;
@@ -398,10 +398,8 @@ async function establishNetworkSession(req, res, tokenData) {
@@ -398,10 +398,8 @@ async function establishNetworkSession(req, res, tokenData) {
const isSecure = (process.env.BASE_URL || '').startsWith('https');
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 });
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
// 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.
// with no session may try one silent sign-in through the network.
@@ -581,7 +579,6 @@ router.post('/refresh', async (req, res) => {
@@ -581,7 +579,6 @@ router.post('/refresh', async (req, res) => {
}
}
const isSecure = (process.env.BASE_URL || '').startsWith('https');
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 });
res.cookie('ov_token', tokenData.access_token, { httpOnly: false, path: '/', maxAge: 7 * 24 * 60 * 60 * 1000, sameSite: 'Lax', secure: isSecure });
if (tokenData.refresh_token) {
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' });
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) => {
@@ -596,7 +593,7 @@ router.post('/refresh', async (req, res) => {
// ── Logout (clear all auth cookies) ──────────────────────────
// ── Logout (clear all auth cookies) ──────────────────────────
function clearSessionCookies(res) {
function clearSessionCookies(res) {
const isSecure = (process.env.BASE_URL || '').startsWith('https');
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_token', { sameSite: 'Lax', secure: isSecure });
res.clearCookie('ov_refresh', { path: '/api/auth', 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 });
res.cookie('ov_sso_hint', 'guest', { ...HINT_COOKIE, secure: isSecure });
new file mode 100644
new file mode 100644
@@ -0,0 +1,34 @@
@@ -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');