openvibers/openvibe.watch Commits

Enable silent first-visit sign-in on OpenVibe.Watch (#240)

OP OpenVibers committed 5 days ago · 3927adfc57 · 3 files, +22 −3

Changes

ddiff --git a/server/auth/sso.js b/server/auth/sso.js
iindex 6a438b3..9ecab18 100644
---- a/server/auth/sso.js
++++ b/server/auth/sso.js
@@@ -22,7 +22,7 @@
*/
const crypto = require('crypto');
const express = require('express');
--const { createSsoClient, failurePage } = require('openvibe-sdk/sso');
++const { createSsoClient, failurePage, withParam } = require('openvibe-sdk/sso');
const cache = require('openvibe-shared/cache-policy');
const SESSION_COOKIE = 'watch_session';
@@@ -164,8 +164,9 @@ function createSso({ config, keys, sessions, fetchImpl = globalThis.fetch, now =
r.get('/login', (req, res) => {
const next = sanitizeNext(req.query.next, config);
-- const started = sdkSso.authorizeUrl();
-- res.cookie(FLOW_COOKIE, JSON.stringify({ state: started.state, verifier: started.verifier, next }), flowCookie());
++ const silent = !!req.query.silent && req.query.silent !== '0';
++ const started = sdkSso.authorizeUrl({ silent });
++ res.cookie(FLOW_COOKIE, JSON.stringify({ state: started.state, verifier: started.verifier, next, silent }), flowCookie());
res.set('Cache-Control', cache.htmlHeaders({ private: true }));
return res.redirect(started.url);
});
@@@ -179,6 +180,7 @@ function createSso({ config, keys, sessions, fetchImpl = globalThis.fetch, now =
if (req.query.error) {
// An explicit answer from the Network (access_denied, login_required, …) is not retried by itself.
res.clearCookie(RETRY_COOKIE, { ...cookieBase(), path: '/auth' });
++ if (flow?.silent && sameString(req.query.state, flow.state)) return res.redirect(withParam(next, 'sso', 'none'));
log.warn(`[sso] sign-in not completed: ${String(req.query.error).slice(0, 120)}`);
return res.status(400).type('html').send(failurePage({
siteName: 'OpenVibe.Watch',
ddiff --git a/server/render/layout.js b/server/render/layout.js
iindex 3171bd7..408077a 100644
---- a/server/render/layout.js
++++ b/server/render/layout.js
@@@ -57,6 +57,7 @@ function renderPage(o) {
appBar: [{ label: 'Home', href: '/', icon: 'fa-house' }, { label: 'Watches', href: '/watches', icon: 'fa-bell' }, { label: 'How it checks', href: '/how-it-works', icon: 'fa-circle-info' }], // phones: the bottom tab bar (Shared 4.20)
history: { type: 'page', title: o.title || SITE_NAME },
sessionUrl: '/auth/me',
++ silentLogin: '/auth/login?silent=1&next={url}',
loginUrl: '/auth/login?next={path}',
logoutUrl: '/auth/logout?next={path}',
notificationsRealtime: true,
ddiff --git a/test/site.test.js b/test/site.test.js
iindex b36e139..5a4a937 100644
---- a/test/site.test.js
++++ b/test/site.test.js
@@@ -65,6 +65,22 @@ t('the signed-out pages say what Watch is and ask for a sign-in', async () => {
assert.doesNotMatch(how.text, /Not built yet/);
});
++t('silent first visit: prompt=none and a guest returns without a login form', async () => {
++ const started = await svc.get('/auth/login?silent=1&next=%2Fwatches%3Fview%3Dall%23latest');
++ const u = new URL(started.location);
++ assert.strictEqual(u.searchParams.get('prompt'), 'none');
++ assert.strictEqual(u.searchParams.get('code_challenge_method'), 'S256');
++ const flow = started.headers.getSetCookie().map(c => c.split(';')[0]).join('; ');
++ const guest = await svc.get('/auth/callback?error=login_required&state='+u.searchParams.get('state'), { cookie: flow });
++ assert.strictEqual(guest.status, 302);
++ assert.strictEqual(guest.location, '/watches?view=all&sso=none#latest');
++ assert.ok(!guest.headers.getSetCookie().some(c => c.startsWith('watch_session=')));
++ const badState = await svc.get('/auth/callback?error=login_required&state=wrong', { cookie: flow });
++ assert.strictEqual(badState.status, 400);
++ const html = require('../server/render/layout').renderPage({ config: svc.config, body: '' });
++ assert.ok(html.includes('/auth/login?silent=1&next={url}'));
++});
++
t('sign-in with the stand-in Network: PKCE, a session, and /auth/me', async () => {
const who = svc.network.addUser('ada'); // same username, distinct subject for this flow test
who.avatar_url = 'https://openvibe.media/o/med_01JABCDEFGHJKMNPQRSTVWXYZ0';