From 6aff7cf993c06d8e1f7c6f6d9a6e582379c331f1 Mon Sep 17 00:00:00 2001 From: James Markey Date: Sun, 26 Jul 2026 08:48:44 +0100 Subject: [PATCH] Send the sign-in link back to Date Polling, not the suite hub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit signInWithOtp had no emailRedirectTo, so the link in the sign-in email fell back to the shared project's site_url (https://app.unisim.co.uk). A host who tapped it landed on the suite hub with no session here and no explanation. The Supabase project is shared by every product, so its site_url can't be any one product's. This sends origin + Vite's BASE_URL with the trailing slash stripped, which is the exact bare form now carried in the redirect allowlist (universal-platform #108) — a listed entry without a wildcard must match exactly, and '.../polling/' is not '.../polling'. Found by auditing every auth redirect on the project after the same bug was fixed in Universal Family. The typed code path was always unaffected, which is why this survived unnoticed: the working path masked the broken one. Verified: build clean. --- src/lib/api.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/lib/api.ts b/src/lib/api.ts index 7e0a2b8..507c781 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -16,7 +16,19 @@ export function shortId(len = 10): string { export async function sendHostCode(email: string): Promise { const { error } = await supabase.auth.signInWithOtp({ email: email.trim(), - options: { shouldCreateUser: true }, + options: { + shouldCreateUser: true, + // Without this the magic link falls back to the project's global site_url + // (https://app.unisim.co.uk) and a host who taps it lands on the suite hub + // instead of here. The Supabase project is shared, so its site_url can't + // be any one product's. + // + // BASE_URL is Vite's configured base ('/polling/' in production, '/' in dev), and + // the trailing slash is stripped so this sends the exact bare form the + // redirect allowlist carries — a listed entry without a wildcard has to + // match exactly, and '.../polling/' is not '.../polling'. + emailRedirectTo: `${window.location.origin}${import.meta.env.BASE_URL}`.replace(/\/$/, ''), + }, }) if (error) throw error }