openvibers/openvibe.rent Commits
Enable silent first-visit sign-in on OpenVibe.Rent (#243)
Changes
@@@ -58,6 +58,10 @@ async function createApp(opts = {}) {const keys = createKeyStore({ config, fetchImpl, log });const sso = createSso({ config, keys, fetchImpl, now: s.now, log });++ const homesSso = createSso({ config: { ...config, baseUrl: config.homes.url,++ oauth: { ...config.oauth, redirectUri: `${config.homes.url}/auth/callback` } }, keys, fetchImpl, now: s.now, log });++ const homesAuthHost = new URL(config.homes.url).hostname;++const principal = createPrincipal({ config, keys });const ctx = { config, s, keys, sso, principal, log };// Listing pages in OpenVibe.Search (./search-index.js): every store write names the listing it touched (s.search),@@@ -168,7 +172,8 @@ async function createApp(opts = {}) {// ── Sign-in (OAuth2 + PKCE client of OpenVibe.Network) ──// /auth/me is the navbar's who-is-signed-in read on every page, so it is not counted with sign-ins: 60 page views in 15 minutes made a signed-in visitor look signed out.app.use('/auth/', rateLimit({ windowMs: 15 * 60_000, limit: 60, skip: (req) => req.path === '/me', standardHeaders: true, legacyHeaders: false }));-- app.use('/auth', sso.routes());++ const rentAuth = sso.routes(), homesAuth = homesSso.routes();++ app.use('/auth', (req, res, next) => (req.hostname === homesAuthHost ? homesAuth : rentAuth)(req, res, next));{ const legal = require('openvibe-shared/legal'); app.get(legal.PATHS, legal.handler({ id: 'rent', service: 'rent', host: 'openvibe.rent', name: 'OpenVibe.Rent', profile: 'ugc' })); }// ── OpenVibe.Events → this service (loopback only) ──────@@@ -91,16 +91,12 @@ function renderPage(o) {links: NAV.map((l) => ({ label: l.label, href: l.href, ...(l.icon ? { icon: l.icon } : {}), ...(l.more ? { more: true } : {}), active: pathNow === l.href || pathNow.startsWith(`${l.href}/`) })),appBar: true, // phones: the places with icons as the bottom tab bar (Shared 4.20)history: { type: 'page', title: o.title || SITE_NAME },-- // On Homes the account lives on OpenVibe.Rent: its session probe answers signed out, and sign-in goes there.-- ...(homes ? {-- loginUrl: `${o.config.baseUrl}/auth/login?next=/board`,-- } : {-- silentLogin: `${o.config.baseUrl}/auth/login?silent=1&next={url}`,-- sessionUrl: '/auth/me',-- 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)-- }),++ silentLogin: '/auth/login?silent=1&next={url}',++ sessionUrl: '/auth/me',++ loginUrl: '/auth/login?next={path}',++ logoutUrl: '/auth/logout?next={path}',++ notificationsRealtime: true,++};const footer = { service: 'rent', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME, updates: homes ? `${o.config.baseUrl}/updates` : '/updates' };const account = homes@@@ -66,10 +66,20 @@ const housingFetchImpl = async (url) => (new URL(String(url)).pathname.endsWith(require('http').get({ hostname: u.hostname, port: u.port, path: u.pathname + u.search, headers: { Host: 'openvibe.homes' } }, (res) => {const chunks = [];res.on('data', (c) => chunks.push(c));-- res.on('end', () => resolve({ status: res.statusCode, text: Buffer.concat(chunks).toString('utf8') }));++ res.on('end', () => resolve({ status: res.statusCode, headers: res.headers, text: Buffer.concat(chunks).toString('utf8') }));}).on('error', reject);});try {++ await check('Homes owns its OAuth callback and silent session', async () => {++ const started = await homes('/auth/login?silent=1&next=%2Fmortgage');++ assert.strictEqual(started.status, 302);++ const u = new URL(started.headers.location);++ assert.strictEqual(u.searchParams.get('redirect_uri'), 'https://openvibe.homes/auth/callback');++ assert.strictEqual(u.searchParams.get('prompt'), 'none');++ const page = await homes('/');++ assert.ok(page.text.includes('"silentLogin":"/auth/login?silent=1&next={url}"'));++ assert.ok(page.text.includes('"sessionUrl":"/auth/me"'));++ });await check('the Homes host: its own home page and name; Rent\'s host is untouched', async () => {const r = await homes('/');assert.strictEqual(r.status, 200);
@@ -58,6 +58,10 @@ async function createApp(opts = {}) {
@@ -58,6 +58,10 @@ async function createApp(opts = {}) {
const keys = createKeyStore({ config, fetchImpl, log });
const keys = createKeyStore({ config, fetchImpl, log });
const sso = createSso({ config, keys, fetchImpl, now: s.now, log });
const sso = createSso({ config, keys, fetchImpl, now: s.now, log });
+ const homesSso = createSso({ config: { ...config, baseUrl: config.homes.url,
+ oauth: { ...config.oauth, redirectUri: `${config.homes.url}/auth/callback` } }, keys, fetchImpl, now: s.now, log });
+ const homesAuthHost = new URL(config.homes.url).hostname;
+
const principal = createPrincipal({ config, keys });
const principal = createPrincipal({ config, keys });
const ctx = { config, s, keys, sso, principal, log };
const ctx = { config, s, keys, sso, principal, log };
// Listing pages in OpenVibe.Search (./search-index.js): every store write names the listing it touched (s.search),
// Listing pages in OpenVibe.Search (./search-index.js): every store write names the listing it touched (s.search),
@@ -168,7 +172,8 @@ async function createApp(opts = {}) {
@@ -168,7 +172,8 @@ async function createApp(opts = {}) {
// ── Sign-in (OAuth2 + PKCE client of OpenVibe.Network) ──
// ── Sign-in (OAuth2 + PKCE client of OpenVibe.Network) ──
// /auth/me is the navbar's who-is-signed-in read on every page, so it is not counted with sign-ins: 60 page views in 15 minutes made a signed-in visitor look signed out.
// /auth/me is the navbar's who-is-signed-in read on every page, so it is not counted with sign-ins: 60 page views in 15 minutes made a signed-in visitor look signed out.
app.use('/auth/', rateLimit({ windowMs: 15 * 60_000, limit: 60, skip: (req) => req.path === '/me', standardHeaders: true, legacyHeaders: false }));
app.use('/auth/', rateLimit({ windowMs: 15 * 60_000, limit: 60, skip: (req) => req.path === '/me', standardHeaders: true, legacyHeaders: false }));
- app.use('/auth', sso.routes());
+ const rentAuth = sso.routes(), homesAuth = homesSso.routes();
+ app.use('/auth', (req, res, next) => (req.hostname === homesAuthHost ? homesAuth : rentAuth)(req, res, next));
{ const legal = require('openvibe-shared/legal'); app.get(legal.PATHS, legal.handler({ id: 'rent', service: 'rent', host: 'openvibe.rent', name: 'OpenVibe.Rent', profile: 'ugc' })); }
{ const legal = require('openvibe-shared/legal'); app.get(legal.PATHS, legal.handler({ id: 'rent', service: 'rent', host: 'openvibe.rent', name: 'OpenVibe.Rent', profile: 'ugc' })); }
// ── OpenVibe.Events → this service (loopback only) ──────
// ── OpenVibe.Events → this service (loopback only) ──────
@@ -91,16 +91,12 @@ function renderPage(o) {
@@ -91,16 +91,12 @@ function renderPage(o) {
links: NAV.map((l) => ({ label: l.label, href: l.href, ...(l.icon ? { icon: l.icon } : {}), ...(l.more ? { more: true } : {}), active: pathNow === l.href || pathNow.startsWith(`${l.href}/`) })),
links: NAV.map((l) => ({ label: l.label, href: l.href, ...(l.icon ? { icon: l.icon } : {}), ...(l.more ? { more: true } : {}), active: pathNow === l.href || pathNow.startsWith(`${l.href}/`) })),
appBar: true, // phones: the places with icons as the bottom tab bar (Shared 4.20)
appBar: true, // phones: the places with icons as the bottom tab bar (Shared 4.20)
history: { type: 'page', title: o.title || SITE_NAME },
history: { type: 'page', title: o.title || SITE_NAME },
- // On Homes the account lives on OpenVibe.Rent: its session probe answers signed out, and sign-in goes there.
- ...(homes ? {
- loginUrl: `${o.config.baseUrl}/auth/login?next=/board`,
- } : {
- silentLogin: `${o.config.baseUrl}/auth/login?silent=1&next={url}`,
- sessionUrl: '/auth/me',
- 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)
- }),
+ silentLogin: '/auth/login?silent=1&next={url}',
+ sessionUrl: '/auth/me',
+ loginUrl: '/auth/login?next={path}',
+ logoutUrl: '/auth/logout?next={path}',
+ notificationsRealtime: true,
+
};
};
const footer = { service: 'rent', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME, updates: homes ? `${o.config.baseUrl}/updates` : '/updates' };
const footer = { service: 'rent', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME, updates: homes ? `${o.config.baseUrl}/updates` : '/updates' };
const account = homes
const account = homes
@@ -66,10 +66,20 @@ const housingFetchImpl = async (url) => (new URL(String(url)).pathname.endsWith(
@@ -66,10 +66,20 @@ const housingFetchImpl = async (url) => (new URL(String(url)).pathname.endsWith(
require('http').get({ hostname: u.hostname, port: u.port, path: u.pathname + u.search, headers: { Host: 'openvibe.homes' } }, (res) => {
require('http').get({ hostname: u.hostname, port: u.port, path: u.pathname + u.search, headers: { Host: 'openvibe.homes' } }, (res) => {
const chunks = [];
const chunks = [];
res.on('data', (c) => chunks.push(c));
res.on('data', (c) => chunks.push(c));
- res.on('end', () => resolve({ status: res.statusCode, text: Buffer.concat(chunks).toString('utf8') }));
+ res.on('end', () => resolve({ status: res.statusCode, headers: res.headers, text: Buffer.concat(chunks).toString('utf8') }));
}).on('error', reject);
}).on('error', reject);
});
});
try {
try {
+ await check('Homes owns its OAuth callback and silent session', async () => {
+ const started = await homes('/auth/login?silent=1&next=%2Fmortgage');
+ assert.strictEqual(started.status, 302);
+ const u = new URL(started.headers.location);
+ assert.strictEqual(u.searchParams.get('redirect_uri'), 'https://openvibe.homes/auth/callback');
+ assert.strictEqual(u.searchParams.get('prompt'), 'none');
+ const page = await homes('/');
+ assert.ok(page.text.includes('"silentLogin":"/auth/login?silent=1&next={url}"'));
+ assert.ok(page.text.includes('"sessionUrl":"/auth/me"'));
+ });
await check('the Homes host: its own home page and name; Rent\'s host is untouched', async () => {
await check('the Homes host: its own home page and name; Rent\'s host is untouched', async () => {
const r = await homes('/');
const r = await homes('/');
assert.strictEqual(r.status, 200);
assert.strictEqual(r.status, 200);