"use client"; import { useRouter } from "next/navigation"; import { useCallback, useRef, useState } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { getBrivenAuth } from "@/lib/auth"; import { establishPortalSession } from "@/lib/portal-session"; /** Most Briven email OTPs are 6 digits — verify as soon as that many land. */ const OTP_LEN = 6; /** * Email OTP + Konnos OAuth (magic link off — use Briven Auth Providers). * After OTP success we mint a first-party portal session so the dashboard sticks. */ export function BrivenPasswordlessSignIn({ redirectTo = "/portal/dashboard", }: { redirectTo?: string; }) { const router = useRouter(); const auth = getBrivenAuth(); const [email, setEmail] = useState(""); const [otp, setOtp] = useState(""); const [otpSent, setOtpSent] = useState(false); const [busy, setBusy] = useState(false); const [message, setMessage] = useState(null); const [error, setError] = useState(null); const verifyingRef = useRef(false); const lastTriedRef = useRef(""); const finishLogin = useCallback( async (userId: string, loginEmail: string) => { await establishPortalSession({ userId, email: loginEmail }); window.dispatchEvent(new Event("krypco-auth-change")); router.push(redirectTo); router.refresh(); }, [redirectTo, router], ); const verifyOtp = useCallback( async (code: string) => { if (!auth) return; const cleaned = code.replace(/\s/g, "").trim(); if (cleaned.length < 4) return; if (verifyingRef.current) return; if (lastTriedRef.current === cleaned) return; verifyingRef.current = true; lastTriedRef.current = cleaned; setError(null); setBusy(true); setMessage("Checking code…"); try { const loginEmail = email.trim().toLowerCase(); const r = await auth.signIn.otpVerify({ email: loginEmail, otp: cleaned, }); if (!r.ok) { setError(r.message || r.code); setMessage(null); lastTriedRef.current = ""; return; } const userId = ("userId" in r && typeof r.userId === "string" && r.userId) || `user_${loginEmail}`; setMessage("Signed in — opening dashboard…"); await finishLogin(userId, loginEmail); } catch (e) { setError(e instanceof Error ? e.message : "Could not verify code"); setMessage(null); lastTriedRef.current = ""; } finally { setBusy(false); verifyingRef.current = false; } }, [auth, email, finishLogin], ); if (!auth) { return (

Briven Auth key missing in environment.

); } async function sendOtp() { if (!auth) return; setError(null); setMessage(null); setBusy(true); try { const r = await auth.signIn.otpRequest({ email: email.trim(), redirectTo, }); if (!r.ok) { setError(r.message || r.code); return; } setOtpSent(true); setOtp(""); lastTriedRef.current = ""; setMessage("Check your email for a one-time code — paste it below."); } catch (e) { setError(e instanceof Error ? e.message : "Could not send code"); } finally { setBusy(false); } } function onOtpChange(raw: string) { const digits = raw.replace(/\D/g, "").slice(0, OTP_LEN); setOtp(digits); setError(null); if (digits.length === OTP_LEN) { void verifyOtp(digits); } } function startKonnos() { if (!auth) return; setError(null); const { redirectUrl } = auth.signIn.social({ provider: "konnos", redirectTo: typeof window !== "undefined" ? `${window.location.origin}${redirectTo}` : redirectTo, }); window.location.assign(redirectUrl); } return (
setEmail(e.target.value)} disabled={busy} />
{otpSent ? (
onOtpChange(e.target.value)} disabled={busy} aria-label="Six-digit code from your email" className="mx-auto h-14 w-full max-w-[16rem] text-center font-mono text-2xl tracking-[0.35em] tabular-nums" />

Paste or type all {OTP_LEN} digits — we check the code and open the dashboard automatically.

{busy && otp.length === OTP_LEN ? (

Signing you in…

) : null} {otp.length >= 4 && otp.length < OTP_LEN ? ( ) : null}
) : null}
or
{message ? (

{message}

) : null} {error ? (

{error}

) : null}

Sign in with a one-time email code. Magic links are off. MFA (if enrolled) is handled by Briven after the first factor.

); }