import React, { FC } from "react"; import { WebAuthnCreationOptions } from "../types"; import { decode, encodeUrl } from "../util/base64"; import WebAuthnButton, { WebAuthnButtonProps } from "./WebAuthnButton"; type CredentialForCreate = { id: string; type: string; rawId: ArrayBuffer; response: { attestationObject: ArrayBuffer; clientDataJSON: ArrayBuffer; }; }; async function createCredential( creationOptions: WebAuthnCreationOptions ): Promise { const credential = await navigator.credentials.create({ publicKey: { attestation: creationOptions?.attestation || undefined, authenticatorSelection: { authenticatorAttachment: creationOptions?.authenticatorSelection?.authenticatorAttachment || undefined, requireResidentKey: creationOptions?.authenticatorSelection?.requireResidentKey || undefined, residentKey: creationOptions?.authenticatorSelection?.residentKey, userVerification: creationOptions?.authenticatorSelection?.userVerification || undefined, }, challenge: decode(creationOptions?.challenge), pubKeyCredParams: creationOptions?.pubKeyCredParams?.map((p) => ({ type: p.type, alg: p.alg, })), rp: { name: creationOptions?.rp?.name, id: creationOptions?.rp?.id, }, timeout: creationOptions?.timeout, user: { id: decode(creationOptions?.user?.id), name: creationOptions?.user?.name, displayName: creationOptions?.user?.displayName, }, }, }); return credential as CredentialForCreate; } export type WebAuthnRegisterButtonProps = Omit< WebAuthnButtonProps, "action" | "enable" | "onClick" | "text" > & { creationOptions: WebAuthnCreationOptions; csrfToken: string; url: string; }; export const WebAuthnRegisterButton: FC = ({ creationOptions, ...props }) => { async function register(): Promise { const credential = await createCredential(creationOptions); return { id: credential.id, type: credential.type, rawId: encodeUrl(credential.rawId), response: { attestationObject: encodeUrl(credential.response.attestationObject), clientDataJSON: encodeUrl(credential.response.clientDataJSON), }, }; } return ( ); }; export default WebAuthnRegisterButton;