diff options
-rw-r--r-- | packages/browser/src/helpers/webAuthnAbortService.test.ts | 23 | ||||
-rw-r--r-- | packages/browser/src/helpers/webAuthnAbortService.ts | 15 |
2 files changed, 38 insertions, 0 deletions
diff --git a/packages/browser/src/helpers/webAuthnAbortService.test.ts b/packages/browser/src/helpers/webAuthnAbortService.test.ts index a538dff..fd2ca7a 100644 --- a/packages/browser/src/helpers/webAuthnAbortService.test.ts +++ b/packages/browser/src/helpers/webAuthnAbortService.test.ts @@ -25,3 +25,26 @@ test('should call abort() with AbortError on existing controller when creating a expect(abortReason).toBeInstanceOf(Error); expect(abortReason.name).toEqual('AbortError'); }); + +test('should cancel active WebAuthn ceremony when manually cancelled', () => { + // Populate `.controller` + WebauthnAbortService.createNewAbortSignal(); + + // Spy on the existing instance of AbortController + const abortSpy = jest.fn(); + // @ts-ignore: Ignore the fact that `controller` is private + WebauthnAbortService.controller.abort = abortSpy; + + // Cancel the in-flight ceremony, which should call `abort()` on the existing controller + WebauthnAbortService.cancelCeremony(); + expect(abortSpy).toHaveBeenCalledTimes(1); + + // Make sure we raise an AbortError so it can be detected correctly + const abortReason = abortSpy.mock.calls[0][0]; + expect(abortReason).toBeInstanceOf(Error); + expect(abortReason.name).toEqual('AbortError'); + + // Ensure that we don't set up a new AbortController because it's unnecessary to do so + // @ts-ignore: Ignore the fact that `controller` is private + expect(WebauthnAbortService.controller).toBeUndefined(); +}); diff --git a/packages/browser/src/helpers/webAuthnAbortService.ts b/packages/browser/src/helpers/webAuthnAbortService.ts index ce6979c..da5380b 100644 --- a/packages/browser/src/helpers/webAuthnAbortService.ts +++ b/packages/browser/src/helpers/webAuthnAbortService.ts @@ -21,6 +21,21 @@ class BaseWebAuthnAbortService { this.controller = newController; return newController.signal; } + + /** + * Manually cancel any active WebAuthn registration or authentication attempt. + */ + cancelCeremony() { + if (this.controller) { + const abortError = new Error( + 'Manually cancelling existing WebAuthn API call', + ); + abortError.name = 'AbortError'; + this.controller.abort(abortError); + + this.controller = undefined; + } + } } /** |