openvibers/openvibe.design Commits

Open Design messages in Dock Chats (#201)

OP OpenVibers committed 4 days ago · e62835505a · 4 files, +23 −0

Changes

ddiff --git a/deploy/nginx/openvibe.design.conf b/deploy/nginx/openvibe.design.conf
iindex c3c78cb..6ebd7f7 100644
---- a/deploy/nginx/openvibe.design.conf
++++ b/deploy/nginx/openvibe.design.conf
@@@ -100,6 +100,13 @@ server {
proxy_pass http://127.0.0.1:5090;
}
++ # The Dock token is fetched on signed-in page views; give it the session probe's budget.
++ location = /auth/navbar-token {
++ limit_req zone=design_api burst=30 nodelay;
++ limit_req_status 429;
++ proxy_pass http://127.0.0.1:5090;
++ }
++
location /auth/ {
limit_except GET { deny all; }
limit_req zone=design_auth burst=10 nodelay;
ddiff --git a/server/auth/sso.js b/server/auth/sso.js
iindex fecf88c..552756d 100644
---- a/server/auth/sso.js
++++ b/server/auth/sso.js
@@@ -183,6 +183,16 @@ function createSso({ config, keys, fetchImpl = globalThis.fetch, now = () => Dat
return res.json({ user: meOf(claims) });
});
++ // The navbar needs a Network access token for the Dock. Keep /auth/me's
++ // profile response token-free; this same-origin answer is never cached.
++ r.get('/navbar-token', async (req, res) => {
++ res.set('Cache-Control', 'private, no-store');
++ const token = sso.extractToken(req);
++ const claims = await sso.verify(token);
++ if (!claims) return res.status(401).json({ error: 'Not signed in' });
++ return res.json({ token, expires_at: claims.exp ? claims.exp * 1000 : null });
++ });
++
// Sign-out stays a same-site action: another site cannot sign a person out.
const sameOriginRequest = (req) => {
const site = String(req.get('sec-fetch-site') || '');
ddiff --git a/server/render/layout.js b/server/render/layout.js
iindex 4b51ab2..fe6f568 100644
---- a/server/render/layout.js
++++ b/server/render/layout.js
@@@ -61,6 +61,7 @@ function layout({ title, description, canonical, path, robots = 'index, follow',
history: { type: 'page', title: title || NAME },
silentLogin: `${BASE}/auth/login?silent=1&next={url}`,
sessionUrl: '/auth/me',
++ tokenUrl: '/auth/navbar-token',
loginUrl: '/auth/login?next={path}',
logoutUrl: '/auth/logout?next={path}',
};
ddiff --git a/test/sso.test.js b/test/sso.test.js
iindex f519c08..e109d79 100644
---- a/test/sso.test.js
++++ b/test/sso.test.js
@@@ -84,6 +84,11 @@ const cookieJar = (r) => cookies(r).map((c) => c.split(';')[0]).join('; ');
assert.strictEqual(me.status, 200);
assert.strictEqual(me.json().user.username, 'kim');
assert.ok(!me.text.includes(at.split('=')[1]), '/auth/me never returns the token');
++ const nav = await t.get('/auth/navbar-token', { cookie: at });
++ assert.strictEqual(nav.status, 200);
++ assert.strictEqual(nav.json().token, at.split('=')[1], 'the Dock receives the verified Network token');
++ assert.strictEqual(nav.headers.get('cache-control'), 'private, no-store');
++ assert.strictEqual((await t.get('/auth/navbar-token')).status, 401, 'a guest receives no token');
const tokenRequest = t.network.tokenRequests[t.network.tokenRequests.length - 1];
assert.strictEqual(tokenRequest.client_id, 'design');
assert.strictEqual(tokenRequest.grant_type, 'authorization_code');