diff --git a/hub/admin-web/src/lib/session.ts b/hub/admin-web/src/lib/session.ts index a219994..00b8fea 100644 --- a/hub/admin-web/src/lib/session.ts +++ b/hub/admin-web/src/lib/session.ts @@ -32,9 +32,36 @@ export async function loadSession(): Promise { } } +/** + * Resolve org slug for org-scoped Feishu OAuth (`GET /auth/feishu/:orgSlug`). + * Unscoped `/auth/feishu` is disabled unless allowLegacyFeishuOAuth is on. + */ +export function resolveLoginOrgSlug(): string | null { + const path = window.location.pathname.split('/').filter(Boolean); + if (path[0] === 'admin' && path[1] === 'org' && path[2]) { + return decodeURIComponent(path[2]); + } + const q = new URLSearchParams(window.location.search).get('org'); + if (q && q.trim() !== '') return q.trim(); + + // Alpha Silo public host: .educraft.paradigm-edu.net (or educraft-dev) + const host = window.location.hostname.toLowerCase(); + const m = host.match(/^([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)\.educraft(?:-dev)?\./); + if (m?.[1]) return m[1]; + return null; +} + export function redirectToLogin(): void { - const ret = encodeURIComponent(window.location.pathname + window.location.hash); - window.location.href = `/auth/feishu?returnTo=${ret}`; + const ret = encodeURIComponent( + window.location.pathname + window.location.search + window.location.hash, + ); + const slug = resolveLoginOrgSlug(); + if (slug === null) { + // Need an org slug for scoped OAuth — backend login helper can prompt. + window.location.href = `/admin/login?returnTo=${ret}`; + return; + } + window.location.href = `/auth/feishu/${encodeURIComponent(slug)}?returnTo=${ret}`; } export async function logout(): Promise {