import { useState } from "react"; import { beginPasskeyRegistration, deletePasskey, finishPasskeyRegistration } from "~/fns/account-security"; import { useAction } from "~/hooks/use-action"; import { usePasskeyRegistration } from "~/hooks/use-passkey"; import { Button } from "~/ui/Button"; import { TextField } from "~/ui/Field"; import { ErrorBanner } from "~/ui/Callout"; import { EmptyState } from "~/ui/EmptyState"; import { Panel } from "~/ui/Panel"; import { RelativeTime } from "~/ui/RelativeTime"; import { RecoveryCodes } from "./RecoveryCodes"; type Passkey = { id: string; name: string; lastUsedAt: number | null; createdAt: number; }; export function PasskeysPanel({ passkeys, hasPassword }: { passkeys: Passkey[]; hasPassword: boolean }) { const register = usePasskeyRegistration(beginPasskeyRegistration, finishPasskeyRegistration); const remove = useAction(deletePasskey); const [removing, setRemoving] = useState(null); const [codes, setCodes] = useState(null); return (

Passkeys sign you in without a password and also count as a second factor. Adding one turns on two-factor checks for password sign-ins.

{codes && } {passkeys.length === 0 ? ( ) : (
    {passkeys.map((key) => (
  • {key.name}
    Added ยท{" "} {key.lastUsedAt ? ( <> used ) : ( "never used" )}
    {removing === key.id && (
    { event.preventDefault(); const password = String(new FormData(event.currentTarget).get("password") ?? ""); if (await remove.run({ data: { id: key.id, password } })) setRemoving(null); }} > {hasPassword && (
    )}
    )}
  • ))}
)}
{ event.preventDefault(); const formElement = event.currentTarget; const form = new FormData(formElement); const name = String(form.get("name") ?? "").trim() || "Passkey"; const result = await register.run({ name, password: String(form.get("password") ?? ""), }); if (result) formElement.reset(); if (result?.recoveryCodes) setCodes(result.recoveryCodes); }} >
{hasPassword && (
)}
); }