import { requestApi } from ':src/utils.ts' import { startAuthentication } from '@simplewebauthn/browser' import { PublicKeyCredentialRequestOptionsJSON } from '@simplewebauthn/types' import { Button, Input } from 'univoq' import type { WebAuthnLoginFinishPayload, WebAuthnLoginStartPayload, } from '../routes/api/webauthn/login/[step].ts' export default function LoginForm() { return (
) } type LoginFormFields = { email: string passkey: 'on' | undefined } async function connect(event: Event) { if (!(event instanceof SubmitEvent)) return false event.preventDefault() const form = event.target as HTMLFormElement const fields = formJSON(form) try { // User disable passkey if (fields.passkey !== 'on') { throw new Error('User refused passkey') } // Try PassKey connection if (!isWebAuthnSupported()) { throw new Error('WebAuthn is not supported by your browser') } await webAuthnLogin(fields) // Reload UI location.reload() } catch (error) { // Check is user stop passkey connection if (error instanceof Error && error.name === 'NotAllowedError') { form.reset() return } // Else use magic link await magicLinkLogin(fields) // Reload UI location.reload() } form.reset() } async function magicLinkLogin(fields: LoginFormFields) { await requestApi('magiclink', 'POST', fields) console.log('Un lien de connection vous a été envoyé par mail !') } async function webAuthnLogin(fields: LoginFormFields) { // Send WebAuthn authentication options const authenticationOptions = await requestApi< WebAuthnLoginStartPayload, PublicKeyCredentialRequestOptionsJSON >('webauthn/login/start', 'POST', fields) // Pass the options to the authenticator and wait for a response const authentication = await startAuthentication(authenticationOptions) // Verify authentication const verification = await requestApi< WebAuthnLoginFinishPayload, { verified: boolean } >('webauthn/login/finish', 'POST', authentication) // Show UI appropriate for the `verified` status if (verification.verified) { console.log('Success!') } else { console.error('PassKey was not verified!') } } function isWebAuthnSupported(): boolean { return 'credentials' in navigator } function formJSON>(form: HTMLFormElement): T { const formData = new FormData(form) return Object.fromEntries(formData) as unknown as T }