'use client' import { useState, useEffect } from 'react' import { useAllauthAPI } from '../../contexts/APIContext' import { useConfig } from '../../contexts/AuthContext' import { useStyles } from '../../contexts/StylesContext' import { SettingsSection, SettingsItem, SettingsList, Button } from './SettingsComponents' import type { Authenticator, WebAuthnAuthenticator } from '../../types' export function PasskeysSection() { const api = useAllauthAPI() const config = useConfig() const styles = useStyles() const [passkeys, setPasskeys] = useState([]) const [loading, setLoading] = useState(true) // Check if passkey login is enabled const passkeyLoginEnabled = config?.data?.mfa?.passkey_login_enabled const fetchPasskeys = async () => { try { const res = await api.mfa.list() if (res.status === 200 && res.data) { const authenticators = res.data as Authenticator[] setPasskeys(authenticators.filter((a): a is WebAuthnAuthenticator => a.type === 'webauthn')) } } catch { // Silently fail - passkeys just won't show } setLoading(false) } useEffect(() => { fetchPasskeys() }, []) // Don't render if passkey login isn't enabled if (!passkeyLoginEnabled) return null if (loading) return null const handleAdd = async () => { try { const { startRegistration } = await import('@simplewebauthn/browser') // Request creation options - use passwordless=true for login passkeys const optionsRes = await api.webauthn.requestOptions.creation(true) if (optionsRes.status !== 200) { return } const publicKeyOptions = optionsRes.data?.creation_options?.publicKey if (!publicKeyOptions) throw new Error('Invalid options response') // @simplewebauthn/browser v13+ expects { optionsJSON: ... } const credential = await startRegistration({ optionsJSON: publicKeyOptions as any }) const name = prompt('Name this passkey:') || 'Passkey' const res = await api.webauthn.add(name, credential) if (res.status === 200) { fetchPasskeys() } } catch (e: any) { if (e.name !== 'AbortError') { alert(e.message || 'Failed to add passkey') } } } const handleRemove = async (id: number) => { if (!confirm('Remove this passkey? You won\'t be able to use it to sign in anymore.')) return await api.webauthn.delete([id]) fetchPasskeys() } return (

Passkeys let you sign in quickly using your device's biometrics or security key. No password needed.

{passkeys.length > 0 && ( {passkeys.map(passkey => ( handleRemove(passkey.id)}> Remove } /> ))} )}
) }