openvibers/openvibe.host Commits

Open Host messages in Dock Chats (#340)

OP OpenVibers committed 4 days ago · aa238f2139 · 5 files, +52 −0

Changes

ddiff --git a/deploy/nginx/openvibe.host.conf b/deploy/nginx/openvibe.host.conf
iindex ec85a86..a37ec19 100644
---- a/deploy/nginx/openvibe.host.conf
++++ b/deploy/nginx/openvibe.host.conf
@@@ -47,6 +47,10 @@ server {
limit_req zone=ovhost_api burst=30 nodelay;
proxy_pass http://127.0.0.1:4910;
}
++ location = /auth/navbar-token {
++ limit_req zone=ovhost_api burst=30 nodelay;
++ proxy_pass http://127.0.0.1:4910;
++ }
location /auth/ {
limit_req zone=ovhost_auth burst=10 nodelay;
proxy_pass http://127.0.0.1:4910;
ddiff --git a/server/auth/sso.js b/server/auth/sso.js
iindex 046169e..79429e6 100644
---- a/server/auth/sso.js
++++ b/server/auth/sso.js
@@@ -2,6 +2,7 @@
/** Host-only, HttpOnly session and OAuth flow cookies around openvibe-sdk/sso. */
const crypto = require('crypto');
++const { ids } = require('openvibe-contracts');
const { COOKIES, decodeJwtPayload, parseCookies } = require('openvibe-sdk/sso');
const SESSION_MAX_AGE = 30 * 24 * 3600e3; // the SDK's ov_refresh lifetime: a refresh always finds the session it renews
@@@ -84,6 +85,17 @@ function createHostSession({ auth, config }) {
function router(express) {
const r = express.Router();
r.use(guard);
++ r.get('/navbar-token', async (req, res) => {
++ res.set('Cache-Control', 'private, no-store');
++ const token = sessionToken(config, req);
++ let claims = null;
++ try { if (token) claims = await auth.verify(token); } catch { /* an invalid session */ }
++ if (!claims || !ids.isSubjectId('user', claims.subject_id) || /^(svc|app|mod):/.test(String(claims.sub || ''))
++ || claims.typ !== undefined || claims.nonce !== undefined || claims.actor_type !== undefined) {
++ return res.status(401).json({ error: 'Not signed in' });
++ }
++ return res.json({ token });
++ });
r.use(auth.router(express));
return r;
}
ddiff --git a/server/render/layout.js b/server/render/layout.js
iindex c63fcb4..a2fcee8 100644
---- a/server/render/layout.js
++++ b/server/render/layout.js
@@@ -74,6 +74,7 @@ function renderPage(o) {
history: { type: 'page', title: o.title || SITE_NAME },
silentLogin: `${o.config.baseUrl}/auth/login?silent=1&next={url}`,
sessionUrl: '/auth/me',
++ tokenUrl: '/auth/navbar-token',
loginUrl: '/auth/login?next={path}', // filled from the current page (boost moves between pages)
logoutUrl: '/auth/logout?next={path}', // Sign out in the shared navbar ends this site's session too
notificationsRealtime: true, // the bell hears new notifications over OpenVibe.Events (Shared 1.22.0)
ddiff --git a/test/navbar-token.test.js b/test/navbar-token.test.js
nnew file mode 100644
iindex 0000000..30de1a6
---- /dev/null
++++ b/test/navbar-token.test.js
@@@ -0,0 +1,34 @@
++'use strict';
++const assert = require('assert');
++const express = require('express');
++const { createHostSession } = require('../server/auth/sso');
++
++const person = { sub: 'usr_01JABCDEFGHJKMNPQRSTVWXYZ0', subject_id: 'usr_01JABCDEFGHJKMNPQRSTVWXYZ0' };
++const auth = {
++ async verify(token) {
++ if (token === 'person.jwt.sig') return person;
++ if (token === 'service.jwt.sig') return { ...person, sub: 'svc:host' };
++ if (token === 'assertion.jwt.sig') return { ...person, typ: 'fedcm' };
++ return null;
++ },
++ router: () => express.Router(),
++};
++const app = express();
++app.use('/auth', createHostSession({ auth, config: { cookies: { secure: false } } }).router(express));
++
++(async () => {
++ const server = app.listen(0, '127.0.0.1');
++ try {
++ await new Promise((resolve) => server.once('listening', resolve));
++ const base = `http://127.0.0.1:${server.address().port}`;
++ const get = (cookie) => fetch(`${base}/auth/navbar-token`, { headers: cookie ? { Cookie: `ov_host_session=${cookie}` } : {} });
++ const ok = await get('person.jwt.sig');
++ assert.strictEqual(ok.status, 200);
++ assert.strictEqual((await ok.json()).token, 'person.jwt.sig');
++ assert.strictEqual(ok.headers.get('cache-control'), 'private, no-store');
++ for (const cookie of [null, 'invalid', 'service.jwt.sig', 'assertion.jwt.sig']) {
++ assert.strictEqual((await get(cookie)).status, 401, `refuse ${cookie || 'a guest'}`);
++ }
++ console.log('navbar token: verified person only, uncached');
++ } finally { await new Promise((resolve) => server.close(resolve)); }
++})().catch((error) => { console.error(error); process.exitCode = 1; });
ddiff --git a/test/render-layout.test.js b/test/render-layout.test.js
iindex 6a00e39..753d8c5 100644
---- a/test/render-layout.test.js
++++ b/test/render-layout.test.js
@@@ -25,5 +25,6 @@ runTests([
const cfg = JSON.parse(page().match(/window\.__OV_PAGE = (.+);/)[1]);
assert.strictEqual(cfg.navbar.loginUrl, '/auth/login?next={path}');
assert.strictEqual(cfg.navbar.logoutUrl, '/auth/logout?next={path}');
++ assert.strictEqual(cfg.navbar.tokenUrl, '/auth/navbar-token');
}),
]);