import { createFileRoute, Link, useNavigate } from "@tanstack/react-router"; import { redirectSignedIn } from "~/lib/route-guards"; import { returnSearch, safeReturnPath } from "~/lib/return-to"; import { getAuthOptions, signIn } from "~/fns/auth"; import { finishPasskeyLogin, startPasskeyLogin } from "~/fns/login"; import { useAction } from "~/hooks/use-action"; import { useTurnstile } from "~/hooks/use-turnstile"; import { usePasskeyAssertion } from "~/hooks/use-passkey"; import { AuthCard } from "~/components/AuthCard"; import { SignupConsent } from "~/components/SignupConsent"; import { Button, buttonClass } from "~/ui/Button"; import { TextField } from "~/ui/Field"; import { ErrorBanner } from "~/ui/Callout"; import { TextLink } from "~/ui/TextLink"; export const Route = createFileRoute("/login")({ validateSearch: returnSearch, beforeLoad: redirectSignedIn, loader: () => getAuthOptions(), component: LoginPage, }); function LoginPage() { const options = Route.useLoaderData(); // Validated again here: router search state can still carry the raw value during SSR. const next = safeReturnPath(Route.useSearch().next) ?? undefined; const navigate = useNavigate(); const action = useAction(signIn); const turnstile = useTurnstile(options.turnstileSiteKey, "login"); const passkey = usePasskeyAssertion(startPasskeyLogin, finishPasskeyLogin); async function submit(event: React.SubmitEvent) { event.preventDefault(); const form = new FormData(event.currentTarget); const result = await action.run({ data: { login: String(form.get("login") ?? ""), password: String(form.get("password") ?? ""), turnstileToken: turnstile.token, }, }); if (result?.twoFactor) await navigate({ to: "/login/two-factor", search: { next } }); else if (result) await navigate({ href: next ?? "/" }); else turnstile.reset(); } async function signInWithPasskey() { if (await passkey.run(undefined)) await navigate({ href: next ?? "/", reloadDocument: true }); } return (
Forgot password?
{turnstile.widget} {turnstile.failed && (

The verification check failed to load. Reload the page to try again.

)} {options.githubEnabled && ( Continue with GitHub )} {options.githubEnabled && options.signupsEnabled && ( )} {options.signupsEnabled && (

New here?{" "} Create an account

)}
); }