diff --git a/src/components/portal/login-flow.tsx b/src/components/portal/login-flow.tsx index 6186507..16094f0 100644 --- a/src/components/portal/login-flow.tsx +++ b/src/components/portal/login-flow.tsx @@ -9,7 +9,7 @@ import { PasswordStrength, } from "./bits"; import { lookupAccount, maskEmail, type Account } from "./data"; -import { useAuth } from "@abe-kap/appshell-sdk/react"; +import { useAuth, useAppShell } from "@abe-kap/appshell-sdk/react"; import { isShellConfigured } from "@/lib/appshell"; // Map the portal's social button ids to Supabase OAuth provider ids. @@ -25,6 +25,7 @@ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; export function LoginFlow() { const router = useRouter(); const { login, loginWithOAuth, completeOAuthLogin } = useAuth(); + const { ready } = useAppShell(); const [step, setStep] = useState("identify"); const [email, setEmail] = useState(""); const [account, setAccount] = useState(null); @@ -70,11 +71,22 @@ export function LoginFlow() { if (!isShellConfigured()) return; const code = new URLSearchParams(window.location.search).get("code"); if (!code) return; + // Wait until the SDK has booted — completeOAuthLogin no-ops (returns null) if + // sdk.auth isn't ready yet, and this effect only re-runs when `ready` flips. + if (!ready) { setStep("connecting"); return; } setStep("connecting"); completeOAuthLogin(code) - .then((ace) => { if (ace) router.replace("/dashboard"); else replace("identify"); }) + .then((ace) => { + if (ace) { + // Clear the ?code=… from the URL, then enter the app. + window.history.replaceState({}, "", "/portal/login"); + router.replace("/dashboard"); + } else { + replace("identify"); + } + }) .catch(() => { setFlash("Sign-in with that provider didn't complete. Try again."); replace("identify"); }); - }, [completeOAuthLogin, router]); + }, [ready, completeOAuthLogin, router]); /* ---- auth resolution ---- */ function afterAuth(factor: "password" | "passkey" | "otp" | "totp" | "push" | "social") {