diff options
author | Matthew Miller <matthew@millerti.me> | 2023-02-08 21:20:52 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-08 21:20:52 -0800 |
commit | c26df4309443f11045e6ed1474812f89ee3868e7 (patch) | |
tree | 2cc5ecfa7ede5cbb11bcf9babbf512ac23ef5a94 /packages/browser/src | |
parent | 94aaa5f35e68588b64e021695231e4690be4ec95 (diff) | |
parent | dc40f408e45fd6c8c053cc4c63e99ad96a739d39 (diff) |
Merge pull request #353 from MasterKale/fix/352-not-allowed-error
fix/352-not-allowed-error
Diffstat (limited to 'packages/browser/src')
5 files changed, 62 insertions, 42 deletions
diff --git a/packages/browser/src/helpers/__jest__/generateCustomError.ts b/packages/browser/src/helpers/__jest__/generateCustomError.ts index 7c018ca..55f6acf 100644 --- a/packages/browser/src/helpers/__jest__/generateCustomError.ts +++ b/packages/browser/src/helpers/__jest__/generateCustomError.ts @@ -10,8 +10,9 @@ type WebAuthnErrorName = | 'SecurityError' | 'UnknownError'; -export function generateCustomError(name: WebAuthnErrorName): Error { +export function generateCustomError(name: WebAuthnErrorName, message = ''): Error { const customError = new Error(); customError.name = name; + customError.message = message; return customError; } diff --git a/packages/browser/src/helpers/identifyAuthenticationError.ts b/packages/browser/src/helpers/identifyAuthenticationError.ts index c994947..600a2d6 100644 --- a/packages/browser/src/helpers/identifyAuthenticationError.ts +++ b/packages/browser/src/helpers/identifyAuthenticationError.ts @@ -23,21 +23,10 @@ export function identifyAuthenticationError({ return new WebAuthnError('Authentication ceremony was sent an abort signal', 'AbortError'); } } else if (error.name === 'NotAllowedError') { - if (publicKey.allowCredentials?.length) { - // https://www.w3.org/TR/webauthn-2/#sctn-discover-from-external-source (Step 17) - // https://www.w3.org/TR/webauthn-2/#sctn-op-get-assertion (Step 6) - return new WebAuthnError( - 'No available authenticator recognized any of the allowed credentials', - 'NotAllowedError', - ); - } - - // https://www.w3.org/TR/webauthn-2/#sctn-discover-from-external-source (Step 18) - // https://www.w3.org/TR/webauthn-2/#sctn-op-get-assertion (Step 7) - return new WebAuthnError( - 'User clicked cancel, or the authentication ceremony timed out', - 'NotAllowedError', - ); + /** + * Pass the error directly through. Platforms are overloading this error beyond what the spec + * defines and we don't want to overwrite potentially useful error messages. + */ } else if (error.name === 'SecurityError') { const effectiveDomain = window.location.hostname; if (!isValidDomain(effectiveDomain)) { diff --git a/packages/browser/src/helpers/identifyRegistrationError.ts b/packages/browser/src/helpers/identifyRegistrationError.ts index 8976602..9b76454 100644 --- a/packages/browser/src/helpers/identifyRegistrationError.ts +++ b/packages/browser/src/helpers/identifyRegistrationError.ts @@ -41,12 +41,10 @@ export function identifyRegistrationError({ // https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (Step 3) return new WebAuthnError('The authenticator was previously registered', 'InvalidStateError'); } else if (error.name === 'NotAllowedError') { - // https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 20) - // https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 21) - return new WebAuthnError( - 'User clicked cancel, or the registration ceremony timed out', - 'NotAllowedError', - ); + /** + * Pass the error directly through. Platforms are overloading this error beyond what the spec + * defines and we don't want to overwrite potentially useful error messages. + */ } else if (error.name === 'NotSupportedError') { const validPubKeyCredParams = publicKey.pubKeyCredParams.filter( param => param.type === 'public-key', diff --git a/packages/browser/src/methods/startAuthentication.test.ts b/packages/browser/src/methods/startAuthentication.test.ts index 31dbde3..1708651 100644 --- a/packages/browser/src/methods/startAuthentication.test.ts +++ b/packages/browser/src/methods/startAuthentication.test.ts @@ -332,29 +332,38 @@ describe('WebAuthnError', () => { }); describe('NotAllowedError', () => { - const NotAllowedError = generateCustomError('NotAllowedError'); - - test('should identify unrecognized allowed credentials', async () => { + test('should pass through error message (iOS Safari - Operation failed)', async () => { + /** + * Thrown when biometric is not enrolled, or a Safari bug prevents conditional UI from being + * aborted properly between page reloads. + * + * See https://github.com/MasterKale/SimpleWebAuthn/discussions/350#discussioncomment-4896572 + */ + const NotAllowedError = generateCustomError('NotAllowedError', 'Operation failed.'); mockNavigatorGet.mockRejectedValueOnce(NotAllowedError); const rejected = await expect(startAuthentication(goodOpts1)).rejects; - rejected.toThrow(WebAuthnError); - rejected.toThrow(/allowed credentials/i); + rejected.toThrow(Error); + rejected.toThrow(/operation failed/i); rejected.toHaveProperty('name', 'NotAllowedError'); }); - test('should identify cancellation or timeout', async () => { + test('should pass through error message (Chrome M110 - Bad TLS Cert)', async () => { + /** + * Starting from Chrome M110, WebAuthn is blocked if the site is being displayed on a URL with + * TLS certificate issues. This includes during development. + * + * See https://github.com/MasterKale/SimpleWebAuthn/discussions/351#discussioncomment-4910458 + */ + const NotAllowedError = generateCustomError( + 'NotAllowedError', + 'WebAuthn is not supported on sites with TLS certificate errors.' + ); mockNavigatorGet.mockRejectedValueOnce(NotAllowedError); - const opts = { - ...goodOpts1, - allowCredentials: [], - }; - - const rejected = await expect(startAuthentication(opts)).rejects; - rejected.toThrow(WebAuthnError); - rejected.toThrow(/cancel/i); - rejected.toThrow(/timed out/i); + const rejected = await expect(startAuthentication(goodOpts1)).rejects; + rejected.toThrow(Error); + rejected.toThrow(/sites with TLS certificate errors/i); rejected.toHaveProperty('name', 'NotAllowedError'); }); }); diff --git a/packages/browser/src/methods/startRegistration.test.ts b/packages/browser/src/methods/startRegistration.test.ts index 8ba6f5a..2c2d2de 100644 --- a/packages/browser/src/methods/startRegistration.test.ts +++ b/packages/browser/src/methods/startRegistration.test.ts @@ -324,15 +324,38 @@ describe('WebAuthnError', () => { }); describe('NotAllowedError', () => { - const NotAllowedError = generateCustomError('NotAllowedError'); + test('should pass through error message (iOS Safari - Operation failed)', async () => { + /** + * Thrown when biometric is not enrolled, or a Safari bug prevents conditional UI from being + * aborted properly between page reloads. + * + * See https://github.com/MasterKale/SimpleWebAuthn/discussions/350#discussioncomment-4896572 + */ + const NotAllowedError = generateCustomError('NotAllowedError', 'Operation failed.'); + mockNavigatorCreate.mockRejectedValueOnce(NotAllowedError); + + const rejected = await expect(startRegistration(goodOpts1)).rejects; + rejected.toThrow(Error); + rejected.toThrow(/operation failed/i); + rejected.toHaveProperty('name', 'NotAllowedError'); + }); - test('should identify cancellation or timeout', async () => { + test('should pass through error message (Chrome M110 - Bad TLS Cert)', async () => { + /** + * Starting from Chrome M110, WebAuthn is blocked if the site is being displayed on a URL with + * TLS certificate issues. This includes during development. + * + * See https://github.com/MasterKale/SimpleWebAuthn/discussions/351#discussioncomment-4910458 + */ + const NotAllowedError = generateCustomError( + 'NotAllowedError', + 'WebAuthn is not supported on sites with TLS certificate errors.' + ); mockNavigatorCreate.mockRejectedValueOnce(NotAllowedError); const rejected = await expect(startRegistration(goodOpts1)).rejects; - rejected.toThrow(WebAuthnError); - rejected.toThrow(/cancel/i); - rejected.toThrow(/timed out/i); + rejected.toThrow(Error); + rejected.toThrow(/sites with TLS certificate errors/i); rejected.toHaveProperty('name', 'NotAllowedError'); }); }); |