summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--packages/browser/src/helpers/webAuthnAbortService.test.ts23
-rw-r--r--packages/browser/src/helpers/webAuthnAbortService.ts15
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;
+ }
+ }
}
/**