From a90572bcb8d6f3a7830ba82858cb38a0766445da Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Sat, 23 May 2026 11:31:10 -0400 Subject: [PATCH] fix(idaa): ensure breakout links preserve site access key and uuid Proactively re-injects 'key' (site access key) and 'uuid' (Novi token) into 'Open Externally' and 'Copy Link' URLs on the Video Conferences page. This prevents authentication failures when members open meetings in a new browser tab after SvelteKit internal navigation has dropped the bootstrap parameters. Updated CLIENT__IDAA_and_customized_mods.md to document the requirement for these keys in breakout URLs. --- .../CLIENT__IDAA_and_customized_mods.md | 11 ++++ .../(idaa)/video_conferences/+page.svelte | 50 +++++++++++++++++-- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/documentation/CLIENT__IDAA_and_customized_mods.md b/documentation/CLIENT__IDAA_and_customized_mods.md index c9c57c3c..4290a9fb 100644 --- a/documentation/CLIENT__IDAA_and_customized_mods.md +++ b/documentation/CLIENT__IDAA_and_customized_mods.md @@ -31,6 +31,17 @@ IDAA is a private membership organization for physicians in recovery. They use t IDAA's Aether instance is embedded as an **iframe inside their existing Novi-powered website** (`idaa.org`). Novi is their external Association Management System (AMS) — it handles membership records and authentication. Aether receives the member context via URL parameters on iframe load. +### Breakout Links and Iframe Persistence + +Members often need to open Jitsi meetings outside the Novi iframe (e.g., for full-screen features or on mobile). These are referred to as **Breakout Links**. + +- **The Problem:** SvelteKit client-side navigation within the iframe often drops "bootstrap" query parameters like `?key=...` (site access key) and `?uuid=...` (Novi identity token). +- **The Requirement:** When a member breaks out of the iframe into a new browser tab, these keys **must** be present in the URL. Without them, the member will hit the site-domain gate or the IDAA auth gate and see "Access Denied." +- **The Solution:** The Video Conferences page uses a derived `breakout_url` that proactively re-injects the missing `key` (from `$ae_loc.allow_access`) and `uuid` (from `$idaa_loc.novi_uuid`) before generating the external link. + +**Example Breakout URL:** +`https://client.oneskyit.com/idaa/video_conferences?uuid=...&key=...&room=...` + --- ## Architecture: Composite Module diff --git a/src/routes/idaa/(idaa)/video_conferences/+page.svelte b/src/routes/idaa/(idaa)/video_conferences/+page.svelte index 542d83d1..dcb45dc3 100644 --- a/src/routes/idaa/(idaa)/video_conferences/+page.svelte +++ b/src/routes/idaa/(idaa)/video_conferences/+page.svelte @@ -57,17 +57,57 @@ let duration_timer_id: any = $state(null); let show_breakout_modal: boolean = $state(false); let breakout_link_copied: boolean = $state(false); +const breakout_url = $derived.by(() => { + const url = new URL($page.url.href); + + // Proactively inject site access key if missing or empty. + // WHY: SvelteKit internal navigation drops bootstrap params (key, uuid). When a member + // "breaks out" of the Novi iframe, they need these keys in the URL to satisfy the + // site-domain gate and the IDAA Novi auth gate in a fresh tab. + if (!url.searchParams.get('key')) { + const access_key = $ae_loc.allow_access; + if (typeof access_key === 'string' && access_key) { + url.searchParams.set('key', access_key); + } else if ($ae_loc.site_access_key) { + url.searchParams.set('key', $ae_loc.site_access_key); + } + } + + // Proactively inject Novi UUID if missing or empty (ensures auth persistence). + if (!url.searchParams.get('uuid') && $idaa_loc.novi_uuid) { + url.searchParams.set('uuid', $idaa_loc.novi_uuid); + } + + return url.href; +}); + function copy_meeting_link() { - navigator.clipboard.writeText($page.url.href).then(() => { + navigator.clipboard.writeText(breakout_url).then(() => { breakout_link_copied = true; setTimeout(() => (breakout_link_copied = false), 2000); }); } function build_jitsi_reports_href() { - const url = new URL($page.url); + const url = new URL($page.url.href); url.pathname = '/idaa/jitsi_reports'; url.searchParams.delete('room'); + + // Proactively inject site access key if missing or empty + if (!url.searchParams.get('key')) { + const access_key = $ae_loc.allow_access; + if (typeof access_key === 'string' && access_key) { + url.searchParams.set('key', access_key); + } else if ($ae_loc.site_access_key) { + url.searchParams.set('key', $ae_loc.site_access_key); + } + } + + // Proactively inject Novi UUID if missing or empty + if (!url.searchParams.get('uuid') && $idaa_loc.novi_uuid) { + url.searchParams.set('uuid', $idaa_loc.novi_uuid); + } + return `${url.pathname}${url.search}${url.hash}`; } @@ -1172,7 +1212,7 @@ async function init_jitsi() { mt-2 flex w-full flex-col flex-wrap items-center justify-center gap-1 border-t-2 border-dashed border-gray-400 pt-2 sm:flex-row"> (show_breakout_modal = false)} @@ -1327,7 +1367,7 @@ async function init_jitsi() {