diff options
Diffstat (limited to 'packages/browser/src')
-rw-r--r-- | packages/browser/src/methods/startRegistration.test.ts | 55 | ||||
-rw-r--r-- | packages/browser/src/methods/startRegistration.ts | 26 |
2 files changed, 74 insertions, 7 deletions
diff --git a/packages/browser/src/methods/startRegistration.test.ts b/packages/browser/src/methods/startRegistration.test.ts index b8ca081..97878c6 100644 --- a/packages/browser/src/methods/startRegistration.test.ts +++ b/packages/browser/src/methods/startRegistration.test.ts @@ -309,6 +309,61 @@ test('should not return convenience values if getters missing', async () => { expect(response.response.authenticatorData).toBeUndefined(); }); +test('should survive browser extensions that intercept WebAuthn and incorrectly implement public key value getters', async () => { + /** + * 1Password browser extension v2.15.1 (the one that introduced passkeys support) seemed to have + * implemented the following methods on AuthenticatorAttestationResponse... + * + * - getPublicKeyAlgorithm() + * - getPublicKey() + * - getAuthenticatorData() + * + * ...But when you attempt to call them as methods they'll error out with `TypeError`'s: + * + * Safari: + * > TypeError: Can only call AuthenticatorAttestationResponse.getPublicKeyAlgorithm on instances + * > of AuthenticatorAttestationResponse + * + * Chrome: + * > TypeError: Illegal invocation + * + * Firefox: + * > N/A (it handled it fine for some reason) + * + * Make sure `startRegistration()` can survive this scenario. + * + * See https://github.com/MasterKale/SimpleWebAuthn/issues/438 for more context. + */ + + // Mock extension return values from the browser extension intercepting WebAuthn + mockNavigatorCreate.mockImplementation((): Promise<unknown> => { + return new Promise((resolve) => { + resolve({ + response: { + getPublicKeyAlgorithm: () => { + throw new Error('I throw for some reason'); + }, + getPublicKey: () => { + throw new Error('I also throw for some reason'); + }, + getAuthenticatorData: () => { + throw new Error('I throw for some reason too'); + }, + }, + getClientExtensionResults: () => {}, + }); + }); + }); + + await expect(startRegistration(goodOpts1)).resolves; + + const response = await startRegistration(goodOpts1); + + expect(response.response.publicKeyAlgorithm).toBeUndefined(); + expect(response.response.publicKey).toBeUndefined(); + expect(response.response.authenticatorData).toBeUndefined(); +}); + describe('WebAuthnError', () => { describe('AbortError', () => { const AbortError = generateCustomError('AbortError'); diff --git a/packages/browser/src/methods/startRegistration.ts b/packages/browser/src/methods/startRegistration.ts index 74da7fd..9f4a104 100644 --- a/packages/browser/src/methods/startRegistration.ts +++ b/packages/browser/src/methods/startRegistration.ts @@ -67,23 +67,35 @@ export async function startRegistration( // L3 says this is required, but browser and webview support are still not guaranteed. let responsePublicKeyAlgorithm: number | undefined = undefined; if (typeof response.getPublicKeyAlgorithm === 'function') { - responsePublicKeyAlgorithm = response.getPublicKeyAlgorithm(); + try { + responsePublicKeyAlgorithm = response.getPublicKeyAlgorithm(); + } catch (error) { + // pass + } } let responsePublicKey: string | undefined = undefined; if (typeof response.getPublicKey === 'function') { - const _publicKey = response.getPublicKey(); - if (_publicKey !== null) { - responsePublicKey = bufferToBase64URLString(_publicKey); + try { + const _publicKey = response.getPublicKey(); + if (_publicKey !== null) { + responsePublicKey = bufferToBase64URLString(_publicKey); + } + } catch (error) { + // pass } } // L3 says this is required, but browser and webview support are still not guaranteed. let responseAuthenticatorData: string | undefined; if (typeof response.getAuthenticatorData === 'function') { - responseAuthenticatorData = bufferToBase64URLString( - response.getAuthenticatorData(), - ); + try { + responseAuthenticatorData = bufferToBase64URLString( + response.getAuthenticatorData(), + ); + } catch (error) { + // pass + } } return { |