summaryrefslogtreecommitdiffhomepage
path: root/packages/browser/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/browser/src')
-rw-r--r--packages/browser/src/methods/startRegistration.test.ts55
-rw-r--r--packages/browser/src/methods/startRegistration.ts26
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 {