diff --git a/frontend/src/lib/components/OAuthSetting.svelte b/frontend/src/lib/components/OAuthSetting.svelte index ebdbfb3223..8b16310877 100644 --- a/frontend/src/lib/components/OAuthSetting.svelte +++ b/frontend/src/lib/components/OAuthSetting.svelte @@ -3,7 +3,7 @@ import CollapseLink from './CollapseLink.svelte' import IconedResourceType from './IconedResourceType.svelte' import Toggle from './Toggle.svelte' - import { onMount, untrack } from 'svelte' + import { untrack } from 'svelte' import { enterpriseLicense } from '$lib/stores' import Button from './common/button/Button.svelte' import TextInput from './text_input/TextInput.svelte' @@ -18,61 +18,65 @@ let { name, value = $bindable(), login = true, eeOnly = false }: Props = $props() - let tenant: string = $state('') + // For Microsoft SSO, tenant is embedded in the login_config URLs, so we need + // a local state + bidirectional sync. For Teams, we bind directly to + // value['tenant'] in the template — no intermediate state, no effect loops. + let microsoftTenant: string = $state('') - onMount(() => { - try { - if ( - name == 'microsoft' && - value && - value['login_config'] && - typeof value['login_config']['auth_url'] == 'string' - ) { - tenant = value['login_config']['auth_url'].split('/')[3] + // Sync microsoftTenant from value.login_config whenever value changes. + // Uses untrack for the write to avoid circular dependencies with the + // push effect below. Loop termination is guaranteed because + // URL.split('/')[3] of a URL we constructed with tenant X always yields X. + $effect(() => { + const v = value + untrack(() => { + try { + if (!v) { + if (microsoftTenant !== '') microsoftTenant = '' + return + } + if ( + name == 'microsoft' && + v['login_config'] && + typeof v['login_config']['auth_url'] == 'string' + ) { + const urlTenant = v['login_config']['auth_url'].split('/')[3] + if (urlTenant !== microsoftTenant) { + microsoftTenant = urlTenant + } + } + } catch (e) { + console.error('Could not set tenantId', e) } - if (name === 'teams' && value?.tenant) { - tenant = value.tenant - } - } catch (e) { - console.error('Could not set tenantId', e) - } + }) }) - function changeTenantId(tenant: string) { - if (value && tenant) { - if (tenant != '') { - if (name === 'teams') { - value = { - ...value, - tenant - } - } else { + // Push microsoftTenant edits back into value.login_config URLs. + $effect(() => { + microsoftTenant + if (name === 'microsoft') { + untrack(() => { + if (!value) return + if (microsoftTenant !== '') { value = { ...value, login_config: { - auth_url: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/authorize`, - token_url: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`, + auth_url: `https://login.microsoftonline.com/${microsoftTenant}/oauth2/v2.0/authorize`, + token_url: `https://login.microsoftonline.com/${microsoftTenant}/oauth2/v2.0/token`, userinfo_url: `https://graph.microsoft.com/oidc/userinfo`, scopes: ['openid', 'profile', 'email'] } } + } else { + if (value['login_config']) { + delete value['login_config'] + } } - } else { - if (value['login_config']) { - delete value['login_config'] - } - } - } - } - - let enabled = $derived(value != undefined && !(eeOnly && !$enterpriseLicense)) - - $effect(() => { - tenant - if (name == 'microsoft' || name == 'teams') { - untrack(() => changeTenantId(tenant)) + }) } }) + + let enabled = $derived(value != undefined && !(eeOnly && !$enterpriseLicense))
@@ -85,7 +89,7 @@ disabled={eeOnly && !$enterpriseLicense} on:change={(e) => { if (e.detail) { - if (name === 'teams' || name === 'microsoft') { + if (name === 'teams') { value = { id: '', secret: '', tenant: '' } } else { value = { id: '', secret: '' } @@ -127,7 +131,17 @@ {#if name == 'microsoft' || name == 'teams'} {:else if login}