openvibers/openvibe.wiki Commits
A guest's session probe answers 200, not 401 (browser check, WS-Q task 3): GET /auth/me with no session credential at all answers { user: null }, which the shared navbar reads as signed out. It asks on every page view, and the 401 logged a console error on each. A credential that is present but invalid or expired still answers 401. Test: auth.test.js (the guest case used to expect 401)
Changes
@@@ -7,7 +7,7 @@* GET /auth/callback → server-side code exchange, sets the cookies* POST /auth/fedcm → the shared navbar's FedCM assertion (nonce checked here, signature by the Network)* GET /auth/logout → clears the cookies (best-effort refresh revoke)-- * GET /auth/me → the signed-in profile (offline JWT verification)++ * GET /auth/me → the signed-in profile (offline JWT verification); a guest gets 200 { user: null }* POST /auth/refresh → rotates tokens with the refresh token** Cookies (host-only): ov_token (access JWT, JS-readable for the navbar), ov_refresh (httpOnly,@@@ -175,9 +175,14 @@ function createSessionRoutes({ config, viewers, fetchImpl = globalThis.fetch, lo});router.get('/me', async (req, res) => {++ res.set('Cache-Control', 'private, no-store');++ // No credential at all (a guest) is signed out, not an error: the shared navbar asks this on every++ // page view, and a 401 logged a console error on each (browser check, OpenVibe.Host). A credential++ // that is present but invalid or expired still answers 401.++ if (!(req.cookies && req.cookies[ACCESS_COOKIE]) && !req.get('authorization')) return res.json({ user: null });const actor = await viewers.resolve(req, { services: false }).catch(() => null);if (!actor || actor.kind !== 'user') return res.status(401).json({ error: 'Not authenticated' });-- res.set('Cache-Control', 'private, no-store').json({ user: actor.user });++ res.json({ user: actor.user });});router.post('/refresh', async (req, res) => {@@@ -55,7 +55,13 @@ const { sanitizeNext } = require('../server/auth/session');r = await H.req(h, 'GET', '/auth/me', { cookie: `ov_token=${token}` });assert.strictEqual(r.status, 200);assert.strictEqual(r.json.user.username, 'ana');-- assert.strictEqual((await H.req(h, 'GET', '/auth/me')).status, 401);++ // A guest (no cookie, no token at all) is signed out, not an error; a bad credential is still 401.++ r = await H.req(h, 'GET', '/auth/me');++ assert.strictEqual(r.status, 200);++ assert.deepStrictEqual(r.json, { user: null });++ assert.strictEqual(r.headers.get('cache-control'), 'private, no-store');++ assert.strictEqual((await H.req(h, 'GET', '/auth/me', { cookie: 'ov_token=expired.or.forged' })).status, 401);++ assert.strictEqual((await H.req(h, 'GET', '/auth/me', { cookie: `ov_token=${H.userToken({ exp: -120 })}` })).status, 401, 'an expired cookie');// An expired cookie is simply signed out on pages.r = await H.req(h, 'GET', '/', { cookie: `ov_token=${H.userToken({ exp: -120 })}` });assert.strictEqual(r.status, 200);
@@ -7,7 +7,7 @@
@@ -7,7 +7,7 @@
* GET /auth/callback → server-side code exchange, sets the cookies
* GET /auth/callback → server-side code exchange, sets the cookies
* POST /auth/fedcm → the shared navbar's FedCM assertion (nonce checked here, signature by the Network)
* POST /auth/fedcm → the shared navbar's FedCM assertion (nonce checked here, signature by the Network)
* GET /auth/logout → clears the cookies (best-effort refresh revoke)
* GET /auth/logout → clears the cookies (best-effort refresh revoke)
- * GET /auth/me → the signed-in profile (offline JWT verification)
+ * GET /auth/me → the signed-in profile (offline JWT verification); a guest gets 200 { user: null }
* POST /auth/refresh → rotates tokens with the refresh token
* POST /auth/refresh → rotates tokens with the refresh token
*
*
* Cookies (host-only): ov_token (access JWT, JS-readable for the navbar), ov_refresh (httpOnly,
* Cookies (host-only): ov_token (access JWT, JS-readable for the navbar), ov_refresh (httpOnly,
@@ -175,9 +175,14 @@ function createSessionRoutes({ config, viewers, fetchImpl = globalThis.fetch, lo
@@ -175,9 +175,14 @@ function createSessionRoutes({ config, viewers, fetchImpl = globalThis.fetch, lo
});
});
router.get('/me', async (req, res) => {
router.get('/me', async (req, res) => {
+ res.set('Cache-Control', 'private, no-store');
+ // No credential at all (a guest) is signed out, not an error: the shared navbar asks this on every
+ // page view, and a 401 logged a console error on each (browser check, OpenVibe.Host). A credential
+ // that is present but invalid or expired still answers 401.
+ if (!(req.cookies && req.cookies[ACCESS_COOKIE]) && !req.get('authorization')) return res.json({ user: null });
const actor = await viewers.resolve(req, { services: false }).catch(() => null);
const actor = await viewers.resolve(req, { services: false }).catch(() => null);
if (!actor || actor.kind !== 'user') return res.status(401).json({ error: 'Not authenticated' });
if (!actor || actor.kind !== 'user') return res.status(401).json({ error: 'Not authenticated' });
- res.set('Cache-Control', 'private, no-store').json({ user: actor.user });
+ res.json({ user: actor.user });
});
});
router.post('/refresh', async (req, res) => {
router.post('/refresh', async (req, res) => {
@@ -55,7 +55,13 @@ const { sanitizeNext } = require('../server/auth/session');
@@ -55,7 +55,13 @@ const { sanitizeNext } = require('../server/auth/session');
r = await H.req(h, 'GET', '/auth/me', { cookie: `ov_token=${token}` });
r = await H.req(h, 'GET', '/auth/me', { cookie: `ov_token=${token}` });
assert.strictEqual(r.status, 200);
assert.strictEqual(r.status, 200);
assert.strictEqual(r.json.user.username, 'ana');
assert.strictEqual(r.json.user.username, 'ana');
- assert.strictEqual((await H.req(h, 'GET', '/auth/me')).status, 401);
+ // A guest (no cookie, no token at all) is signed out, not an error; a bad credential is still 401.
+ r = await H.req(h, 'GET', '/auth/me');
+ assert.strictEqual(r.status, 200);
+ assert.deepStrictEqual(r.json, { user: null });
+ assert.strictEqual(r.headers.get('cache-control'), 'private, no-store');
+ assert.strictEqual((await H.req(h, 'GET', '/auth/me', { cookie: 'ov_token=expired.or.forged' })).status, 401);
+ assert.strictEqual((await H.req(h, 'GET', '/auth/me', { cookie: `ov_token=${H.userToken({ exp: -120 })}` })).status, 401, 'an expired cookie');
// An expired cookie is simply signed out on pages.
// An expired cookie is simply signed out on pages.
r = await H.req(h, 'GET', '/', { cookie: `ov_token=${H.userToken({ exp: -120 })}` });
r = await H.req(h, 'GET', '/', { cookie: `ov_token=${H.userToken({ exp: -120 })}` });
assert.strictEqual(r.status, 200);
assert.strictEqual(r.status, 200);