feat(ux): allow user to name passkey at register

This commit is contained in:
Julien Oculi 2024-06-19 10:34:43 +02:00
parent ee69d545b5
commit b01bbfdb5b

View file

@ -1,6 +1,6 @@
import { startRegistration } from '@simplewebauthn/browser' import { startRegistration } from '@simplewebauthn/browser'
import { PublicKeyCredentialCreationOptionsJSON } from '@simplewebauthn/types' import { PublicKeyCredentialCreationOptionsJSON } from '@simplewebauthn/types'
import { Button } from 'univoq' import { Button, Input } from 'univoq'
import type { import type {
WebAuthnRegisterFinishPayload, WebAuthnRegisterFinishPayload,
WebAuthnRegisterStartPayload, WebAuthnRegisterStartPayload,
@ -11,40 +11,50 @@ function isWebAuthnSupported(): boolean {
return 'credentials' in navigator return 'credentials' in navigator
} }
function RegisterButton({ disabled }: { disabled?: boolean }) { function RegisterForm({ disabled }: { disabled?: boolean }) {
return ( return (
<form onSubmit={register} method='POST' action=''>
<Input label='Nom de la clé' name='name' required></Input>
<Button <Button
label='Enregistrer une PassKey' label='Enregistrer une PassKey'
variant='primary' variant='primary'
onClick={register}
disabled={disabled} disabled={disabled}
> >
Enregistrer une PassKey Enregistrer une PassKey
</Button> </Button>
</form>
) )
} }
export default function PassKeyRegister() { export default function PassKeyRegister() {
if (!isWebAuthnSupported()) { if (!isWebAuthnSupported()) {
return ( return (
<label> <div>
<span>Erreur: WebAuthn n'est pas supporté par votre navigateur</span> <span>Erreur: WebAuthn n'est pas supporté par votre navigateur</span>
<RegisterButton disabled /> <RegisterForm disabled />
</label> </div>
) )
} }
return ( return (
<label> <div>
<span>Enregistrer une PassKey pour cet appareil</span> <span>Enregistrer une PassKey pour cet appareil</span>
<RegisterButton /> <RegisterForm />
</label> </div>
) )
} }
async function register() { type RegisterFormFields = {
name: string
}
async function register(event: Event) {
if (!(event instanceof SubmitEvent)) return false
event.preventDefault()
const form = event.target as HTMLFormElement
const fields = formJSON<RegisterFormFields>(form)
try { try {
await webAuthnRegister() await webAuthnRegister(fields)
} catch (cause) { } catch (cause) {
console.error( console.error(
new Error('passkey register failed', { new Error('passkey register failed', {
@ -54,7 +64,7 @@ async function register() {
} }
} }
async function webAuthnRegister() { async function webAuthnRegister(fields: RegisterFormFields) {
if (localStorage.getItem('webauthn-registered')) { if (localStorage.getItem('webauthn-registered')) {
return return
} }
@ -62,7 +72,7 @@ async function webAuthnRegister() {
const registrationOptions = await requestApi< const registrationOptions = await requestApi<
WebAuthnRegisterStartPayload, WebAuthnRegisterStartPayload,
PublicKeyCredentialCreationOptionsJSON PublicKeyCredentialCreationOptionsJSON
>('webauthn/register/start', 'POST') >('webauthn/register/start', 'POST', fields)
// Pass the options to the authenticator and wait for a response // Pass the options to the authenticator and wait for a response
const registration = await startRegistration(registrationOptions) const registration = await startRegistration(registrationOptions)
@ -92,3 +102,8 @@ async function webAuthnRegister() {
console.error('Oh no, something went wrong! Response:', error) console.error('Oh no, something went wrong! Response:', error)
} }
} }
function formJSON<T extends Record<string, unknown>>(form: HTMLFormElement): T {
const formData = new FormData(form)
return Object.fromEntries(formData) as unknown as T
}