diff --git a/keepassxc-browser/content/passkeys.js b/keepassxc-browser/content/passkeys.js index 8f6ed71..e89f6ad 100644 --- a/keepassxc-browser/content/passkeys.js +++ b/keepassxc-browser/content/passkeys.js @@ -26,6 +26,51 @@ return kpxcStringToArrayBuffer(window.atob(str?.replaceAll('-', '+').replaceAll('_', '/'))); }; + // From ArrayBuffer to URL encoded base64 string + const kpxcArrayBufferToBase64 = function(buf) { + const str = [ ...new Uint8Array(buf) ].map(c => String.fromCharCode(c)).join(''); + return window.btoa(str).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', ''); + }; + + // Returns the PublicKeyCredential as JSON + // See: https://w3c.github.io/webauthn/#dom-publickeycredential-tojson + const kpxcPublicKeyCredentialJson = function (credential, publicKey) { + const clientExtensionResults = credential.getClientExtensionResults(); + const type = credential.type; + const authenticatorAttachment = credential.authenticatorAttachment; + let response; + + if (credential.response instanceof AuthenticatorAttestationResponse) { + const responsePublicKey = credential.response.getPublicKey(); + response = { + clientDataJSON: publicKey.response.clientDataJSON, + authenticatorData: publicKey.response.authenticatorData, + transports: credential.response.getTransports(), + publicKey: responsePublicKey ? kpxcArrayBufferToBase64(responsePublicKey) : null, + publicKeyAlgorithm: credential.response.getPublicKeyAlgorithm(), + attestationObject: publicKey.response.attestationObject, + }; + } + + if (credential.response instanceof AuthenticatorAssertionResponse) { + response = { + clientDataJSON: publicKey.response.clientDataJSON, + authenticatorData: publicKey.response.authenticatorData, + signature: publicKey.response.signature, + userHandle: publicKey.response?.userHandle || undefined, + }; + } + + return { + id: publicKey.id, + rawId: publicKey.id, + response, + authenticatorAttachment, + clientExtensionResults, + type, + }; + }; + // Wraps response to AuthenticatorAttestationResponse object const createAttestationResponse = function(publicKey) { const response = { @@ -63,7 +108,8 @@ response: authenticatorResponse, type: publicKey.type, clientExtensionResults: () => publicKey?.response?.clientExtensionResults || {}, - getClientExtensionResults: () => publicKey?.response?.clientExtensionResults || {} + getClientExtensionResults: () => publicKey?.response?.clientExtensionResults || {}, + toJSON: () => kpxcPublicKeyCredentialJson(publicKeyCredential, publicKey) }; return Object.setPrototypeOf(publicKeyCredential, PublicKeyCredential.prototype);