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'
import { requestApi } from '../src/utils.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
}