"use client"; import { authClient, useSession } from "@/lib/auth-client"; import { trpc } from "@/lib/trpc"; import { useRouter } from "next/navigation"; import { useMemo, useState } from "react"; export default function TwoFactorSetupPage() { const router = useRouter(); const { data: session } = useSession(); const securityQuery = trpc.security.status.useQuery(); const [password, setPassword] = useState(""); const [otpCode, setOtpCode] = useState(""); const [statusMessage, setStatusMessage] = useState(null); const [errorMessage, setErrorMessage] = useState(null); const [totpUri, setTotpUri] = useState(null); const [backupCodes, setBackupCodes] = useState([]); const [loading, setLoading] = useState(false); const qrCodeUrl = useMemo(() => { if (!totpUri) return null; const sanitized = totpUri.trim(); return `https://api.qrserver.com/v1/create-qr-code/?size=320x320&margin=16&data=${encodeURIComponent( sanitized, )}`; }, [totpUri]); const manualSecret = useMemo(() => { if (!totpUri) return null; const match = totpUri.match(/[?&]secret=([^&]+)/i); if (!match?.[1]) return null; try { return decodeURIComponent(match[1]); } catch { return match[1]; } }, [totpUri]); const requiresSetup = securityQuery.data?.requiresTwoFactorSetup ?? false; async function handleEnableTwoFactor() { setErrorMessage(null); setStatusMessage(null); setLoading(true); try { const result = (await authClient.twoFactor.enable({ password, issuer: "Hellth Hub", })) as unknown as { error?: { message?: string }; data?: { totpURI?: string; backupCodes?: string[] }; totpURI?: string; backupCodes?: string[]; }; if (result?.error) { setErrorMessage(result.error.message ?? "2FA konnte nicht aktiviert werden."); return; } const data = result.data ?? result; setTotpUri(data.totpURI ?? null); setBackupCodes(data.backupCodes ?? []); setStatusMessage( "OTP vorbereitet. Bitte QR-Code scannen und danach den 6-stelligen Code bestätigen.", ); setPassword(""); await securityQuery.refetch(); } catch { setErrorMessage("2FA konnte derzeit nicht aktiviert werden."); } finally { setLoading(false); } } async function handleVerifyTotp() { setErrorMessage(null); setStatusMessage(null); setLoading(true); try { if (!totpUri) { setErrorMessage( "OTP ist noch nicht vorbereitet. Bitte zuerst in Schritt 1 mit deinem Passwort aktivieren.", ); return; } const result = (await authClient.twoFactor.verifyTotp({ code: otpCode.trim(), })) as unknown as { error?: { message?: string }; }; if (result?.error) { const message = result.error.message ?? "OTP-Code ist ungültig."; if (message.toLowerCase().includes("totp not enabled")) { setErrorMessage( "TOTP ist noch nicht aktiviert. Bitte Schritt 1 (OTP aktivieren) ausführen.", ); return; } setErrorMessage(message); return; } setStatusMessage("2FA erfolgreich aktiviert."); setOtpCode(""); const refreshed = await securityQuery.refetch(); if (!refreshed.data?.requiresTwoFactorSetup) { router.push("/"); } } catch { setErrorMessage("OTP-Code konnte nicht verifiziert werden."); } finally { setLoading(false); } } return (

Sicherheits-Setup

Für Admin-Accounts ist 2FA verpflichtend. Bitte richte jetzt OTP (Authenticator-App) ein.

1. OTP aktivieren

Gib dein aktuelles Passwort ein, um die OTP-Einrichtung zu starten.

setPassword(event.target.value)} />

2. QR-Code scannen

{!qrCodeUrl && (

Nach der Aktivierung erscheint hier der QR-Code für deine Authenticator-App.

)} {qrCodeUrl && (
OTP QR Code

{totpUri}

{manualSecret && (
Falls Scannen nicht klappt, manuell eingeben:{" "} {manualSecret}
)}
)}

3. Backup-Codes sichern

{backupCodes.length === 0 ? (

Nach Aktivierung werden hier Backup-Codes angezeigt.

) : (
{backupCodes.map((code) => ( {code} ))}
)}

4. OTP-Code bestätigen

Bitte den aktuellen 6-stelligen Code aus deiner Authenticator-App eingeben.

setOtpCode(event.target.value.replace(/\D/g, "").slice(0, 6)) } />

Passkey Hinweis

Passkey ist jetzt verfügbar. Du kannst in den Einstellungen unter Sicherheit einen Passkey hinterlegen. Für Admin-Accounts bleibt OTP weiterhin verpflichtend.

{statusMessage && (
{statusMessage}
)} {errorMessage && (
{errorMessage}
)}
{!session && (
Keine aktive Session gefunden. Bitte erneut einloggen.
)}
); }