'use client' import { useEffect, useState } from 'react' import { useRouter } from 'next/navigation' import { AlertCircle, CheckCircle2, Fingerprint, Loader2, Plus, Smartphone, Trash2, } from 'lucide-react' import { passkeyEinrichtungAbschliessen, passkeyEinrichtungStarten, passkeyEntfernen, } from '@/server/actions/passkey' import { browserSupportsWebAuthn, passkeyBrowserfehler, startRegistration, } from '@/lib/passkey-browser' import type { PasskeyUebersicht } from '@/lib/auth/passkey' import { Alert, AlertDescription } from '@/components/ui/alert' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Feld } from '@/components/feld' import { formatDatum, formatDatumZeit } from '@/lib/utils' /** * Verwaltung der eigenen Passkeys unter "Mein Konto". * * Die Liste kommt vom Server; nach jeder Aenderung wird die Seite neu * geladen (router.refresh) statt den Zustand hier mitzufuehren. Bei einer * Handvoll Eintraegen ist das die ehrlichere Loesung: Was auf dem Bildschirm * steht, steht auch so in der Datenbank. */ export function PasskeyVerwaltung({ passkeys, zweiterFaktorAktiv, moeglich, }: { passkeys: PasskeyUebersicht[] zweiterFaktorAktiv: boolean /** Ob der Server Passkeys ueberhaupt anbieten kann (https). */ moeglich: boolean }) { const router = useRouter() const [browserKann, setBrowserKann] = useState(false) const [name, setName] = useState('') const [laeuft, setLaeuft] = useState(false) const [entfernt, setEntfernt] = useState(null) const [fehler, setFehler] = useState(null) const [erfolg, setErfolg] = useState(null) const [nachfrage, setNachfrage] = useState(null) useEffect(() => { setBrowserKann(browserSupportsWebAuthn()) }, []) async function einrichten() { setFehler(null) setErfolg(null) if (name.trim() === '') { setFehler('Bitte gib dem Gerät zuerst einen Namen.') return } setLaeuft(true) try { const start = await passkeyEinrichtungStarten() if (!start.ok) { setFehler(start.fehler) return } const antwort = await startRegistration({ optionsJSON: start.optionen }) const ergebnis = await passkeyEinrichtungAbschliessen(name, JSON.stringify(antwort)) if (!ergebnis.ok) { setFehler(ergebnis.fehler) return } setName('') setErfolg(ergebnis.meldung ?? 'Passkey eingerichtet.') router.refresh() } catch (ausnahme) { setFehler(passkeyBrowserfehler(ausnahme, true)) } finally { setLaeuft(false) } } async function entfernen(id: string) { setFehler(null) setErfolg(null) setNachfrage(null) setEntfernt(id) try { const ergebnis = await passkeyEntfernen(id) if (!ergebnis.ok) { setFehler(ergebnis.fehler) return } setErfolg(ergebnis.meldung ?? 'Passkey entfernt.') router.refresh() } finally { setEntfernt(null) } } if (!moeglich) { return ( ) } if (!zweiterFaktorAktiv) { return ( ) } return (
{fehler && ( )} {erfolg && ( )} {passkeys.length === 0 ? (

Noch kein Passkey eingerichtet. Du meldest dich weiterhin mit E-Mail, Passwort und Code an — das bleibt auch danach möglich.

) : (
    {passkeys.map((passkey) => (
  • ))}
)} {browserKann ? (
setName(ereignis.target.value)} maxLength={60} placeholder="iPhone" autoComplete="off" />
) : ( )}
) }