"use client"; import { useState } from "react"; import { useSearchParams } from "next/navigation"; export function LoginForm() { const params = useSearchParams(); // Only allow same-origin relative paths, so a crafted ?next=https://evil or ?next=//evil // can't turn login into an open redirect. const raw = params.get("next") || "/"; const next = raw.startsWith("/") && !raw.startsWith("//") ? raw : "/"; const [password, setPassword] = useState(""); const [error, setError] = useState(""); const [busy, setBusy] = useState(false); async function submit(e: React.FormEvent) { e.preventDefault(); setBusy(true); setError(""); try { const res = await fetch("/api/auth", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ password }), }); if (res.ok) { // full navigation so middleware re-evaluates with the new cookie window.location.assign(next); return; } const data = (await res.json().catch(() => ({}))) as { error?: string }; setError(data.error || "Login failed"); setBusy(false); } catch { setError("Login failed"); setBusy(false); } } return (
{error ?

{error}

: null}
); }