fix(idaa): upgrade Novi UUID verification to server-side API call

Previously, IDAA iframe access relied on trusting URL params (uuid, email,
full_name) passed from Novi — any 36-char string granted authenticated access
with no actual verification.

The (idaa)/+layout.svelte now performs an async Novi API call on every UUID
load to verify the UUID exists, fetches name/email directly from Novi (cannot
be spoofed via URL), and sets $idaa_loc.novi_verified on success.
All-or-nothing: if novi_idaa_api_key is absent or the call fails, access denied.

- ae_idaa_stores.ts: add novi_verified boolean field to idaa_loc
- (idaa)/+layout.svelte: async UUID verification with spinner to prevent
  Access Denied flash; permission upgrade-only strategy preserved
- video_conferences/+page.svelte: skip duplicate Novi member details call if
  layout already verified ($idaa_loc.novi_verified check)
- iframe HTML files: remove browser-side Novi API fetch and email/full_name
  params; pass only uuid; add README/START/STOP/WARNING comments for client
  staff; fix iframe-before-script DOM ordering bug
- documentation: CLIENT__IDAA_and_customized_mods.md updated with full
  verification flow, site_cfg_json fields, permission table, access gate

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-03-09 14:48:49 -04:00
parent 7df887fabd
commit eb0dcb17f8
8 changed files with 2538 additions and 513 deletions

View File

@@ -2,6 +2,7 @@
import { onMount, onDestroy } from 'svelte';
import { page } from '$app/stores';
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
import { idaa_loc } from '$lib/stores/ae_idaa_stores';
import MyClipboard from '$lib/app_components/e_app_clipboard.svelte';
import {
create_ae_obj__activity_log,
@@ -350,14 +351,23 @@
const novi_api_key = $ae_loc.site_cfg_json?.novi_idaa_api_key;
if (novi_api_root_url && novi_api_key) {
const member_details = await get_novi_member_details(user_id, novi_api_root_url, novi_api_key);
if (member_details.display_name) {
display_name = member_details.display_name;
console.log(`Jitsi: Updated display_name from Novi: ${display_name}`);
}
if (member_details.email) {
email = member_details.email;
console.log(`Jitsi: Updated email from Novi: ${email}`);
// If the IDAA layout already verified this UUID, re-use those results rather than
// making a duplicate Novi API call. The layout runs on every IDAA route, so by the
// time onMount fires here it will usually have completed verification already.
if ($idaa_loc.novi_verified && $idaa_loc.novi_email) {
display_name = $idaa_loc.novi_full_name ?? display_name;
email = $idaa_loc.novi_email ?? email;
console.log(`Jitsi: Using layout-verified Novi data for user ${user_id}`);
} else {
const member_details = await get_novi_member_details(user_id, novi_api_root_url, novi_api_key);
if (member_details.display_name) {
display_name = member_details.display_name;
console.log(`Jitsi: Updated display_name from Novi: ${display_name}`);
}
if (member_details.email) {
email = member_details.email;
console.log(`Jitsi: Updated email from Novi: ${email}`);
}
}
const novi_idaa_group_guid_li = $ae_loc.site_cfg_json?.novi_idaa_group_guid_li ?? [];