diff --git a/.changeset/mighty-paws-tap.md b/.changeset/mighty-paws-tap.md new file mode 100644 index 0000000000..683aa87d0b --- /dev/null +++ b/.changeset/mighty-paws-tap.md @@ -0,0 +1,5 @@ +--- +"@near-js/biometric-ed25519": minor +--- + +Include sanitization on navigator.credentials response to support Bitwarden password manager diff --git a/packages/biometric-ed25519/src/index.ts b/packages/biometric-ed25519/src/index.ts index f0016e4511..6835b159b9 100644 --- a/packages/biometric-ed25519/src/index.ts +++ b/packages/biometric-ed25519/src/index.ts @@ -12,7 +12,8 @@ import { preformatGetAssertReq, publicKeyCredentialToJSON, recoverPublicKey, - uint8ArrayToBigInt + uint8ArrayToBigInt, + convertToArrayBuffer } from './utils'; import { Fido2 } from './fido2'; import { AssertionResponse } from './index.d'; @@ -63,8 +64,10 @@ export const createKey = async (username: string): Promise => { throw new PasskeyProcessCanceled('Failed to retrieve response from navigator.credentials.create'); } + const sanitizedResponse = convertToArrayBuffer(res); + const result = await f2l.attestation({ - clientAttestationResponse: res, + clientAttestationResponse: sanitizedResponse, origin, challenge: challengeMakeCred.challenge }); @@ -93,7 +96,8 @@ export const getKeys = async (username: string): Promise<[KeyPair, KeyPair]> => setBufferIfUndefined(); return navigator.credentials.get({ publicKey }) .then(async (response: Credential) => { - const getAssertionResponse: AssertionResponse = publicKeyCredentialToJSON(response); + const sanitizedResponse = convertToArrayBuffer(response); + const getAssertionResponse: AssertionResponse = publicKeyCredentialToJSON(sanitizedResponse); const signature = base64.toArrayBuffer(getAssertionResponse.response.signature, true); // eslint-disable-next-line @typescript-eslint/ban-ts-comment diff --git a/packages/biometric-ed25519/src/utils.ts b/packages/biometric-ed25519/src/utils.ts index c9b5647a3e..b5f48bcf76 100644 --- a/packages/biometric-ed25519/src/utils.ts +++ b/packages/biometric-ed25519/src/utils.ts @@ -90,4 +90,19 @@ export const recoverPublicKey = async (r, s, message, recovery) => { export const uint8ArrayToBigInt = (uint8Array: Uint8Array) => { const array = Array.from(uint8Array); return BigInt('0x' + array.map(byte => byte.toString(16).padStart(2, '0')).join('')); +}; + +// This function is tries converts Uint8Array, Array or object to ArrayBuffer. Returns the original object if it doesn't match any of the aforementioned types. +export const convertToArrayBuffer = (obj) => { + if (obj instanceof Uint8Array) { + return obj.buffer.slice(obj.byteOffset, obj.byteOffset + obj.byteLength); + } else if (Array.isArray(obj)) { + return obj.map(convertToArrayBuffer); + } else if (obj !== null && typeof obj === 'object') { + return Object.keys(obj).reduce((acc, key) => { + acc[key] = convertToArrayBuffer(obj[key]); + return acc; + }, {}); + } + return obj; }; \ No newline at end of file