"use client"; import { useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { startAuthentication } from "@simplewebauthn/browser"; import { Fingerprint, KeyRound, Loader2, LogIn } from "lucide-react"; export function LoginForm({ passkeysAvailable, registrationOpen, next, }: { passkeysAvailable: boolean; registrationOpen: boolean; next: string; }) { const router = useRouter(); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [busy, setBusy] = useState<"password" | "passkey" | null>(null); const [error, setError] = useState(null); function done() { // replace statt push: die Anmeldeseite soll nicht im Verlauf landen. router.replace(next); router.refresh(); } async function loginWithPassword(event: React.FormEvent) { event.preventDefault(); setBusy("password"); setError(null); try { const response = await fetch("/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password }), }); const payload = await response.json().catch(() => ({})); if (!response.ok) throw new Error(payload.error ?? "Anmeldung fehlgeschlagen."); done(); } catch (caught) { setError(caught instanceof Error ? caught.message : String(caught)); setBusy(null); } } async function loginWithPasskey() { setBusy("passkey"); setError(null); try { const optionsResponse = await fetch("/api/auth/passkey/login/options", { method: "POST", headers: { "Content-Type": "application/json" }, body: "{}", }); const optionsPayload = await optionsResponse.json().catch(() => ({})); if (!optionsResponse.ok) { throw new Error(optionsPayload.error ?? "Passkey-Anmeldung nicht moeglich."); } const assertion = await startAuthentication({ optionsJSON: optionsPayload.options, }); const verifyResponse = await fetch("/api/auth/passkey/login/verify", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ response: assertion }), }); const verifyPayload = await verifyResponse.json().catch(() => ({})); if (!verifyResponse.ok) { throw new Error(verifyPayload.error ?? "Passkey wurde nicht akzeptiert."); } done(); } catch (caught) { // Bricht der Benutzer den Systemdialog ab, ist das kein Fehler. const message = caught instanceof Error ? caught.message : String(caught); setError(/abort|NotAllowed/i.test(message) ? null : message); setBusy(null); } } return (
{passkeysAvailable ? ( <>
oder
) : null}
setUsername(event.target.value)} autoComplete="username webauthn" autoCapitalize="none" spellCheck={false} required autoFocus />
setPassword(event.target.value)} autoComplete="current-password" required />
{error ? (

{error}

) : null}
{registrationOpen ? (

Noch kein Konto?{" "} Registrieren

) : null} {!passkeysAvailable ? (

Passkeys sind deaktiviert: Sie brauchen einen sicheren Kontext. Dafuer muss APP_URL auf eine https-Adresse oder auf http://localhost zeigen.

) : null}
); }