From 38096db2dd1bfcd14e222f188989b4c5f7c56b5c Mon Sep 17 00:00:00 2001 From: "Prad Nukala (aider)" Date: Mon, 9 Dec 2024 15:21:18 -0500 Subject: [PATCH] fix: Improve cross-browser passkey credential handling and encoding --- pkg/blocks/forms/register_passkey.templ | 51 +++++++++++++++++++------ 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/pkg/blocks/forms/register_passkey.templ b/pkg/blocks/forms/register_passkey.templ index 7681e8ad2..f19ba1209 100644 --- a/pkg/blocks/forms/register_passkey.templ +++ b/pkg/blocks/forms/register_passkey.templ @@ -77,27 +77,54 @@ const publicKey = { }, }, }; +// Helper function to convert ArrayBuffer to Base64URL string +function arrayBufferToBase64URL(buffer) { + const bytes = new Uint8Array(buffer); + let str = ''; + bytes.forEach(byte => { str += String.fromCharCode(byte) }); + return btoa(str) + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=/g, ''); +} + navigator.credentials .create({ publicKey }) .then((newCredentialInfo) => { - // Convert credential to base64 string - // Convert the credential data to a proper format - const credentialJSON = JSON.stringify({ - id: base64URLEncode(newCredentialInfo.rawId), + if (!(newCredentialInfo instanceof PublicKeyCredential)) { + throw new Error('Received credential is not a PublicKeyCredential'); + } + + const response = newCredentialInfo.response; + if (!(response instanceof AuthenticatorAttestationResponse)) { + throw new Error('Response is not an AuthenticatorAttestationResponse'); + } + + // Convert the credential data to a cross-platform compatible format + const credentialJSON = { + id: newCredentialInfo.id, + rawId: arrayBufferToBase64URL(newCredentialInfo.rawId), type: newCredentialInfo.type, - authenticatorAttachment: newCredentialInfo.authenticatorAttachment, + authenticatorAttachment: newCredentialInfo.authenticatorAttachment || null, + transports: Array.isArray(response.getTransports) ? response.getTransports() : [], clientExtensionResults: newCredentialInfo.getClientExtensionResults(), response: { - attestationObject: base64URLEncode(newCredentialInfo.response.attestationObject), - clientDataJSON: base64URLEncode(newCredentialInfo.response.clientDataJSON) + attestationObject: arrayBufferToBase64URL(response.attestationObject), + clientDataJSON: arrayBufferToBase64URL(response.clientDataJSON) } - }); - document.getElementById('credential-data').value = btoa(credentialJSON); - document.getElementById('passkey-form').submit(); + }; + + // Set the form value with the stringified credential data + const credentialInput = document.getElementById('credential-data'); + credentialInput.value = JSON.stringify(credentialJSON); + + // Submit the form + const form = document.getElementById('passkey-form'); + form.submit(); }) .catch((err) => { - console.error(err); - alert('Failed to create passkey. Please try again.'); + console.error('Passkey creation failed:', err); + alert(`Failed to create passkey: ${err.message || 'Unknown error'}`); }); }