From 7a9953f7034758293ed531d100d173c9b0b27a55 Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Mon, 7 Sep 2026 00:27:58 +0000 Subject: [PATCH] Admin UI: single origins list with automatic in-domain/ROR split + well-known check --- frontend/auth/admin/AdminApp.vue | 32 +++-- frontend/src/admin/AdminDialogs.vue | 205 ++++++++++++++++------------ 2 files changed, 132 insertions(+), 105 deletions(-) diff --git a/frontend/auth/admin/AdminApp.vue b/frontend/auth/admin/AdminApp.vue index c979a0f..e3472f8 100644 --- a/frontend/auth/admin/AdminApp.vue +++ b/frontend/auth/admin/AdminApp.vue @@ -483,16 +483,16 @@ function createRealm() { auth_host: '', origins: [], originValidation: [], - related_origins: [], - relatedValidation: [], + wellKnownCheck: null, authHostValidation: null, }) } function openRealm(realm) { - // Strip https:// scheme from stored origins and auth_host for editing - const origins = (realm.origins || []).map(o => o.replace(/^https:\/\//, '')) - const related = (realm.related_origins || []).map(o => o.replace(/^https:\/\//, '')) + // One combined list for editing: in-domain sites and related origins, + // classified by hostname. Strip https:// scheme for editing. + const origins = [...(realm.origins || []), ...(realm.related_origins || [])] + .map(o => o.replace(/^https:\/\//, '')) openDialog('realm-edit', { isNew: false, rp_id: realm.rp_id, @@ -500,8 +500,7 @@ function openRealm(realm) { auth_host: (realm.auth_host || '').replace(/^https:\/\//, ''), origins, originValidation: origins.map(() => null), - related_origins: related, - relatedValidation: related.map(() => null), + wellKnownCheck: null, authHostValidation: null, }) } @@ -944,13 +943,18 @@ async function submitDialog() { if (!rp_id) throw new Error('Domain (rp-id) required') const rp_name = d.rp_name?.trim() || '' const auth_host = d.auth_host?.trim() || '' - // Origins are stored as-is (hostnames); backend normalizes with https:// - const origins = (d.origins || []) - .map(o => o.trim()) - .filter(o => o) - const related_origins = (d.related_origins || []) - .map(o => o.trim()) - .filter(o => o) + // The combined origins list is split by hostname: entries on the + // rp-id domain form the in-domain allow-list, entries elsewhere are + // related origins (ROR). Bare hostnames are sent as-is; the backend + // normalizes them with https://. + const origins = [] + const related_origins = [] + for (const o of (d.origins || []).map(o => o.trim()).filter(o => o)) { + let hn = null + try { hn = new URL(o.startsWith('http') ? o : 'https://' + o).hostname } catch { continue } + if (hn === rp_id || hn.endsWith('.' + rp_id)) origins.push(o) + else related_origins.push(o) + } closeDialog() const req = d.isNew diff --git a/frontend/src/admin/AdminDialogs.vue b/frontend/src/admin/AdminDialogs.vue index 891a21e..cbfd47a 100644 --- a/frontend/src/admin/AdminDialogs.vue +++ b/frontend/src/admin/AdminDialogs.vue @@ -1,5 +1,5 @@