@authsignal/browser 0.3.7 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { AddAuthenticatorRequest, AddAuthenticatorResponse, AuthenticationOptsRequest, AuthenticationOptsResponse, RegistrationOptsRequest, RegistrationOptsResponse, VerifyRequest, VerifyResponse } from "./types";
1
+ import { AddAuthenticatorRequest, AddAuthenticatorResponse, AuthenticationOptsRequest, AuthenticationOptsResponse, PasskeyAuthenticatorResponse, RegistrationOptsRequest, RegistrationOptsResponse, VerifyRequest, VerifyResponse } from "./types";
2
2
  declare type PasskeyApiClientOptions = {
3
3
  baseUrl: string;
4
4
  tenantId: string;
@@ -7,10 +7,11 @@ export declare class PasskeyApiClient {
7
7
  tenantId: string;
8
8
  baseUrl: string;
9
9
  constructor({ baseUrl, tenantId }: PasskeyApiClientOptions);
10
- registrationOptions({ token, userName }: RegistrationOptsRequest): Promise<RegistrationOptsResponse>;
10
+ registrationOptions({ token, userName, authenticatorAttachment, }: RegistrationOptsRequest): Promise<RegistrationOptsResponse>;
11
11
  authenticationOptions({ token }: AuthenticationOptsRequest): Promise<AuthenticationOptsResponse>;
12
12
  addAuthenticator({ token, ...rest }: AddAuthenticatorRequest): Promise<AddAuthenticatorResponse>;
13
13
  verify({ token, ...rest }: VerifyRequest): Promise<VerifyResponse>;
14
+ getPasskeyAuthenticator(credentialId: string): Promise<PasskeyAuthenticatorResponse>;
14
15
  private buildHeaders;
15
16
  }
16
17
  export {};
@@ -1,7 +1,8 @@
1
- import { AuthenticationResponseJSON, PublicKeyCredentialCreationOptionsJSON, RegistrationResponseJSON } from "@simplewebauthn/types";
1
+ import { AuthenticationResponseJSON, AuthenticatorAttachment, PublicKeyCredentialCreationOptionsJSON, RegistrationResponseJSON } from "@simplewebauthn/types";
2
2
  export declare type RegistrationOptsRequest = {
3
3
  userName?: string;
4
4
  token: string;
5
+ authenticatorAttachment?: AuthenticatorAttachment | null;
5
6
  };
6
7
  export declare type RegistrationOptsResponse = {
7
8
  challengeId: string;
@@ -33,3 +34,7 @@ export declare type VerifyResponse = {
33
34
  isVerified: boolean;
34
35
  accessToken?: string;
35
36
  };
37
+ export declare type PasskeyAuthenticatorResponse = {
38
+ credentialId: string;
39
+ verifiedAt: string;
40
+ };
@@ -1,18 +1,18 @@
1
+ import { A11yDialogEvent } from "a11y-dialog";
1
2
  declare type PopupShowInput = {
2
3
  url: string;
3
4
  };
4
- declare type EventType = "show" | "hide" | "destroy" | "create";
5
- declare type EventHandler = (node: Element, event?: Event) => void;
6
5
  declare type PopupHandlerOptions = {
7
6
  width?: string;
7
+ isClosable?: boolean;
8
8
  };
9
9
  export declare class PopupHandler {
10
10
  private popup;
11
- constructor({ width }: PopupHandlerOptions);
12
- create({ width }: PopupHandlerOptions): void;
11
+ constructor({ width, isClosable }: PopupHandlerOptions);
12
+ create({ width, isClosable }: PopupHandlerOptions): void;
13
13
  destroy(): void;
14
14
  show({ url }: PopupShowInput): void;
15
15
  close(): void;
16
- on(event: EventType, handler: EventHandler): void;
16
+ on(event: A11yDialogEvent, listener: EventListener): void;
17
17
  }
18
18
  export {};
package/dist/index.js CHANGED
@@ -543,18 +543,21 @@ var PasskeyApiClient = /** @class */ (function () {
543
543
  this.baseUrl = baseUrl;
544
544
  }
545
545
  PasskeyApiClient.prototype.registrationOptions = function (_a) {
546
- var token = _a.token, userName = _a.userName;
546
+ var token = _a.token, userName = _a.userName, authenticatorAttachment = _a.authenticatorAttachment;
547
547
  return __awaiter(this, void 0, void 0, function () {
548
- var request;
548
+ var body, response;
549
549
  return __generator(this, function (_b) {
550
550
  switch (_b.label) {
551
551
  case 0:
552
- request = fetch("".concat(this.baseUrl, "/client/user-authenticators/passkey/registration-options"), {
552
+ body = Boolean(authenticatorAttachment)
553
+ ? { username: userName, authenticatorAttachment: authenticatorAttachment }
554
+ : { username: userName };
555
+ response = fetch("".concat(this.baseUrl, "/client/user-authenticators/passkey/registration-options"), {
553
556
  method: "POST",
554
557
  headers: this.buildHeaders(token),
555
- body: JSON.stringify({ username: userName })
558
+ body: JSON.stringify(body)
556
559
  });
557
- return [4 /*yield*/, request];
560
+ return [4 /*yield*/, response];
558
561
  case 1: return [2 /*return*/, (_b.sent()).json()];
559
562
  }
560
563
  });
@@ -563,16 +566,16 @@ var PasskeyApiClient = /** @class */ (function () {
563
566
  PasskeyApiClient.prototype.authenticationOptions = function (_a) {
564
567
  var token = _a.token;
565
568
  return __awaiter(this, void 0, void 0, function () {
566
- var request;
569
+ var response;
567
570
  return __generator(this, function (_b) {
568
571
  switch (_b.label) {
569
572
  case 0:
570
- request = fetch("".concat(this.baseUrl, "/client/user-authenticators/passkey/authentication-options"), {
573
+ response = fetch("".concat(this.baseUrl, "/client/user-authenticators/passkey/authentication-options"), {
571
574
  method: "POST",
572
575
  headers: this.buildHeaders(token),
573
576
  body: JSON.stringify({})
574
577
  });
575
- return [4 /*yield*/, request];
578
+ return [4 /*yield*/, response];
576
579
  case 1: return [2 /*return*/, (_b.sent()).json()];
577
580
  }
578
581
  });
@@ -581,16 +584,16 @@ var PasskeyApiClient = /** @class */ (function () {
581
584
  PasskeyApiClient.prototype.addAuthenticator = function (_a) {
582
585
  var token = _a.token, rest = __rest(_a, ["token"]);
583
586
  return __awaiter(this, void 0, void 0, function () {
584
- var request;
587
+ var response;
585
588
  return __generator(this, function (_b) {
586
589
  switch (_b.label) {
587
590
  case 0:
588
- request = fetch("".concat(this.baseUrl, "/client/user-authenticators/passkey"), {
591
+ response = fetch("".concat(this.baseUrl, "/client/user-authenticators/passkey"), {
589
592
  method: "POST",
590
593
  headers: this.buildHeaders(token),
591
594
  body: JSON.stringify(rest)
592
595
  });
593
- return [4 /*yield*/, request];
596
+ return [4 /*yield*/, response];
594
597
  case 1: return [2 /*return*/, (_b.sent()).json()];
595
598
  }
596
599
  });
@@ -599,21 +602,40 @@ var PasskeyApiClient = /** @class */ (function () {
599
602
  PasskeyApiClient.prototype.verify = function (_a) {
600
603
  var token = _a.token, rest = __rest(_a, ["token"]);
601
604
  return __awaiter(this, void 0, void 0, function () {
602
- var request;
605
+ var response;
603
606
  return __generator(this, function (_b) {
604
607
  switch (_b.label) {
605
608
  case 0:
606
- request = fetch("".concat(this.baseUrl, "/client/verify/passkey"), {
609
+ response = fetch("".concat(this.baseUrl, "/client/verify/passkey"), {
607
610
  method: "POST",
608
611
  headers: this.buildHeaders(token),
609
612
  body: JSON.stringify(rest)
610
613
  });
611
- return [4 /*yield*/, request];
614
+ return [4 /*yield*/, response];
612
615
  case 1: return [2 /*return*/, (_b.sent()).json()];
613
616
  }
614
617
  });
615
618
  });
616
619
  };
620
+ PasskeyApiClient.prototype.getPasskeyAuthenticator = function (credentialId) {
621
+ return __awaiter(this, void 0, void 0, function () {
622
+ var response;
623
+ return __generator(this, function (_a) {
624
+ switch (_a.label) {
625
+ case 0: return [4 /*yield*/, fetch("".concat(this.baseUrl, "/client/user-authenticators/passkey?credentialId=").concat(credentialId), {
626
+ method: "GET",
627
+ headers: this.buildHeaders()
628
+ })];
629
+ case 1:
630
+ response = _a.sent();
631
+ if (!response.ok) {
632
+ throw new Error(response.statusText);
633
+ }
634
+ return [2 /*return*/, response.json()];
635
+ }
636
+ });
637
+ });
638
+ };
617
639
  PasskeyApiClient.prototype.buildHeaders = function (token) {
618
640
  var authorizationHeader = token ? "Bearer ".concat(token) : "Basic ".concat(window.btoa(encodeURIComponent(this.tenantId)));
619
641
  return {
@@ -627,27 +649,31 @@ var PasskeyApiClient = /** @class */ (function () {
627
649
  var Passkey = /** @class */ (function () {
628
650
  function Passkey(_a) {
629
651
  var baseUrl = _a.baseUrl, tenantId = _a.tenantId;
652
+ this.passkeyLocalStorageKey = "as_passkey_credential_id";
630
653
  this.api = new PasskeyApiClient({ baseUrl: baseUrl, tenantId: tenantId });
631
654
  }
632
655
  Passkey.prototype.signUp = function (_a) {
633
- var userName = _a.userName, token = _a.token;
656
+ var userName = _a.userName, token = _a.token, _b = _a.authenticatorAttachment, authenticatorAttachment = _b === void 0 ? "platform" : _b;
634
657
  return __awaiter(this, void 0, void 0, function () {
635
658
  var optionsResponse, registrationResponse, addAuthenticatorResponse;
636
- return __generator(this, function (_b) {
637
- switch (_b.label) {
638
- case 0: return [4 /*yield*/, this.api.registrationOptions({ userName: userName, token: token })];
659
+ return __generator(this, function (_c) {
660
+ switch (_c.label) {
661
+ case 0: return [4 /*yield*/, this.api.registrationOptions({ userName: userName, token: token, authenticatorAttachment: authenticatorAttachment })];
639
662
  case 1:
640
- optionsResponse = _b.sent();
663
+ optionsResponse = _c.sent();
641
664
  return [4 /*yield*/, startRegistration(optionsResponse.options)];
642
665
  case 2:
643
- registrationResponse = _b.sent();
666
+ registrationResponse = _c.sent();
644
667
  return [4 /*yield*/, this.api.addAuthenticator({
645
668
  challengeId: optionsResponse.challengeId,
646
669
  registrationCredential: registrationResponse,
647
670
  token: token
648
671
  })];
649
672
  case 3:
650
- addAuthenticatorResponse = _b.sent();
673
+ addAuthenticatorResponse = _c.sent();
674
+ if (addAuthenticatorResponse === null || addAuthenticatorResponse === void 0 ? void 0 : addAuthenticatorResponse.isVerified) {
675
+ this.storeCredentialAgainstDevice(registrationResponse);
676
+ }
651
677
  return [2 /*return*/, addAuthenticatorResponse === null || addAuthenticatorResponse === void 0 ? void 0 : addAuthenticatorResponse.accessToken];
652
678
  }
653
679
  });
@@ -675,11 +701,46 @@ var Passkey = /** @class */ (function () {
675
701
  })];
676
702
  case 3:
677
703
  verifyResponse = _a.sent();
704
+ if (verifyResponse === null || verifyResponse === void 0 ? void 0 : verifyResponse.isVerified) {
705
+ this.storeCredentialAgainstDevice(authenticationResponse);
706
+ }
678
707
  return [2 /*return*/, verifyResponse === null || verifyResponse === void 0 ? void 0 : verifyResponse.accessToken];
679
708
  }
680
709
  });
681
710
  });
682
711
  };
712
+ Passkey.prototype.isAvailableOnDevice = function () {
713
+ return __awaiter(this, void 0, void 0, function () {
714
+ var credentialId;
715
+ return __generator(this, function (_b) {
716
+ switch (_b.label) {
717
+ case 0:
718
+ credentialId = localStorage.getItem(this.passkeyLocalStorageKey);
719
+ if (!credentialId) {
720
+ return [2 /*return*/, false];
721
+ }
722
+ _b.label = 1;
723
+ case 1:
724
+ _b.trys.push([1, 3, , 4]);
725
+ return [4 /*yield*/, this.api.getPasskeyAuthenticator(credentialId)];
726
+ case 2:
727
+ _b.sent();
728
+ return [2 /*return*/, true];
729
+ case 3:
730
+ _b.sent();
731
+ return [2 /*return*/, false];
732
+ case 4: return [2 /*return*/];
733
+ }
734
+ });
735
+ });
736
+ };
737
+ Passkey.prototype.storeCredentialAgainstDevice = function (_a) {
738
+ var id = _a.id, authenticatorAttachment = _a.authenticatorAttachment;
739
+ if (authenticatorAttachment === "cross-platform") {
740
+ return;
741
+ }
742
+ localStorage.setItem(this.passkeyLocalStorageKey, id);
743
+ };
683
744
  return Passkey;
684
745
  }());
685
746
 
@@ -716,408 +777,423 @@ function openWindow(_a) {
716
777
  return window.open(url, "", "toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=".concat(width, ", height=").concat(height, ", top=").concat(y, ", left=").concat(x));
717
778
  }
718
779
 
780
+ const not = {
781
+ inert: ':not([inert]):not([inert] *)',
782
+ negTabIndex: ':not([tabindex^="-"])',
783
+ disabled: ':not(:disabled)',
784
+ };
785
+
719
786
  var focusableSelectors = [
720
- 'a[href]:not([tabindex^="-"])',
721
- 'area[href]:not([tabindex^="-"])',
722
- 'input:not([type="hidden"]):not([type="radio"]):not([disabled]):not([tabindex^="-"])',
723
- 'input[type="radio"]:not([disabled]):not([tabindex^="-"])',
724
- 'select:not([disabled]):not([tabindex^="-"])',
725
- 'textarea:not([disabled]):not([tabindex^="-"])',
726
- 'button:not([disabled]):not([tabindex^="-"])',
727
- 'iframe:not([tabindex^="-"])',
728
- 'audio[controls]:not([tabindex^="-"])',
729
- 'video[controls]:not([tabindex^="-"])',
730
- '[contenteditable]:not([tabindex^="-"])',
731
- '[tabindex]:not([tabindex^="-"])',
787
+ `a[href]${not.inert}${not.negTabIndex}`,
788
+ `area[href]${not.inert}${not.negTabIndex}`,
789
+ `input:not([type="hidden"]):not([type="radio"])${not.inert}${not.negTabIndex}${not.disabled}`,
790
+ `input[type="radio"]${not.inert}${not.negTabIndex}${not.disabled}`,
791
+ `select${not.inert}${not.negTabIndex}${not.disabled}`,
792
+ `textarea${not.inert}${not.negTabIndex}${not.disabled}`,
793
+ `button${not.inert}${not.negTabIndex}${not.disabled}`,
794
+ `details${not.inert} > summary:first-of-type${not.negTabIndex}`,
795
+ // Discard until Firefox supports `:has()`
796
+ // See: https://github.com/KittyGiraudel/focusable-selectors/issues/12
797
+ // `details:not(:has(> summary))${not.inert}${not.negTabIndex}`,
798
+ `iframe${not.inert}${not.negTabIndex}`,
799
+ `audio[controls]${not.inert}${not.negTabIndex}`,
800
+ `video[controls]${not.inert}${not.negTabIndex}`,
801
+ `[contenteditable]${not.inert}${not.negTabIndex}`,
802
+ `[tabindex]${not.inert}${not.negTabIndex}`,
732
803
  ];
733
804
 
734
- var TAB_KEY = 'Tab';
735
- var ESCAPE_KEY = 'Escape';
736
-
737
805
  /**
738
- * Define the constructor to instantiate a dialog
739
- *
740
- * @constructor
741
- * @param {Element} element
806
+ * Set the focus to the first element with `autofocus` with the element or the
807
+ * element itself.
742
808
  */
743
- function A11yDialog(element) {
744
- // Prebind the functions that will be bound in addEventListener and
745
- // removeEventListener to avoid losing references
746
- this._show = this.show.bind(this);
747
- this._hide = this.hide.bind(this);
748
- this._maintainFocus = this._maintainFocus.bind(this);
749
- this._bindKeypress = this._bindKeypress.bind(this);
750
-
751
- this.$el = element;
752
- this.shown = false;
753
- this._id = this.$el.getAttribute('data-a11y-dialog') || this.$el.id;
754
- this._previouslyFocused = null;
755
- this._listeners = {};
756
-
757
- // Initialise everything needed for the dialog to work properly
758
- this.create();
809
+ function moveFocusToDialog(el) {
810
+ const focused = (el.querySelector('[autofocus]') || el);
811
+ focused.focus();
759
812
  }
760
-
761
- /**
762
- * Set up everything necessary for the dialog to be functioning
763
- *
764
- * @param {(NodeList | Element | string)} targets
765
- * @return {this}
766
- */
767
- A11yDialog.prototype.create = function () {
768
- this.$el.setAttribute('aria-hidden', true);
769
- this.$el.setAttribute('aria-modal', true);
770
- this.$el.setAttribute('tabindex', -1);
771
-
772
- if (!this.$el.hasAttribute('role')) {
773
- this.$el.setAttribute('role', 'dialog');
774
- }
775
-
776
- // Keep a collection of dialog openers, each of which will be bound a click
777
- // event listener to open the dialog
778
- this._openers = $$('[data-a11y-dialog-show="' + this._id + '"]');
779
- this._openers.forEach(
780
- function (opener) {
781
- opener.addEventListener('click', this._show);
782
- }.bind(this)
783
- );
784
-
785
- // Keep a collection of dialog closers, each of which will be bound a click
786
- // event listener to close the dialog
787
- const $el = this.$el;
788
-
789
- this._closers = $$('[data-a11y-dialog-hide]', this.$el)
790
- // This filter is necessary in case there are nested dialogs, so that
791
- // only closers from the current dialog are retrieved and effective
792
- .filter(function (closer) {
793
- // Testing for `[aria-modal="true"]` is not enough since this attribute
794
- // and the collect of closers is done at instantation time, when nested
795
- // dialogs might not have yet been instantiated. Note that if the dialogs
796
- // are manually instantiated, this could still fail because none of these
797
- // selectors would match; this would cause closers to close all parent
798
- // dialogs instead of just the current one
799
- return closer.closest('[aria-modal="true"], [data-a11y-dialog]') === $el
800
- })
801
- .concat($$('[data-a11y-dialog-hide="' + this._id + '"]'));
802
-
803
- this._closers.forEach(
804
- function (closer) {
805
- closer.addEventListener('click', this._hide);
806
- }.bind(this)
807
- );
808
-
809
- // Execute all callbacks registered for the `create` event
810
- this._fire('create');
811
-
812
- return this
813
- };
814
-
815
- /**
816
- * Show the dialog element, disable all the targets (siblings), trap the
817
- * current focus within it, listen for some specific key presses and fire all
818
- * registered callbacks for `show` event
819
- *
820
- * @param {CustomEvent} event
821
- * @return {this}
822
- */
823
- A11yDialog.prototype.show = function (event) {
824
- // If the dialog is already open, abort
825
- if (this.shown) {
826
- return this
827
- }
828
-
829
- // Keep a reference to the currently focused element to be able to restore
830
- // it later
831
- this._previouslyFocused = document.activeElement;
832
- this.$el.removeAttribute('aria-hidden');
833
- this.shown = true;
834
-
835
- // Set the focus to the dialog element
836
- moveFocusToDialog(this.$el);
837
-
838
- // Bind a focus event listener to the body element to make sure the focus
839
- // stays trapped inside the dialog while open, and start listening for some
840
- // specific key presses (TAB and ESC)
841
- document.body.addEventListener('focus', this._maintainFocus, true);
842
- document.addEventListener('keydown', this._bindKeypress);
843
-
844
- // Execute all callbacks registered for the `show` event
845
- this._fire('show', event);
846
-
847
- return this
848
- };
849
-
850
- /**
851
- * Hide the dialog element, enable all the targets (siblings), restore the
852
- * focus to the previously active element, stop listening for some specific
853
- * key presses and fire all registered callbacks for `hide` event
854
- *
855
- * @param {CustomEvent} event
856
- * @return {this}
857
- */
858
- A11yDialog.prototype.hide = function (event) {
859
- // If the dialog is already closed, abort
860
- if (!this.shown) {
861
- return this
862
- }
863
-
864
- this.shown = false;
865
- this.$el.setAttribute('aria-hidden', 'true');
866
-
867
- // If there was a focused element before the dialog was opened (and it has a
868
- // `focus` method), restore the focus back to it
869
- // See: https://github.com/KittyGiraudel/a11y-dialog/issues/108
870
- if (this._previouslyFocused && this._previouslyFocused.focus) {
871
- this._previouslyFocused.focus();
872
- }
873
-
874
- // Remove the focus event listener to the body element and stop listening
875
- // for specific key presses
876
- document.body.removeEventListener('focus', this._maintainFocus, true);
877
- document.removeEventListener('keydown', this._bindKeypress);
878
-
879
- // Execute all callbacks registered for the `hide` event
880
- this._fire('hide', event);
881
-
882
- return this
883
- };
884
-
885
- /**
886
- * Destroy the current instance (after making sure the dialog has been hidden)
887
- * and remove all associated listeners from dialog openers and closers
888
- *
889
- * @return {this}
890
- */
891
- A11yDialog.prototype.destroy = function () {
892
- // Hide the dialog to avoid destroying an open instance
893
- this.hide();
894
-
895
- // Remove the click event listener from all dialog openers
896
- this._openers.forEach(
897
- function (opener) {
898
- opener.removeEventListener('click', this._show);
899
- }.bind(this)
900
- );
901
-
902
- // Remove the click event listener from all dialog closers
903
- this._closers.forEach(
904
- function (closer) {
905
- closer.removeEventListener('click', this._hide);
906
- }.bind(this)
907
- );
908
-
909
- // Execute all callbacks registered for the `destroy` event
910
- this._fire('destroy');
911
-
912
- // Keep an object of listener types mapped to callback functions
913
- this._listeners = {};
914
-
915
- return this
916
- };
917
-
918
813
  /**
919
- * Register a new callback for the given event type
920
- *
921
- * @param {string} type
922
- * @param {Function} handler
814
+ * Get the first and last focusable elements in a given tree.
923
815
  */
924
- A11yDialog.prototype.on = function (type, handler) {
925
- if (typeof this._listeners[type] === 'undefined') {
926
- this._listeners[type] = [];
927
- }
928
-
929
- this._listeners[type].push(handler);
930
-
931
- return this
932
- };
933
-
934
- /**
935
- * Unregister an existing callback for the given event type
936
- *
937
- * @param {string} type
938
- * @param {Function} handler
939
- */
940
- A11yDialog.prototype.off = function (type, handler) {
941
- var index = (this._listeners[type] || []).indexOf(handler);
942
-
943
- if (index > -1) {
944
- this._listeners[type].splice(index, 1);
945
- }
946
-
947
- return this
948
- };
949
-
816
+ function getFocusableEdges(el) {
817
+ // Check for a focusable element within the subtree of `el`.
818
+ const first = findFocusableElement(el, true);
819
+ // Only if we find the first element do we need to look for the last one. If
820
+ // there’s no last element, we set `last` as a reference to `first` so that
821
+ // the returned array is always of length 2.
822
+ const last = first ? findFocusableElement(el, false) || first : null;
823
+ return [first, last];
824
+ }
950
825
  /**
951
- * Iterate over all registered handlers for given type and call them all with
952
- * the dialog element as first argument, event as second argument (if any). Also
953
- * dispatch a custom event on the DOM element itself to make it possible to
954
- * react to the lifecycle of auto-instantiated dialogs.
955
- *
956
- * @access private
957
- * @param {string} type
958
- * @param {CustomEvent} event
826
+ * Find the first focusable element inside the given node if `forward` is truthy
827
+ * or the last focusable element otherwise.
959
828
  */
960
- A11yDialog.prototype._fire = function (type, event) {
961
- var listeners = this._listeners[type] || [];
962
- var domEvent = new CustomEvent(type, { detail: event });
963
-
964
- this.$el.dispatchEvent(domEvent);
965
-
966
- listeners.forEach(
967
- function (listener) {
968
- listener(this.$el, event);
969
- }.bind(this)
970
- );
971
- };
972
-
829
+ function findFocusableElement(node, forward) {
830
+ // If we’re walking forward, check if this node is focusable, and return it
831
+ // immediately if it is.
832
+ if (forward && isFocusable(node))
833
+ return node;
834
+ // We should only search the subtree of this node if it can have focusable
835
+ // children.
836
+ if (canHaveFocusableChildren(node)) {
837
+ // Start walking the DOM tree, looking for focusable elements.
838
+ // Case 1: If this node has a shadow root, search it recursively.
839
+ if (node.shadowRoot) {
840
+ // Descend into this subtree.
841
+ let next = getNextChildEl(node.shadowRoot, forward);
842
+ // Traverse siblings, searching the subtree of each one
843
+ // for focusable elements.
844
+ while (next) {
845
+ const focusableEl = findFocusableElement(next, forward);
846
+ if (focusableEl)
847
+ return focusableEl;
848
+ next = getNextSiblingEl(next, forward);
849
+ }
850
+ }
851
+ // Case 2: If this node is a slot for a Custom Element, search its assigned
852
+ // nodes recursively.
853
+ else if (node.localName === 'slot') {
854
+ const assignedElements = node.assignedElements({
855
+ flatten: true,
856
+ });
857
+ if (!forward)
858
+ assignedElements.reverse();
859
+ for (const assignedElement of assignedElements) {
860
+ const focusableEl = findFocusableElement(assignedElement, forward);
861
+ if (focusableEl)
862
+ return focusableEl;
863
+ }
864
+ }
865
+ // Case 3: this is a regular Light DOM node. Search its subtree.
866
+ else {
867
+ // Descend into this subtree.
868
+ let next = getNextChildEl(node, forward);
869
+ // Traverse siblings, searching the subtree of each one
870
+ // for focusable elements.
871
+ while (next) {
872
+ const focusableEl = findFocusableElement(next, forward);
873
+ if (focusableEl)
874
+ return focusableEl;
875
+ next = getNextSiblingEl(next, forward);
876
+ }
877
+ }
878
+ }
879
+ // If we’re walking backward, we want to check the node’s entire subtree
880
+ // before checking the node itself. If this node is focusable, return it.
881
+ if (!forward && isFocusable(node))
882
+ return node;
883
+ return null;
884
+ }
885
+ function getNextChildEl(node, forward) {
886
+ return forward ? node.firstElementChild : node.lastElementChild;
887
+ }
888
+ function getNextSiblingEl(el, forward) {
889
+ return forward ? el.nextElementSibling : el.previousElementSibling;
890
+ }
973
891
  /**
974
- * Private event handler used when listening to some specific key presses
975
- * (namely ESCAPE and TAB)
976
- *
977
- * @access private
978
- * @param {Event} event
892
+ * Determine if an element is hidden from the user.
979
893
  */
980
- A11yDialog.prototype._bindKeypress = function (event) {
981
- // This is an escape hatch in case there are nested dialogs, so the keypresses
982
- // are only reacted to for the most recent one
983
- const focused = document.activeElement;
984
- if (focused && focused.closest('[aria-modal="true"]') !== this.$el) return
985
-
986
- // If the dialog is shown and the ESCAPE key is being pressed, prevent any
987
- // further effects from the ESCAPE key and hide the dialog, unless its role
988
- // is 'alertdialog', which should be modal
989
- if (
990
- this.shown &&
991
- event.key === ESCAPE_KEY &&
992
- this.$el.getAttribute('role') !== 'alertdialog'
993
- ) {
994
- event.preventDefault();
995
- this.hide(event);
996
- }
997
-
998
- // If the dialog is shown and the TAB key is being pressed, make sure the
999
- // focus stays trapped within the dialog element
1000
- if (this.shown && event.key === TAB_KEY) {
1001
- trapTabKey(this.$el, event);
1002
- }
894
+ const isHidden = (el) => {
895
+ // Browsers hide all non-<summary> descendants of closed <details> elements
896
+ // from user interaction, but those non-<summary> elements may still match our
897
+ // focusable-selectors and may still have dimensions, so we need a special
898
+ // case to ignore them.
899
+ if (el.matches('details:not([open]) *') &&
900
+ !el.matches('details>summary:first-of-type'))
901
+ return true;
902
+ // If this element has no painted dimensions, it's hidden.
903
+ return !(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
1003
904
  };
1004
-
1005
905
  /**
1006
- * Private event handler used when making sure the focus stays within the
1007
- * currently open dialog
1008
- *
1009
- * @access private
1010
- * @param {Event} event
906
+ * Determine if an element is focusable and has user-visible painted dimensions.
1011
907
  */
1012
- A11yDialog.prototype._maintainFocus = function (event) {
1013
- // If the dialog is shown and the focus is not within a dialog element (either
1014
- // this one or another one in case of nested dialogs) or within an element
1015
- // with the `data-a11y-dialog-focus-trap-ignore` attribute, move it back to
1016
- // its first focusable child.
1017
- // See: https://github.com/KittyGiraudel/a11y-dialog/issues/177
1018
- if (
1019
- this.shown &&
1020
- !event.target.closest('[aria-modal="true"]') &&
1021
- !event.target.closest('[data-a11y-dialog-ignore-focus-trap]')
1022
- ) {
1023
- moveFocusToDialog(this.$el);
1024
- }
908
+ const isFocusable = (el) => {
909
+ // A shadow host that delegates focus will never directly receive focus,
910
+ // even with `tabindex=0`. Consider our <fancy-button> custom element, which
911
+ // delegates focus to its shadow button:
912
+ //
913
+ // <fancy-button tabindex="0">
914
+ // #shadow-root
915
+ // <button><slot></slot></button>
916
+ // </fancy-button>
917
+ //
918
+ // The browser acts as as if there is only one focusable element – the shadow
919
+ // button. Our library should behave the same way.
920
+ if (el.shadowRoot?.delegatesFocus)
921
+ return false;
922
+ return el.matches(focusableSelectors.join(',')) && !isHidden(el);
1025
923
  };
1026
-
1027
- /**
1028
- * Convert a NodeList into an array
1029
- *
1030
- * @param {NodeList} collection
1031
- * @return {Array<Element>}
1032
- */
1033
- function toArray(collection) {
1034
- return Array.prototype.slice.call(collection)
1035
- }
1036
-
1037
924
  /**
1038
- * Query the DOM for nodes matching the given selector, scoped to context (or
1039
- * the whole document)
1040
- *
1041
- * @param {String} selector
1042
- * @param {Element} [context = document]
1043
- * @return {Array<Element>}
925
+ * Determine if an element can have focusable children. Useful for bailing out
926
+ * early when walking the DOM tree.
927
+ * @example
928
+ * This div is inert, so none of its children can be focused, even though they
929
+ * meet our criteria for what is focusable. Once we check the div, we can skip
930
+ * the rest of the subtree.
931
+ * ```html
932
+ * <div inert>
933
+ * <button>Button</button>
934
+ * <a href="#">Link</a>
935
+ * </div>
936
+ * ```
1044
937
  */
1045
- function $$(selector, context) {
1046
- return toArray((context || document).querySelectorAll(selector))
938
+ function canHaveFocusableChildren(el) {
939
+ // The browser will never send focus into a Shadow DOM if the host element
940
+ // has a negative tabindex. This applies to both slotted Light DOM Shadow DOM
941
+ // children
942
+ if (el.shadowRoot && el.getAttribute('tabindex') === '-1')
943
+ return false;
944
+ // Elemments matching this selector are either hidden entirely from the user,
945
+ // or are visible but unavailable for interaction. Their descentants can never
946
+ // receive focus.
947
+ return !el.matches(':disabled,[hidden],[inert]');
1047
948
  }
1048
-
1049
949
  /**
1050
- * Set the focus to the first element with `autofocus` with the element or the
1051
- * element itself
1052
- *
1053
- * @param {Element} node
950
+ * Get the active element, accounting for Shadow DOM subtrees.
951
+ * @author Cory LaViska
952
+ * @see: https://www.abeautifulsite.net/posts/finding-the-active-element-in-a-shadow-root/
1054
953
  */
1055
- function moveFocusToDialog(node) {
1056
- var focused = node.querySelector('[autofocus]') || node;
1057
-
1058
- focused.focus();
1059
- }
1060
-
1061
- /**
1062
- * Get the focusable children of the given element
1063
- *
1064
- * @param {Element} node
1065
- * @return {Array<Element>}
1066
- */
1067
- function getFocusableChildren(node) {
1068
- return $$(focusableSelectors.join(','), node).filter(function (child) {
1069
- return !!(
1070
- child.offsetWidth ||
1071
- child.offsetHeight ||
1072
- child.getClientRects().length
1073
- )
1074
- })
954
+ function getActiveElement(root = document) {
955
+ const activeEl = root.activeElement;
956
+ if (!activeEl)
957
+ return null;
958
+ // If there’s a shadow root, recursively find the active element within it.
959
+ // If the recursive call returns null, return the active element
960
+ // of the top-level Document.
961
+ if (activeEl.shadowRoot)
962
+ return getActiveElement(activeEl.shadowRoot) || document.activeElement;
963
+ // If not, we can just return the active element
964
+ return activeEl;
1075
965
  }
1076
-
1077
966
  /**
1078
967
  * Trap the focus inside the given element
1079
- *
1080
- * @param {Element} node
1081
- * @param {Event} event
1082
968
  */
1083
- function trapTabKey(node, event) {
1084
- var focusableChildren = getFocusableChildren(node);
1085
- var focusedItemIndex = focusableChildren.indexOf(document.activeElement);
969
+ function trapTabKey(el, event) {
970
+ const [firstFocusableChild, lastFocusableChild] = getFocusableEdges(el);
971
+ // If there are no focusable children in the dialog, prevent the user from
972
+ // tabbing out of it
973
+ if (!firstFocusableChild)
974
+ return event.preventDefault();
975
+ const activeElement = getActiveElement();
976
+ // If the SHIFT key is pressed while tabbing (moving backwards) and the
977
+ // currently focused item is the first one, move the focus to the last
978
+ // focusable item from the dialog element
979
+ if (event.shiftKey && activeElement === firstFocusableChild) {
980
+ // @ts-ignore: we know that `lastFocusableChild` is not null here
981
+ lastFocusableChild.focus();
982
+ event.preventDefault();
983
+ }
984
+ // If the SHIFT key is not pressed (moving forwards) and the currently focused
985
+ // item is the last one, move the focus to the first focusable item from the
986
+ // dialog element
987
+ else if (!event.shiftKey && activeElement === lastFocusableChild) {
988
+ firstFocusableChild.focus();
989
+ event.preventDefault();
990
+ }
991
+ }
1086
992
 
1087
- // If the SHIFT key is being pressed while tabbing (moving backwards) and
1088
- // the currently focused item is the first one, move the focus to the last
1089
- // focusable item from the dialog element
1090
- if (event.shiftKey && focusedItemIndex === 0) {
1091
- focusableChildren[focusableChildren.length - 1].focus();
1092
- event.preventDefault();
1093
- // If the SHIFT key is not being pressed (moving forwards) and the currently
1094
- // focused item is the last one, move the focus to the first focusable item
1095
- // from the dialog element
1096
- } else if (
1097
- !event.shiftKey &&
1098
- focusedItemIndex === focusableChildren.length - 1
1099
- ) {
1100
- focusableChildren[0].focus();
1101
- event.preventDefault();
1102
- }
993
+ class A11yDialog {
994
+ $el;
995
+ id;
996
+ previouslyFocused;
997
+ shown;
998
+ constructor(element) {
999
+ this.$el = element;
1000
+ this.id = this.$el.getAttribute('data-a11y-dialog') || this.$el.id;
1001
+ this.previouslyFocused = null;
1002
+ this.shown = false;
1003
+ this.maintainFocus = this.maintainFocus.bind(this);
1004
+ this.bindKeypress = this.bindKeypress.bind(this);
1005
+ this.handleTriggerClicks = this.handleTriggerClicks.bind(this);
1006
+ this.show = this.show.bind(this);
1007
+ this.hide = this.hide.bind(this);
1008
+ this.$el.setAttribute('aria-hidden', 'true');
1009
+ this.$el.setAttribute('aria-modal', 'true');
1010
+ this.$el.setAttribute('tabindex', '-1');
1011
+ if (!this.$el.hasAttribute('role')) {
1012
+ this.$el.setAttribute('role', 'dialog');
1013
+ }
1014
+ document.addEventListener('click', this.handleTriggerClicks, true);
1015
+ }
1016
+ /**
1017
+ * Destroy the current instance (after making sure the dialog has been hidden)
1018
+ * and remove all associated listeners from dialog openers and closers
1019
+ */
1020
+ destroy() {
1021
+ // Hide the dialog to avoid destroying an open instance
1022
+ this.hide();
1023
+ // Remove the click event delegates for our openers and closers
1024
+ document.removeEventListener('click', this.handleTriggerClicks, true);
1025
+ // Clone and replace the dialog element to prevent memory leaks caused by
1026
+ // event listeners that the author might not have cleaned up.
1027
+ this.$el.replaceWith(this.$el.cloneNode(true));
1028
+ // Dispatch a `destroy` event
1029
+ this.fire('destroy');
1030
+ return this;
1031
+ }
1032
+ /**
1033
+ * Show the dialog element, trap the current focus within it, listen for some
1034
+ * specific key presses and fire all registered callbacks for `show` event
1035
+ */
1036
+ show(event) {
1037
+ // If the dialog is already open, abort
1038
+ if (this.shown)
1039
+ return this;
1040
+ // Keep a reference to the currently focused element to be able to restore
1041
+ // it later
1042
+ this.shown = true;
1043
+ this.$el.removeAttribute('aria-hidden');
1044
+ this.previouslyFocused = getActiveElement();
1045
+ // Due to a long lasting bug in Safari, clicking an interactive element
1046
+ // (like a <button>) does *not* move the focus to that element, which means
1047
+ // `document.activeElement` is whatever element is currently focused (like
1048
+ // an <input>), or the <body> element otherwise. We can work around that
1049
+ // problem by checking whether the focused element is the <body>, and if it,
1050
+ // store the click event target.
1051
+ // See: https://bugs.webkit.org/show_bug.cgi?id=22261
1052
+ if (this.previouslyFocused?.tagName === 'BODY' && event?.target) {
1053
+ this.previouslyFocused = event.target;
1054
+ }
1055
+ // Set the focus to the dialog element
1056
+ // See: https://github.com/KittyGiraudel/a11y-dialog/pull/583
1057
+ if (event?.type === 'focus') {
1058
+ this.maintainFocus(event);
1059
+ }
1060
+ else {
1061
+ moveFocusToDialog(this.$el);
1062
+ }
1063
+ // Bind a focus event listener to the body element to make sure the focus
1064
+ // stays trapped inside the dialog while open, and start listening for some
1065
+ // specific key presses (TAB and ESC)
1066
+ document.body.addEventListener('focus', this.maintainFocus, true);
1067
+ this.$el.addEventListener('keydown', this.bindKeypress, true);
1068
+ // Dispatch a `show` event
1069
+ this.fire('show', event);
1070
+ return this;
1071
+ }
1072
+ /**
1073
+ * Hide the dialog element, restore the focus to the previously active
1074
+ * element, stop listening for some specific key presses and fire all
1075
+ * registered callbacks for `hide` event
1076
+ */
1077
+ hide(event) {
1078
+ // If the dialog is already closed, abort
1079
+ if (!this.shown)
1080
+ return this;
1081
+ this.shown = false;
1082
+ this.$el.setAttribute('aria-hidden', 'true');
1083
+ this.previouslyFocused?.focus?.();
1084
+ // Remove the focus event listener to the body element and stop listening
1085
+ // for specific key presses
1086
+ document.body.removeEventListener('focus', this.maintainFocus, true);
1087
+ this.$el.removeEventListener('keydown', this.bindKeypress, true);
1088
+ // Dispatch a `hide` event
1089
+ this.fire('hide', event);
1090
+ return this;
1091
+ }
1092
+ /**
1093
+ * Register a new callback for the given event type
1094
+ */
1095
+ on(type, handler, options) {
1096
+ this.$el.addEventListener(type, handler, options);
1097
+ return this;
1098
+ }
1099
+ /**
1100
+ * Unregister an existing callback for the given event type
1101
+ */
1102
+ off(type, handler, options) {
1103
+ this.$el.removeEventListener(type, handler, options);
1104
+ return this;
1105
+ }
1106
+ /**
1107
+ * Dispatch a custom event from the DOM element associated with this dialog.
1108
+ * This allows authors to listen for and respond to the events in their own
1109
+ * code
1110
+ */
1111
+ fire(type, event) {
1112
+ this.$el.dispatchEvent(new CustomEvent(type, {
1113
+ detail: event,
1114
+ cancelable: true,
1115
+ }));
1116
+ }
1117
+ /**
1118
+ * Add a delegated event listener for when elememts that open or close the
1119
+ * dialog are clicked, and call `show` or `hide`, respectively
1120
+ */
1121
+ handleTriggerClicks(event) {
1122
+ const target = event.target;
1123
+ // We use `.closest(..)` and not `.matches(..)` here so that clicking
1124
+ // an element nested within a dialog opener does cause the dialog to open
1125
+ if (target.closest(`[data-a11y-dialog-show="${this.id}"]`)) {
1126
+ this.show(event);
1127
+ }
1128
+ if (target.closest(`[data-a11y-dialog-hide="${this.id}"]`) ||
1129
+ (target.closest('[data-a11y-dialog-hide]') &&
1130
+ target.closest('[aria-modal="true"]') === this.$el)) {
1131
+ this.hide(event);
1132
+ }
1133
+ }
1134
+ /**
1135
+ * Private event handler used when listening to some specific key presses
1136
+ * (namely ESC and TAB)
1137
+ */
1138
+ bindKeypress(event) {
1139
+ // This is an escape hatch in case there are nested open dialogs, so that
1140
+ // only the top most dialog gets interacted with
1141
+ if (document.activeElement?.closest('[aria-modal="true"]') !== this.$el) {
1142
+ return;
1143
+ }
1144
+ let hasOpenPopover = false;
1145
+ try {
1146
+ hasOpenPopover = !!this.$el.querySelector('[popover]:not([popover="manual"]):popover-open');
1147
+ }
1148
+ catch {
1149
+ // Run that DOM query in a try/catch because not all browsers support the
1150
+ // `:popover-open` selector, which would cause the whole expression to
1151
+ // fail
1152
+ // See: https://caniuse.com/mdn-css_selectors_popover-open
1153
+ // See: https://github.com/KittyGiraudel/a11y-dialog/pull/578#discussion_r1343215149
1154
+ }
1155
+ // If the dialog is shown and the ESC key is pressed, prevent any further
1156
+ // effects from the ESC key and hide the dialog, unless:
1157
+ // - its role is `alertdialog`, which means it should be modal
1158
+ // - or it contains an open popover, in which case ESC should close it
1159
+ if (event.key === 'Escape' &&
1160
+ this.$el.getAttribute('role') !== 'alertdialog' &&
1161
+ !hasOpenPopover) {
1162
+ event.preventDefault();
1163
+ this.hide(event);
1164
+ }
1165
+ // If the dialog is shown and the TAB key is pressed, make sure the focus
1166
+ // stays trapped within the dialog element
1167
+ if (event.key === 'Tab') {
1168
+ trapTabKey(this.$el, event);
1169
+ }
1170
+ }
1171
+ /**
1172
+ * If the dialog is shown and the focus is not within a dialog element (either
1173
+ * this one or another one in case of nested dialogs) or attribute, move it
1174
+ * back to the dialog container
1175
+ * See: https://github.com/KittyGiraudel/a11y-dialog/issues/177
1176
+ */
1177
+ maintainFocus(event) {
1178
+ const target = event.target;
1179
+ if (!target.closest('[aria-modal="true"], [data-a11y-dialog-ignore-focus-trap]')) {
1180
+ moveFocusToDialog(this.$el);
1181
+ }
1182
+ }
1103
1183
  }
1104
1184
 
1105
1185
  function instantiateDialogs() {
1106
- $$('[data-a11y-dialog]').forEach(function (node) {
1107
- new A11yDialog(node);
1108
- });
1186
+ for (const el of document.querySelectorAll('[data-a11y-dialog]')) {
1187
+ new A11yDialog(el);
1188
+ }
1109
1189
  }
1110
-
1111
1190
  if (typeof document !== 'undefined') {
1112
- if (document.readyState === 'loading') {
1113
- document.addEventListener('DOMContentLoaded', instantiateDialogs);
1114
- } else {
1115
- if (window.requestAnimationFrame) {
1116
- window.requestAnimationFrame(instantiateDialogs);
1117
- } else {
1118
- window.setTimeout(instantiateDialogs, 16);
1191
+ if (document.readyState === 'loading') {
1192
+ document.addEventListener('DOMContentLoaded', instantiateDialogs);
1193
+ }
1194
+ else {
1195
+ instantiateDialogs();
1119
1196
  }
1120
- }
1121
1197
  }
1122
1198
 
1123
1199
  var CONTAINER_ID = "__authsignal-popup-container";
@@ -1129,16 +1205,16 @@ var DEFAULT_WIDTH = "385px";
1129
1205
  var INITIAL_HEIGHT = "384px";
1130
1206
  var PopupHandler = /** @class */ (function () {
1131
1207
  function PopupHandler(_a) {
1132
- var width = _a.width;
1208
+ var width = _a.width, isClosable = _a.isClosable;
1133
1209
  this.popup = null;
1134
1210
  if (document.querySelector("#".concat(CONTAINER_ID))) {
1135
1211
  throw new Error("Multiple instances of Authsignal popup is not supported.");
1136
1212
  }
1137
- this.create({ width: width });
1213
+ this.create({ width: width, isClosable: isClosable });
1138
1214
  }
1139
1215
  PopupHandler.prototype.create = function (_a) {
1140
1216
  var _this = this;
1141
- var _b = _a.width, width = _b === void 0 ? DEFAULT_WIDTH : _b;
1217
+ var _b = _a.width, width = _b === void 0 ? DEFAULT_WIDTH : _b, _c = _a.isClosable, isClosable = _c === void 0 ? true : _c;
1142
1218
  var isWidthValidCSSValue = CSS.supports("width", width);
1143
1219
  var popupWidth = width;
1144
1220
  if (!isWidthValidCSSValue) {
@@ -1149,10 +1225,15 @@ var PopupHandler = /** @class */ (function () {
1149
1225
  var container = document.createElement("div");
1150
1226
  container.setAttribute("id", CONTAINER_ID);
1151
1227
  container.setAttribute("aria-hidden", "true");
1228
+ if (!isClosable) {
1229
+ container.setAttribute("role", "alertdialog");
1230
+ }
1152
1231
  // Create dialog overlay
1153
1232
  var overlay = document.createElement("div");
1154
1233
  overlay.setAttribute("id", OVERLAY_ID);
1155
- overlay.setAttribute("data-a11y-dialog-hide", "true");
1234
+ if (isClosable) {
1235
+ overlay.setAttribute("data-a11y-dialog-hide", "true");
1236
+ }
1156
1237
  // Create dialog content
1157
1238
  var content = document.createElement("div");
1158
1239
  content.setAttribute("id", CONTENT_ID);
@@ -1206,11 +1287,11 @@ var PopupHandler = /** @class */ (function () {
1206
1287
  }
1207
1288
  this.popup.hide();
1208
1289
  };
1209
- PopupHandler.prototype.on = function (event, handler) {
1290
+ PopupHandler.prototype.on = function (event, listener) {
1210
1291
  if (!this.popup) {
1211
1292
  throw new Error("Popup is not initialized");
1212
1293
  }
1213
- this.popup.on(event, handler);
1294
+ this.popup.on(event, listener);
1214
1295
  };
1215
1296
  return PopupHandler;
1216
1297
  }());
@@ -1305,7 +1386,7 @@ var Authsignal = /** @class */ (function () {
1305
1386
  Authsignal.prototype.launchWithPopup = function (url, options) {
1306
1387
  var _this = this;
1307
1388
  var popupOptions = options.popupOptions;
1308
- var popupHandler = new PopupHandler({ width: popupOptions === null || popupOptions === void 0 ? void 0 : popupOptions.width });
1389
+ var popupHandler = new PopupHandler({ width: popupOptions === null || popupOptions === void 0 ? void 0 : popupOptions.width, isClosable: popupOptions === null || popupOptions === void 0 ? void 0 : popupOptions.isClosable });
1309
1390
  var popupUrl = "".concat(url, "&mode=popup");
1310
1391
  popupHandler.show({ url: popupUrl });
1311
1392
  return new Promise(function (resolve) {
package/dist/index.min.js CHANGED
@@ -1 +1 @@
1
- var authsignal=function(t){"use strict";let e;const n=new Uint8Array(16);function o(){if(!e&&(e="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!e))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return e(n)}const i=[];for(let t=0;t<256;++t)i.push((t+256).toString(16).slice(1));var r={randomUUID:"undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function a(t,e,n){if(r.randomUUID&&!e&&!t)return r.randomUUID();const a=(t=t||{}).random||(t.rng||o)();if(a[6]=15&a[6]|64,a[8]=63&a[8]|128,e){n=n||0;for(let t=0;t<16;++t)e[n+t]=a[t];return e}return function(t,e=0){return(i[t[e+0]]+i[t[e+1]]+i[t[e+2]]+i[t[e+3]]+"-"+i[t[e+4]]+i[t[e+5]]+"-"+i[t[e+6]]+i[t[e+7]]+"-"+i[t[e+8]]+i[t[e+9]]+"-"+i[t[e+10]]+i[t[e+11]]+i[t[e+12]]+i[t[e+13]]+i[t[e+14]]+i[t[e+15]]).toLowerCase()}(a)}var s=function(t){var e=t.name,n=t.value,o=t.expire,i=t.domain,r=t.secure,a=o===1/0?" expires=Fri, 31 Dec 9999 23:59:59 GMT":"; max-age="+o;document.cookie=encodeURIComponent(e)+"="+n+"; path=/;"+a+(i?"; domain="+i:"")+(r?"; secure":"")};function c(t,e){var n={};for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&e.indexOf(o)<0&&(n[o]=t[o]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(o=Object.getOwnPropertySymbols(t);i<o.length;i++)e.indexOf(o[i])<0&&Object.prototype.propertyIsEnumerable.call(t,o[i])&&(n[o[i]]=t[o[i]])}return n}function u(t,e,n,o){return new(n||(n=Promise))((function(i,r){function a(t){try{c(o.next(t))}catch(t){r(t)}}function s(t){try{c(o.throw(t))}catch(t){r(t)}}function c(t){var e;t.done?i(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(a,s)}c((o=o.apply(t,e||[])).next())}))}function d(t,e){var n,o,i,r,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return r={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(r[Symbol.iterator]=function(){return this}),r;function s(r){return function(s){return function(r){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,o&&(i=2&r[0]?o.return:r[0]?o.throw||((i=o.return)&&i.call(o),0):o.next)&&!(i=i.call(o,r[1])).done)return i;switch(o=0,i&&(r=[2&r[0],i.value]),r[0]){case 0:case 1:i=r;break;case 4:return a.label++,{value:r[1],done:!1};case 5:a.label++,o=r[1],r=[0];continue;case 7:r=a.ops.pop(),a.trys.pop();continue;default:if(!(i=a.trys,(i=i.length>0&&i[i.length-1])||6!==r[0]&&2!==r[0])){a=0;continue}if(3===r[0]&&(!i||r[1]>i[0]&&r[1]<i[3])){a.label=r[1];break}if(6===r[0]&&a.label<i[1]){a.label=i[1],i=r;break}if(i&&a.label<i[2]){a.label=i[2],a.ops.push(r);break}i[2]&&a.ops.pop(),a.trys.pop();continue}r=e.call(t,a)}catch(t){r=[6,t],o=0}finally{n=i=0}if(5&r[0])throw r[1];return{value:r[0]?r[1]:void 0,done:!0}}([r,s])}}}function l(t){const e=new Uint8Array(t);let n="";for(const t of e)n+=String.fromCharCode(t);return btoa(n).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function h(t){const e=t.replace(/-/g,"+").replace(/_/g,"/"),n=(4-e.length%4)%4,o=e.padEnd(e.length+n,"="),i=atob(o),r=new ArrayBuffer(i.length),a=new Uint8Array(r);for(let t=0;t<i.length;t++)a[t]=i.charCodeAt(t);return r}function p(){return void 0!==window?.PublicKeyCredential&&"function"==typeof window.PublicKeyCredential}function f(t){const{id:e}=t;return{...t,id:h(e),transports:t.transports}}function w(t){return"localhost"===t||/^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(t)}t.AuthsignalWindowMessage=void 0,(t.AuthsignalWindowMessage||(t.AuthsignalWindowMessage={})).AUTHSIGNAL_CLOSE_POPUP="AUTHSIGNAL_CLOSE_POPUP";class m extends Error{constructor({message:t,code:e,cause:n,name:o}){super(t,{cause:n}),this.name=o??n.name,this.code=e}}const y=new class{createNewAbortSignal(){if(this.controller){const t=new Error("Cancelling existing WebAuthn API call for new one");t.name="AbortError",this.controller.abort(t)}const t=new AbortController;return this.controller=t,t.signal}cancelCeremony(){if(this.controller){const t=new Error("Manually cancelling existing WebAuthn API call");t.name="AbortError",this.controller.abort(t),this.controller=void 0}}},b=["cross-platform","platform"];function g(t){if(t&&!(b.indexOf(t)<0))return t}async function v(t){if(!p())throw new Error("WebAuthn is not supported in this browser");var e;const n={publicKey:{...t,challenge:h(t.challenge),user:{...t.user,id:(e=t.user.id,(new TextEncoder).encode(e))},excludeCredentials:t.excludeCredentials?.map(f)}};let o;n.signal=y.createNewAbortSignal();try{o=await navigator.credentials.create(n)}catch(t){throw function({error:t,options:e}){const{publicKey:n}=e;if(!n)throw Error("options was missing required publicKey property");if("AbortError"===t.name){if(e.signal instanceof AbortSignal)return new m({message:"Registration ceremony was sent an abort signal",code:"ERROR_CEREMONY_ABORTED",cause:t})}else if("ConstraintError"===t.name){if(!0===n.authenticatorSelection?.requireResidentKey)return new m({message:"Discoverable credentials were required but no available authenticator supported it",code:"ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT",cause:t});if("required"===n.authenticatorSelection?.userVerification)return new m({message:"User verification was required but no available authenticator supported it",code:"ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT",cause:t})}else{if("InvalidStateError"===t.name)return new m({message:"The authenticator was previously registered",code:"ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED",cause:t});if("NotAllowedError"===t.name)return new m({message:t.message,code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:t});if("NotSupportedError"===t.name)return 0===n.pubKeyCredParams.filter((t=>"public-key"===t.type)).length?new m({message:'No entry in pubKeyCredParams was of type "public-key"',code:"ERROR_MALFORMED_PUBKEYCREDPARAMS",cause:t}):new m({message:"No available authenticator supported any of the specified pubKeyCredParams algorithms",code:"ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG",cause:t});if("SecurityError"===t.name){const e=window.location.hostname;if(!w(e))return new m({message:`${window.location.hostname} is an invalid domain`,code:"ERROR_INVALID_DOMAIN",cause:t});if(n.rp.id!==e)return new m({message:`The RP ID "${n.rp.id}" is invalid for this domain`,code:"ERROR_INVALID_RP_ID",cause:t})}else if("TypeError"===t.name){if(n.user.id.byteLength<1||n.user.id.byteLength>64)return new m({message:"User ID was not between 1 and 64 characters",code:"ERROR_INVALID_USER_ID_LENGTH",cause:t})}else if("UnknownError"===t.name)return new m({message:"The authenticator was unable to process the specified options, or could not create a new credential",code:"ERROR_AUTHENTICATOR_GENERAL_ERROR",cause:t})}return t}({error:t,options:n})}if(!o)throw new Error("Registration was not completed");const{id:i,rawId:r,response:a,type:s}=o;let c,u,d,b;if("function"==typeof a.getTransports&&(c=a.getTransports()),"function"==typeof a.getPublicKeyAlgorithm)try{u=a.getPublicKeyAlgorithm()}catch(t){_("getPublicKeyAlgorithm()",t)}if("function"==typeof a.getPublicKey)try{const t=a.getPublicKey();null!==t&&(d=l(t))}catch(t){_("getPublicKey()",t)}if("function"==typeof a.getAuthenticatorData)try{b=l(a.getAuthenticatorData())}catch(t){_("getAuthenticatorData()",t)}return{id:i,rawId:l(r),response:{attestationObject:l(a.attestationObject),clientDataJSON:l(a.clientDataJSON),transports:c,publicKeyAlgorithm:u,publicKey:d,authenticatorData:b},type:s,clientExtensionResults:o.getClientExtensionResults(),authenticatorAttachment:g(o.authenticatorAttachment)}}function _(t,e){console.warn(`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${t}. You should report this error to them.\n`,e)}async function E(t,e=!1){if(!p())throw new Error("WebAuthn is not supported in this browser");let n;0!==t.allowCredentials?.length&&(n=t.allowCredentials?.map(f));const o={...t,challenge:h(t.challenge),allowCredentials:n},i={};if(e){if(!await function(){const t=window.PublicKeyCredential;return void 0===t.isConditionalMediationAvailable?new Promise((t=>t(!1))):t.isConditionalMediationAvailable()}())throw Error("Browser does not support WebAuthn autofill");if(document.querySelectorAll("input[autocomplete$='webauthn']").length<1)throw Error('No <input> with "webauthn" as the only or last value in its `autocomplete` attribute was detected');i.mediation="conditional",o.allowCredentials=[]}let r;i.publicKey=o,i.signal=y.createNewAbortSignal();try{r=await navigator.credentials.get(i)}catch(t){throw function({error:t,options:e}){const{publicKey:n}=e;if(!n)throw Error("options was missing required publicKey property");if("AbortError"===t.name){if(e.signal instanceof AbortSignal)return new m({message:"Authentication ceremony was sent an abort signal",code:"ERROR_CEREMONY_ABORTED",cause:t})}else{if("NotAllowedError"===t.name)return new m({message:t.message,code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:t});if("SecurityError"===t.name){const e=window.location.hostname;if(!w(e))return new m({message:`${window.location.hostname} is an invalid domain`,code:"ERROR_INVALID_DOMAIN",cause:t});if(n.rpId!==e)return new m({message:`The RP ID "${n.rpId}" is invalid for this domain`,code:"ERROR_INVALID_RP_ID",cause:t})}else if("UnknownError"===t.name)return new m({message:"The authenticator was unable to process the specified options, or could not create a new assertion signature",code:"ERROR_AUTHENTICATOR_GENERAL_ERROR",cause:t})}return t}({error:t,options:i})}if(!r)throw new Error("Authentication was not completed");const{id:a,rawId:s,response:c,type:u}=r;let d;var b;return c.userHandle&&(b=c.userHandle,d=new TextDecoder("utf-8").decode(b)),{id:a,rawId:l(s),response:{authenticatorData:l(c.authenticatorData),clientDataJSON:l(c.clientDataJSON),signature:l(c.signature),userHandle:d},type:u,clientExtensionResults:r.getClientExtensionResults(),authenticatorAttachment:g(r.authenticatorAttachment)}}var A=function(){function t(t){var e=t.baseUrl,n=t.tenantId;this.tenantId=n,this.baseUrl=e}return t.prototype.registrationOptions=function(t){var e=t.token,n=t.userName;return u(this,void 0,void 0,(function(){return d(this,(function(t){switch(t.label){case 0:return[4,fetch("".concat(this.baseUrl,"/client/user-authenticators/passkey/registration-options"),{method:"POST",headers:this.buildHeaders(e),body:JSON.stringify({username:n})})];case 1:return[2,t.sent().json()]}}))}))},t.prototype.authenticationOptions=function(t){var e=t.token;return u(this,void 0,void 0,(function(){return d(this,(function(t){switch(t.label){case 0:return[4,fetch("".concat(this.baseUrl,"/client/user-authenticators/passkey/authentication-options"),{method:"POST",headers:this.buildHeaders(e),body:JSON.stringify({})})];case 1:return[2,t.sent().json()]}}))}))},t.prototype.addAuthenticator=function(t){var e=t.token,n=c(t,["token"]);return u(this,void 0,void 0,(function(){return d(this,(function(t){switch(t.label){case 0:return[4,fetch("".concat(this.baseUrl,"/client/user-authenticators/passkey"),{method:"POST",headers:this.buildHeaders(e),body:JSON.stringify(n)})];case 1:return[2,t.sent().json()]}}))}))},t.prototype.verify=function(t){var e=t.token,n=c(t,["token"]);return u(this,void 0,void 0,(function(){return d(this,(function(t){switch(t.label){case 0:return[4,fetch("".concat(this.baseUrl,"/client/verify/passkey"),{method:"POST",headers:this.buildHeaders(e),body:JSON.stringify(n)})];case 1:return[2,t.sent().json()]}}))}))},t.prototype.buildHeaders=function(t){return{"Content-Type":"application/json",Authorization:t?"Bearer ".concat(t):"Basic ".concat(window.btoa(encodeURIComponent(this.tenantId)))}},t}(),R=function(){function t(t){var e=t.baseUrl,n=t.tenantId;this.api=new A({baseUrl:e,tenantId:n})}return t.prototype.signUp=function(t){var e=t.userName,n=t.token;return u(this,void 0,void 0,(function(){var t,o,i;return d(this,(function(r){switch(r.label){case 0:return[4,this.api.registrationOptions({userName:e,token:n})];case 1:return[4,v((t=r.sent()).options)];case 2:return o=r.sent(),[4,this.api.addAuthenticator({challengeId:t.challengeId,registrationCredential:o,token:n})];case 3:return[2,null==(i=r.sent())?void 0:i.accessToken]}}))}))},t.prototype.signIn=function(t){return u(this,void 0,void 0,(function(){var e,n,o;return d(this,(function(i){switch(i.label){case 0:if((null==t?void 0:t.token)&&t.autofill)throw new Error("Autofill is not supported when providing a token");return[4,this.api.authenticationOptions({token:null==t?void 0:t.token})];case 1:return[4,E((e=i.sent()).options,null==t?void 0:t.autofill)];case 2:return n=i.sent(),[4,this.api.verify({challengeId:e.challengeId,authenticationCredential:n,token:null==t?void 0:t.token})];case 3:return[2,null==(o=i.sent())?void 0:o.accessToken]}}))}))},t}(),O=function(){function t(){this.windowRef=null}return t.prototype.show=function(t){var e=t.url,n=t.width,o=void 0===n?400:n,i=t.height,r=function(t){var e=t.url,n=t.width,o=t.height,i=t.win;if(!i.top)return null;var r=i.top.outerHeight/2+i.top.screenY-o/2,a=i.top.outerWidth/2+i.top.screenX-n/2;return window.open(e,"","toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=".concat(n,", height=").concat(o,", top=").concat(r,", left=").concat(a))}({url:e,width:o,height:void 0===i?500:i,win:window});if(!r)throw new Error("Window is not initialized");return this.windowRef=r,r},t.prototype.close=function(){if(!this.windowRef)throw new Error("Window is not initialized");this.windowRef.close()},t}();var I=['a[href]:not([tabindex^="-"])','area[href]:not([tabindex^="-"])','input:not([type="hidden"]):not([type="radio"]):not([disabled]):not([tabindex^="-"])','input[type="radio"]:not([disabled]):not([tabindex^="-"])','select:not([disabled]):not([tabindex^="-"])','textarea:not([disabled]):not([tabindex^="-"])','button:not([disabled]):not([tabindex^="-"])','iframe:not([tabindex^="-"])','audio[controls]:not([tabindex^="-"])','video[controls]:not([tabindex^="-"])','[contenteditable]:not([tabindex^="-"])','[tabindex]:not([tabindex^="-"])'];function S(t){this._show=this.show.bind(this),this._hide=this.hide.bind(this),this._maintainFocus=this._maintainFocus.bind(this),this._bindKeypress=this._bindKeypress.bind(this),this.$el=t,this.shown=!1,this._id=this.$el.getAttribute("data-a11y-dialog")||this.$el.id,this._previouslyFocused=null,this._listeners={},this.create()}function C(t,e){return n=(e||document).querySelectorAll(t),Array.prototype.slice.call(n);var n}function P(t){(t.querySelector("[autofocus]")||t).focus()}function k(){C("[data-a11y-dialog]").forEach((function(t){new S(t)}))}S.prototype.create=function(){this.$el.setAttribute("aria-hidden",!0),this.$el.setAttribute("aria-modal",!0),this.$el.setAttribute("tabindex",-1),this.$el.hasAttribute("role")||this.$el.setAttribute("role","dialog"),this._openers=C('[data-a11y-dialog-show="'+this._id+'"]'),this._openers.forEach(function(t){t.addEventListener("click",this._show)}.bind(this));const t=this.$el;return this._closers=C("[data-a11y-dialog-hide]",this.$el).filter((function(e){return e.closest('[aria-modal="true"], [data-a11y-dialog]')===t})).concat(C('[data-a11y-dialog-hide="'+this._id+'"]')),this._closers.forEach(function(t){t.addEventListener("click",this._hide)}.bind(this)),this._fire("create"),this},S.prototype.show=function(t){return this.shown||(this._previouslyFocused=document.activeElement,this.$el.removeAttribute("aria-hidden"),this.shown=!0,P(this.$el),document.body.addEventListener("focus",this._maintainFocus,!0),document.addEventListener("keydown",this._bindKeypress),this._fire("show",t)),this},S.prototype.hide=function(t){return this.shown?(this.shown=!1,this.$el.setAttribute("aria-hidden","true"),this._previouslyFocused&&this._previouslyFocused.focus&&this._previouslyFocused.focus(),document.body.removeEventListener("focus",this._maintainFocus,!0),document.removeEventListener("keydown",this._bindKeypress),this._fire("hide",t),this):this},S.prototype.destroy=function(){return this.hide(),this._openers.forEach(function(t){t.removeEventListener("click",this._show)}.bind(this)),this._closers.forEach(function(t){t.removeEventListener("click",this._hide)}.bind(this)),this._fire("destroy"),this._listeners={},this},S.prototype.on=function(t,e){return void 0===this._listeners[t]&&(this._listeners[t]=[]),this._listeners[t].push(e),this},S.prototype.off=function(t,e){var n=(this._listeners[t]||[]).indexOf(e);return n>-1&&this._listeners[t].splice(n,1),this},S.prototype._fire=function(t,e){var n=this._listeners[t]||[],o=new CustomEvent(t,{detail:e});this.$el.dispatchEvent(o),n.forEach(function(t){t(this.$el,e)}.bind(this))},S.prototype._bindKeypress=function(t){const e=document.activeElement;e&&e.closest('[aria-modal="true"]')!==this.$el||(this.shown&&"Escape"===t.key&&"alertdialog"!==this.$el.getAttribute("role")&&(t.preventDefault(),this.hide(t)),this.shown&&"Tab"===t.key&&function(t,e){var n=function(t){return C(I.join(","),t).filter((function(t){return!!(t.offsetWidth||t.offsetHeight||t.getClientRects().length)}))}(t),o=n.indexOf(document.activeElement);e.shiftKey&&0===o?(n[n.length-1].focus(),e.preventDefault()):e.shiftKey||o!==n.length-1||(n[0].focus(),e.preventDefault())}(this.$el,t))},S.prototype._maintainFocus=function(t){!this.shown||t.target.closest('[aria-modal="true"]')||t.target.closest("[data-a11y-dialog-ignore-focus-trap]")||P(this.$el)},"undefined"!=typeof document&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",k):window.requestAnimationFrame?window.requestAnimationFrame(k):window.setTimeout(k,16));var x="__authsignal-popup-container",U="__authsignal-popup-content",T="__authsignal-popup-overlay",N="__authsignal-popup-style",D="__authsignal-popup-iframe",L="385px",K=function(){function t(t){var e=t.width;if(this.popup=null,document.querySelector("#".concat(x)))throw new Error("Multiple instances of Authsignal popup is not supported.");this.create({width:e})}return t.prototype.create=function(t){var e=this,n=t.width,o=void 0===n?L:n,i=o;CSS.supports("width",o)||(console.warn("Invalid CSS value for `popupOptions.width`. Using default value instead."),i=L);var r=document.createElement("div");r.setAttribute("id",x),r.setAttribute("aria-hidden","true");var a=document.createElement("div");a.setAttribute("id",T),a.setAttribute("data-a11y-dialog-hide","true");var s=document.createElement("div");s.setAttribute("id",U),document.body.appendChild(r);var c=document.createElement("style");c.setAttribute("id",N),c.textContent="\n #".concat(x,",\n #").concat(T," {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n }\n\n #").concat(x," {\n z-index: 2147483647;\n display: flex;\n }\n\n #").concat(x,"[aria-hidden='true'] {\n display: none;\n }\n\n #").concat(T," {\n background-color: rgba(0, 0, 0, 0.18);\n }\n\n #").concat(U," {\n margin: auto;\n z-index: 2147483647;\n position: relative;\n background-color: transparent;\n border-radius: 8px;\n width: ").concat(i,";\n }\n\n #").concat(U," iframe {\n width: 1px;\n min-width: 100%;\n border-radius: inherit;\n max-height: 95vh;\n height: ").concat("384px",";\n }\n "),document.head.insertAdjacentElement("beforeend",c),r.appendChild(a),r.appendChild(s),this.popup=new S(r),this.popup.on("hide",(function(){e.destroy()}))},t.prototype.destroy=function(){var t=document.querySelector("#".concat(x)),e=document.querySelector("#".concat(N));t&&e&&(document.body.removeChild(t),document.head.removeChild(e)),window.removeEventListener("message",$)},t.prototype.show=function(t){var e,n=t.url;if(!this.popup)throw new Error("Popup is not initialized");var o=document.createElement("iframe");o.setAttribute("id",D),o.setAttribute("name","authsignal"),o.setAttribute("title","Authsignal multi-factor authentication"),o.setAttribute("src",n),o.setAttribute("frameborder","0"),o.setAttribute("allow","publickey-credentials-get *; publickey-credentials-create *; clipboard-write");var i=document.querySelector("#".concat(U));i&&i.appendChild(o),window.addEventListener("message",$),null===(e=this.popup)||void 0===e||e.show()},t.prototype.close=function(){if(!this.popup)throw new Error("Popup is not initialized");this.popup.hide()},t.prototype.on=function(t,e){if(!this.popup)throw new Error("Popup is not initialized");this.popup.on(t,e)},t}();function $(t){var e=document.querySelector("#".concat(D));e&&t.data.height&&(e.style.height=t.data.height+"px")}var H="4a08uqve",W=function(){function e(t){var e=t.cookieDomain,n=t.cookieName,o=void 0===n?"__as_aid":n,i=t.baseUrl,r=void 0===i?"https://api.authsignal.com/v1":i,c=t.tenantId;if(this.anonymousId="",this.profilingId="",this.cookieDomain="",this.anonymousIdCookieName="",this._token=void 0,this.cookieDomain=e||document.location.hostname.replace("www.",""),this.anonymousIdCookieName=o,!c)throw new Error("tenantId is required");this.passkey=new R({tenantId:c,baseUrl:r});var u,d=(u=this.anonymousIdCookieName)&&decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*"+encodeURIComponent(u).replace(/[\-\.\+\*]/g,"\\$&")+"\\s*\\=\\s*([^;]*).*$)|^.*$"),"$1"))||null;d?this.anonymousId=d:(this.anonymousId=a(),s({name:this.anonymousIdCookieName,value:this.anonymousId,expire:1/0,domain:this.cookieDomain,secure:"http:"!==document.location.protocol}))}return e.prototype.launch=function(t,e){switch(null==e?void 0:e.mode){case"window":return this.launchWithWindow(t,e);case"popup":return this.launchWithPopup(t,e);default:this.launchWithRedirect(t)}},e.prototype.initAdvancedProfiling=function(t){var e=a();this.profilingId=e,s({name:"__as_pid",value:e,expire:1/0,domain:this.cookieDomain,secure:"http:"!==document.location.protocol});var n=t?"".concat(t,"/fp/tags.js?org_id=").concat(H,"&session_id=").concat(e):"https://h.online-metrix.net/fp/tags.js?org_id=".concat(H,"&session_id=").concat(e),o=document.createElement("script");o.src=n,o.async=!1,o.id="as_adv_profile",document.head.appendChild(o);var i=document.createElement("noscript");i.setAttribute("id","as_adv_profile_pixel"),i.setAttribute("aria-hidden","true");var r=document.createElement("iframe"),c=t?"".concat(t,"/fp/tags?org_id=").concat(H,"&session_id=").concat(e):"https://h.online-metrix.net/fp/tags?org_id=".concat(H,"&session_id=").concat(e);r.setAttribute("id","as_adv_profile_pixel"),r.setAttribute("src",c),r.setAttribute("style","width: 100px; height: 100px; border: 0; position: absolute; top: -5000px;"),i&&(i.appendChild(r),document.body.prepend(i))},e.prototype.launchWithRedirect=function(t){window.location.href=t},e.prototype.launchWithPopup=function(e,n){var o=this,i=n.popupOptions,r=new K({width:null==i?void 0:i.width}),a="".concat(e,"&mode=popup");return r.show({url:a}),new Promise((function(e){r.on("hide",(function(){e({token:o._token})})),window.addEventListener("message",(function(e){var n=null;try{n=JSON.parse(e.data)}catch(t){}(null==n?void 0:n.event)===t.AuthsignalWindowMessage.AUTHSIGNAL_CLOSE_POPUP&&(o._token=n.token,r.close())}),!1)}))},e.prototype.launchWithWindow=function(e,n){var o=this,i=n.windowOptions,r=new O,a="".concat(e,"&mode=popup");return r.show({url:a,width:null==i?void 0:i.width,height:null==i?void 0:i.height}),new Promise((function(e){window.addEventListener("message",(function(n){var i=null;try{i=JSON.parse(n.data)}catch(t){}(null==i?void 0:i.event)===t.AuthsignalWindowMessage.AUTHSIGNAL_CLOSE_POPUP&&(o._token=i.token,r.close(),e({token:o._token}))}),!1)}))},e}();return t.Authsignal=W,Object.defineProperty(t,"__esModule",{value:!0}),t}({});
1
+ var authsignal=function(t){"use strict";let e;const n=new Uint8Array(16);function o(){if(!e&&(e="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!e))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return e(n)}const i=[];for(let t=0;t<256;++t)i.push((t+256).toString(16).slice(1));var r={randomUUID:"undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function a(t,e,n){if(r.randomUUID&&!e&&!t)return r.randomUUID();const a=(t=t||{}).random||(t.rng||o)();if(a[6]=15&a[6]|64,a[8]=63&a[8]|128,e){n=n||0;for(let t=0;t<16;++t)e[n+t]=a[t];return e}return function(t,e=0){return(i[t[e+0]]+i[t[e+1]]+i[t[e+2]]+i[t[e+3]]+"-"+i[t[e+4]]+i[t[e+5]]+"-"+i[t[e+6]]+i[t[e+7]]+"-"+i[t[e+8]]+i[t[e+9]]+"-"+i[t[e+10]]+i[t[e+11]]+i[t[e+12]]+i[t[e+13]]+i[t[e+14]]+i[t[e+15]]).toLowerCase()}(a)}var s=function(t){var e=t.name,n=t.value,o=t.expire,i=t.domain,r=t.secure,a=o===1/0?" expires=Fri, 31 Dec 9999 23:59:59 GMT":"; max-age="+o;document.cookie=encodeURIComponent(e)+"="+n+"; path=/;"+a+(i?"; domain="+i:"")+(r?"; secure":"")};function c(t,e){var n={};for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&e.indexOf(o)<0&&(n[o]=t[o]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(o=Object.getOwnPropertySymbols(t);i<o.length;i++)e.indexOf(o[i])<0&&Object.prototype.propertyIsEnumerable.call(t,o[i])&&(n[o[i]]=t[o[i]])}return n}function u(t,e,n,o){return new(n||(n=Promise))((function(i,r){function a(t){try{c(o.next(t))}catch(t){r(t)}}function s(t){try{c(o.throw(t))}catch(t){r(t)}}function c(t){var e;t.done?i(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(a,s)}c((o=o.apply(t,e||[])).next())}))}function l(t,e){var n,o,i,r,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return r={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(r[Symbol.iterator]=function(){return this}),r;function s(r){return function(s){return function(r){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,o&&(i=2&r[0]?o.return:r[0]?o.throw||((i=o.return)&&i.call(o),0):o.next)&&!(i=i.call(o,r[1])).done)return i;switch(o=0,i&&(r=[2&r[0],i.value]),r[0]){case 0:case 1:i=r;break;case 4:return a.label++,{value:r[1],done:!1};case 5:a.label++,o=r[1],r=[0];continue;case 7:r=a.ops.pop(),a.trys.pop();continue;default:if(!(i=a.trys,(i=i.length>0&&i[i.length-1])||6!==r[0]&&2!==r[0])){a=0;continue}if(3===r[0]&&(!i||r[1]>i[0]&&r[1]<i[3])){a.label=r[1];break}if(6===r[0]&&a.label<i[1]){a.label=i[1],i=r;break}if(i&&a.label<i[2]){a.label=i[2],a.ops.push(r);break}i[2]&&a.ops.pop(),a.trys.pop();continue}r=e.call(t,a)}catch(t){r=[6,t],o=0}finally{n=i=0}if(5&r[0])throw r[1];return{value:r[0]?r[1]:void 0,done:!0}}([r,s])}}}function d(t){const e=new Uint8Array(t);let n="";for(const t of e)n+=String.fromCharCode(t);return btoa(n).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function h(t){const e=t.replace(/-/g,"+").replace(/_/g,"/"),n=(4-e.length%4)%4,o=e.padEnd(e.length+n,"="),i=atob(o),r=new ArrayBuffer(i.length),a=new Uint8Array(r);for(let t=0;t<i.length;t++)a[t]=i.charCodeAt(t);return r}function p(){return void 0!==window?.PublicKeyCredential&&"function"==typeof window.PublicKeyCredential}function f(t){const{id:e}=t;return{...t,id:h(e),transports:t.transports}}function m(t){return"localhost"===t||/^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(t)}t.AuthsignalWindowMessage=void 0,(t.AuthsignalWindowMessage||(t.AuthsignalWindowMessage={})).AUTHSIGNAL_CLOSE_POPUP="AUTHSIGNAL_CLOSE_POPUP";class w extends Error{constructor({message:t,code:e,cause:n,name:o}){super(t,{cause:n}),this.name=o??n.name,this.code=e}}const y=new class{createNewAbortSignal(){if(this.controller){const t=new Error("Cancelling existing WebAuthn API call for new one");t.name="AbortError",this.controller.abort(t)}const t=new AbortController;return this.controller=t,t.signal}cancelCeremony(){if(this.controller){const t=new Error("Manually cancelling existing WebAuthn API call");t.name="AbortError",this.controller.abort(t),this.controller=void 0}}},g=["cross-platform","platform"];function b(t){if(t&&!(g.indexOf(t)<0))return t}async function v(t){if(!p())throw new Error("WebAuthn is not supported in this browser");var e;const n={publicKey:{...t,challenge:h(t.challenge),user:{...t.user,id:(e=t.user.id,(new TextEncoder).encode(e))},excludeCredentials:t.excludeCredentials?.map(f)}};let o;n.signal=y.createNewAbortSignal();try{o=await navigator.credentials.create(n)}catch(t){throw function({error:t,options:e}){const{publicKey:n}=e;if(!n)throw Error("options was missing required publicKey property");if("AbortError"===t.name){if(e.signal instanceof AbortSignal)return new w({message:"Registration ceremony was sent an abort signal",code:"ERROR_CEREMONY_ABORTED",cause:t})}else if("ConstraintError"===t.name){if(!0===n.authenticatorSelection?.requireResidentKey)return new w({message:"Discoverable credentials were required but no available authenticator supported it",code:"ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT",cause:t});if("required"===n.authenticatorSelection?.userVerification)return new w({message:"User verification was required but no available authenticator supported it",code:"ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT",cause:t})}else{if("InvalidStateError"===t.name)return new w({message:"The authenticator was previously registered",code:"ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED",cause:t});if("NotAllowedError"===t.name)return new w({message:t.message,code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:t});if("NotSupportedError"===t.name)return 0===n.pubKeyCredParams.filter((t=>"public-key"===t.type)).length?new w({message:'No entry in pubKeyCredParams was of type "public-key"',code:"ERROR_MALFORMED_PUBKEYCREDPARAMS",cause:t}):new w({message:"No available authenticator supported any of the specified pubKeyCredParams algorithms",code:"ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG",cause:t});if("SecurityError"===t.name){const e=window.location.hostname;if(!m(e))return new w({message:`${window.location.hostname} is an invalid domain`,code:"ERROR_INVALID_DOMAIN",cause:t});if(n.rp.id!==e)return new w({message:`The RP ID "${n.rp.id}" is invalid for this domain`,code:"ERROR_INVALID_RP_ID",cause:t})}else if("TypeError"===t.name){if(n.user.id.byteLength<1||n.user.id.byteLength>64)return new w({message:"User ID was not between 1 and 64 characters",code:"ERROR_INVALID_USER_ID_LENGTH",cause:t})}else if("UnknownError"===t.name)return new w({message:"The authenticator was unable to process the specified options, or could not create a new credential",code:"ERROR_AUTHENTICATOR_GENERAL_ERROR",cause:t})}return t}({error:t,options:n})}if(!o)throw new Error("Registration was not completed");const{id:i,rawId:r,response:a,type:s}=o;let c,u,l,g;if("function"==typeof a.getTransports&&(c=a.getTransports()),"function"==typeof a.getPublicKeyAlgorithm)try{u=a.getPublicKeyAlgorithm()}catch(t){E("getPublicKeyAlgorithm()",t)}if("function"==typeof a.getPublicKey)try{const t=a.getPublicKey();null!==t&&(l=d(t))}catch(t){E("getPublicKey()",t)}if("function"==typeof a.getAuthenticatorData)try{g=d(a.getAuthenticatorData())}catch(t){E("getAuthenticatorData()",t)}return{id:i,rawId:d(r),response:{attestationObject:d(a.attestationObject),clientDataJSON:d(a.clientDataJSON),transports:c,publicKeyAlgorithm:u,publicKey:l,authenticatorData:g},type:s,clientExtensionResults:o.getClientExtensionResults(),authenticatorAttachment:b(o.authenticatorAttachment)}}function E(t,e){console.warn(`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${t}. You should report this error to them.\n`,e)}async function A(t,e=!1){if(!p())throw new Error("WebAuthn is not supported in this browser");let n;0!==t.allowCredentials?.length&&(n=t.allowCredentials?.map(f));const o={...t,challenge:h(t.challenge),allowCredentials:n},i={};if(e){if(!await function(){const t=window.PublicKeyCredential;return void 0===t.isConditionalMediationAvailable?new Promise((t=>t(!1))):t.isConditionalMediationAvailable()}())throw Error("Browser does not support WebAuthn autofill");if(document.querySelectorAll("input[autocomplete$='webauthn']").length<1)throw Error('No <input> with "webauthn" as the only or last value in its `autocomplete` attribute was detected');i.mediation="conditional",o.allowCredentials=[]}let r;i.publicKey=o,i.signal=y.createNewAbortSignal();try{r=await navigator.credentials.get(i)}catch(t){throw function({error:t,options:e}){const{publicKey:n}=e;if(!n)throw Error("options was missing required publicKey property");if("AbortError"===t.name){if(e.signal instanceof AbortSignal)return new w({message:"Authentication ceremony was sent an abort signal",code:"ERROR_CEREMONY_ABORTED",cause:t})}else{if("NotAllowedError"===t.name)return new w({message:t.message,code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:t});if("SecurityError"===t.name){const e=window.location.hostname;if(!m(e))return new w({message:`${window.location.hostname} is an invalid domain`,code:"ERROR_INVALID_DOMAIN",cause:t});if(n.rpId!==e)return new w({message:`The RP ID "${n.rpId}" is invalid for this domain`,code:"ERROR_INVALID_RP_ID",cause:t})}else if("UnknownError"===t.name)return new w({message:"The authenticator was unable to process the specified options, or could not create a new assertion signature",code:"ERROR_AUTHENTICATOR_GENERAL_ERROR",cause:t})}return t}({error:t,options:i})}if(!r)throw new Error("Authentication was not completed");const{id:a,rawId:s,response:c,type:u}=r;let l;var g;return c.userHandle&&(g=c.userHandle,l=new TextDecoder("utf-8").decode(g)),{id:a,rawId:d(s),response:{authenticatorData:d(c.authenticatorData),clientDataJSON:d(c.clientDataJSON),signature:d(c.signature),userHandle:l},type:u,clientExtensionResults:r.getClientExtensionResults(),authenticatorAttachment:b(r.authenticatorAttachment)}}var R=function(){function t(t){var e=t.baseUrl,n=t.tenantId;this.tenantId=n,this.baseUrl=e}return t.prototype.registrationOptions=function(t){var e=t.token,n=t.userName,o=t.authenticatorAttachment;return u(this,void 0,void 0,(function(){var t;return l(this,(function(i){switch(i.label){case 0:return t=Boolean(o)?{username:n,authenticatorAttachment:o}:{username:n},[4,fetch("".concat(this.baseUrl,"/client/user-authenticators/passkey/registration-options"),{method:"POST",headers:this.buildHeaders(e),body:JSON.stringify(t)})];case 1:return[2,i.sent().json()]}}))}))},t.prototype.authenticationOptions=function(t){var e=t.token;return u(this,void 0,void 0,(function(){return l(this,(function(t){switch(t.label){case 0:return[4,fetch("".concat(this.baseUrl,"/client/user-authenticators/passkey/authentication-options"),{method:"POST",headers:this.buildHeaders(e),body:JSON.stringify({})})];case 1:return[2,t.sent().json()]}}))}))},t.prototype.addAuthenticator=function(t){var e=t.token,n=c(t,["token"]);return u(this,void 0,void 0,(function(){return l(this,(function(t){switch(t.label){case 0:return[4,fetch("".concat(this.baseUrl,"/client/user-authenticators/passkey"),{method:"POST",headers:this.buildHeaders(e),body:JSON.stringify(n)})];case 1:return[2,t.sent().json()]}}))}))},t.prototype.verify=function(t){var e=t.token,n=c(t,["token"]);return u(this,void 0,void 0,(function(){return l(this,(function(t){switch(t.label){case 0:return[4,fetch("".concat(this.baseUrl,"/client/verify/passkey"),{method:"POST",headers:this.buildHeaders(e),body:JSON.stringify(n)})];case 1:return[2,t.sent().json()]}}))}))},t.prototype.getPasskeyAuthenticator=function(t){return u(this,void 0,void 0,(function(){var e;return l(this,(function(n){switch(n.label){case 0:return[4,fetch("".concat(this.baseUrl,"/client/user-authenticators/passkey?credentialId=").concat(t),{method:"GET",headers:this.buildHeaders()})];case 1:if(!(e=n.sent()).ok)throw new Error(e.statusText);return[2,e.json()]}}))}))},t.prototype.buildHeaders=function(t){return{"Content-Type":"application/json",Authorization:t?"Bearer ".concat(t):"Basic ".concat(window.btoa(encodeURIComponent(this.tenantId)))}},t}(),_=function(){function t(t){var e=t.baseUrl,n=t.tenantId;this.passkeyLocalStorageKey="as_passkey_credential_id",this.api=new R({baseUrl:e,tenantId:n})}return t.prototype.signUp=function(t){var e=t.userName,n=t.token,o=t.authenticatorAttachment,i=void 0===o?"platform":o;return u(this,void 0,void 0,(function(){var t,o,r;return l(this,(function(a){switch(a.label){case 0:return[4,this.api.registrationOptions({userName:e,token:n,authenticatorAttachment:i})];case 1:return[4,v((t=a.sent()).options)];case 2:return o=a.sent(),[4,this.api.addAuthenticator({challengeId:t.challengeId,registrationCredential:o,token:n})];case 3:return(null==(r=a.sent())?void 0:r.isVerified)&&this.storeCredentialAgainstDevice(o),[2,null==r?void 0:r.accessToken]}}))}))},t.prototype.signIn=function(t){return u(this,void 0,void 0,(function(){var e,n,o;return l(this,(function(i){switch(i.label){case 0:if((null==t?void 0:t.token)&&t.autofill)throw new Error("Autofill is not supported when providing a token");return[4,this.api.authenticationOptions({token:null==t?void 0:t.token})];case 1:return[4,A((e=i.sent()).options,null==t?void 0:t.autofill)];case 2:return n=i.sent(),[4,this.api.verify({challengeId:e.challengeId,authenticationCredential:n,token:null==t?void 0:t.token})];case 3:return(null==(o=i.sent())?void 0:o.isVerified)&&this.storeCredentialAgainstDevice(n),[2,null==o?void 0:o.accessToken]}}))}))},t.prototype.isAvailableOnDevice=function(){return u(this,void 0,void 0,(function(){var t;return l(this,(function(e){switch(e.label){case 0:if(!(t=localStorage.getItem(this.passkeyLocalStorageKey)))return[2,!1];e.label=1;case 1:return e.trys.push([1,3,,4]),[4,this.api.getPasskeyAuthenticator(t)];case 2:return e.sent(),[2,!0];case 3:return e.sent(),[2,!1];case 4:return[2]}}))}))},t.prototype.storeCredentialAgainstDevice=function(t){var e=t.id;"cross-platform"!==t.authenticatorAttachment&&localStorage.setItem(this.passkeyLocalStorageKey,e)},t}(),O=function(){function t(){this.windowRef=null}return t.prototype.show=function(t){var e=t.url,n=t.width,o=void 0===n?400:n,i=t.height,r=function(t){var e=t.url,n=t.width,o=t.height,i=t.win;if(!i.top)return null;var r=i.top.outerHeight/2+i.top.screenY-o/2,a=i.top.outerWidth/2+i.top.screenX-n/2;return window.open(e,"","toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=".concat(n,", height=").concat(o,", top=").concat(r,", left=").concat(a))}({url:e,width:o,height:void 0===i?500:i,win:window});if(!r)throw new Error("Window is not initialized");return this.windowRef=r,r},t.prototype.close=function(){if(!this.windowRef)throw new Error("Window is not initialized");this.windowRef.close()},t}();const I=":not([inert]):not([inert] *)",C=':not([tabindex^="-"])',S=":not(:disabled)";var k=[`a[href]${I}${C}`,`area[href]${I}${C}`,`input:not([type="hidden"]):not([type="radio"])${I}${C}${S}`,`input[type="radio"]${I}${C}${S}`,`select${I}${C}${S}`,`textarea${I}${C}${S}`,`button${I}${C}${S}`,`details${I} > summary:first-of-type${C}`,`iframe${I}${C}`,`audio[controls]${I}${C}`,`video[controls]${I}${C}`,`[contenteditable]${I}${C}`,`[tabindex]${I}${C}`];function $(t){(t.querySelector("[autofocus]")||t).focus()}function P(t,e){if(e&&N(t))return t;if(!((n=t).shadowRoot&&"-1"===n.getAttribute("tabindex")||n.matches(":disabled,[hidden],[inert]")))if(t.shadowRoot){let n=T(t.shadowRoot,e);for(;n;){const t=P(n,e);if(t)return t;n=U(n,e)}}else if("slot"===t.localName){const n=t.assignedElements({flatten:!0});e||n.reverse();for(const t of n){const n=P(t,e);if(n)return n}}else{let n=T(t,e);for(;n;){const t=P(n,e);if(t)return t;n=U(n,e)}}var n;return!e&&N(t)?t:null}function T(t,e){return e?t.firstElementChild:t.lastElementChild}function U(t,e){return e?t.nextElementSibling:t.previousElementSibling}const N=t=>!t.shadowRoot?.delegatesFocus&&(t.matches(k.join(","))&&!(t=>!(!t.matches("details:not([open]) *")||t.matches("details>summary:first-of-type"))||!(t.offsetWidth||t.offsetHeight||t.getClientRects().length))(t));function D(t=document){const e=t.activeElement;return e?e.shadowRoot?D(e.shadowRoot)||document.activeElement:e:null}function x(t,e){const[n,o]=function(t){const e=P(t,!0);return[e,e?P(t,!1)||e:null]}(t);if(!n)return e.preventDefault();const i=D();e.shiftKey&&i===n?(o.focus(),e.preventDefault()):e.shiftKey||i!==o||(n.focus(),e.preventDefault())}class L{$el;id;previouslyFocused;shown;constructor(t){this.$el=t,this.id=this.$el.getAttribute("data-a11y-dialog")||this.$el.id,this.previouslyFocused=null,this.shown=!1,this.maintainFocus=this.maintainFocus.bind(this),this.bindKeypress=this.bindKeypress.bind(this),this.handleTriggerClicks=this.handleTriggerClicks.bind(this),this.show=this.show.bind(this),this.hide=this.hide.bind(this),this.$el.setAttribute("aria-hidden","true"),this.$el.setAttribute("aria-modal","true"),this.$el.setAttribute("tabindex","-1"),this.$el.hasAttribute("role")||this.$el.setAttribute("role","dialog"),document.addEventListener("click",this.handleTriggerClicks,!0)}destroy(){return this.hide(),document.removeEventListener("click",this.handleTriggerClicks,!0),this.$el.replaceWith(this.$el.cloneNode(!0)),this.fire("destroy"),this}show(t){return this.shown||(this.shown=!0,this.$el.removeAttribute("aria-hidden"),this.previouslyFocused=D(),"BODY"===this.previouslyFocused?.tagName&&t?.target&&(this.previouslyFocused=t.target),"focus"===t?.type?this.maintainFocus(t):$(this.$el),document.body.addEventListener("focus",this.maintainFocus,!0),this.$el.addEventListener("keydown",this.bindKeypress,!0),this.fire("show",t)),this}hide(t){return this.shown?(this.shown=!1,this.$el.setAttribute("aria-hidden","true"),this.previouslyFocused?.focus?.(),document.body.removeEventListener("focus",this.maintainFocus,!0),this.$el.removeEventListener("keydown",this.bindKeypress,!0),this.fire("hide",t),this):this}on(t,e,n){return this.$el.addEventListener(t,e,n),this}off(t,e,n){return this.$el.removeEventListener(t,e,n),this}fire(t,e){this.$el.dispatchEvent(new CustomEvent(t,{detail:e,cancelable:!0}))}handleTriggerClicks(t){const e=t.target;e.closest(`[data-a11y-dialog-show="${this.id}"]`)&&this.show(t),(e.closest(`[data-a11y-dialog-hide="${this.id}"]`)||e.closest("[data-a11y-dialog-hide]")&&e.closest('[aria-modal="true"]')===this.$el)&&this.hide(t)}bindKeypress(t){if(document.activeElement?.closest('[aria-modal="true"]')!==this.$el)return;let e=!1;try{e=!!this.$el.querySelector('[popover]:not([popover="manual"]):popover-open')}catch{}"Escape"!==t.key||"alertdialog"===this.$el.getAttribute("role")||e||(t.preventDefault(),this.hide(t)),"Tab"===t.key&&x(this.$el,t)}maintainFocus(t){t.target.closest('[aria-modal="true"], [data-a11y-dialog-ignore-focus-trap]')||$(this.$el)}}function K(){for(const t of document.querySelectorAll("[data-a11y-dialog]"))new L(t)}"undefined"!=typeof document&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",K):K());var H="__authsignal-popup-container",W="__authsignal-popup-content",M="__authsignal-popup-overlay",j="__authsignal-popup-style",q="__authsignal-popup-iframe",F="385px",G=function(){function t(t){var e=t.width,n=t.isClosable;if(this.popup=null,document.querySelector("#".concat(H)))throw new Error("Multiple instances of Authsignal popup is not supported.");this.create({width:e,isClosable:n})}return t.prototype.create=function(t){var e=this,n=t.width,o=void 0===n?F:n,i=t.isClosable,r=void 0===i||i,a=o;CSS.supports("width",o)||(console.warn("Invalid CSS value for `popupOptions.width`. Using default value instead."),a=F);var s=document.createElement("div");s.setAttribute("id",H),s.setAttribute("aria-hidden","true"),r||s.setAttribute("role","alertdialog");var c=document.createElement("div");c.setAttribute("id",M),r&&c.setAttribute("data-a11y-dialog-hide","true");var u=document.createElement("div");u.setAttribute("id",W),document.body.appendChild(s);var l=document.createElement("style");l.setAttribute("id",j),l.textContent="\n #".concat(H,",\n #").concat(M," {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n }\n\n #").concat(H," {\n z-index: 2147483647;\n display: flex;\n }\n\n #").concat(H,"[aria-hidden='true'] {\n display: none;\n }\n\n #").concat(M," {\n background-color: rgba(0, 0, 0, 0.18);\n }\n\n #").concat(W," {\n margin: auto;\n z-index: 2147483647;\n position: relative;\n background-color: transparent;\n border-radius: 8px;\n width: ").concat(a,";\n }\n\n #").concat(W," iframe {\n width: 1px;\n min-width: 100%;\n border-radius: inherit;\n max-height: 95vh;\n height: ").concat("384px",";\n }\n "),document.head.insertAdjacentElement("beforeend",l),s.appendChild(c),s.appendChild(u),this.popup=new L(s),this.popup.on("hide",(function(){e.destroy()}))},t.prototype.destroy=function(){var t=document.querySelector("#".concat(H)),e=document.querySelector("#".concat(j));t&&e&&(document.body.removeChild(t),document.head.removeChild(e)),window.removeEventListener("message",V)},t.prototype.show=function(t){var e,n=t.url;if(!this.popup)throw new Error("Popup is not initialized");var o=document.createElement("iframe");o.setAttribute("id",q),o.setAttribute("name","authsignal"),o.setAttribute("title","Authsignal multi-factor authentication"),o.setAttribute("src",n),o.setAttribute("frameborder","0"),o.setAttribute("allow","publickey-credentials-get *; publickey-credentials-create *; clipboard-write");var i=document.querySelector("#".concat(W));i&&i.appendChild(o),window.addEventListener("message",V),null===(e=this.popup)||void 0===e||e.show()},t.prototype.close=function(){if(!this.popup)throw new Error("Popup is not initialized");this.popup.hide()},t.prototype.on=function(t,e){if(!this.popup)throw new Error("Popup is not initialized");this.popup.on(t,e)},t}();function V(t){var e=document.querySelector("#".concat(q));e&&t.data.height&&(e.style.height=t.data.height+"px")}var z="4a08uqve",B=function(){function e(t){var e=t.cookieDomain,n=t.cookieName,o=void 0===n?"__as_aid":n,i=t.baseUrl,r=void 0===i?"https://api.authsignal.com/v1":i,c=t.tenantId;if(this.anonymousId="",this.profilingId="",this.cookieDomain="",this.anonymousIdCookieName="",this._token=void 0,this.cookieDomain=e||document.location.hostname.replace("www.",""),this.anonymousIdCookieName=o,!c)throw new Error("tenantId is required");this.passkey=new _({tenantId:c,baseUrl:r});var u,l=(u=this.anonymousIdCookieName)&&decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*"+encodeURIComponent(u).replace(/[\-\.\+\*]/g,"\\$&")+"\\s*\\=\\s*([^;]*).*$)|^.*$"),"$1"))||null;l?this.anonymousId=l:(this.anonymousId=a(),s({name:this.anonymousIdCookieName,value:this.anonymousId,expire:1/0,domain:this.cookieDomain,secure:"http:"!==document.location.protocol}))}return e.prototype.launch=function(t,e){switch(null==e?void 0:e.mode){case"window":return this.launchWithWindow(t,e);case"popup":return this.launchWithPopup(t,e);default:this.launchWithRedirect(t)}},e.prototype.initAdvancedProfiling=function(t){var e=a();this.profilingId=e,s({name:"__as_pid",value:e,expire:1/0,domain:this.cookieDomain,secure:"http:"!==document.location.protocol});var n=t?"".concat(t,"/fp/tags.js?org_id=").concat(z,"&session_id=").concat(e):"https://h.online-metrix.net/fp/tags.js?org_id=".concat(z,"&session_id=").concat(e),o=document.createElement("script");o.src=n,o.async=!1,o.id="as_adv_profile",document.head.appendChild(o);var i=document.createElement("noscript");i.setAttribute("id","as_adv_profile_pixel"),i.setAttribute("aria-hidden","true");var r=document.createElement("iframe"),c=t?"".concat(t,"/fp/tags?org_id=").concat(z,"&session_id=").concat(e):"https://h.online-metrix.net/fp/tags?org_id=".concat(z,"&session_id=").concat(e);r.setAttribute("id","as_adv_profile_pixel"),r.setAttribute("src",c),r.setAttribute("style","width: 100px; height: 100px; border: 0; position: absolute; top: -5000px;"),i&&(i.appendChild(r),document.body.prepend(i))},e.prototype.launchWithRedirect=function(t){window.location.href=t},e.prototype.launchWithPopup=function(e,n){var o=this,i=n.popupOptions,r=new G({width:null==i?void 0:i.width,isClosable:null==i?void 0:i.isClosable}),a="".concat(e,"&mode=popup");return r.show({url:a}),new Promise((function(e){r.on("hide",(function(){e({token:o._token})})),window.addEventListener("message",(function(e){var n=null;try{n=JSON.parse(e.data)}catch(t){}(null==n?void 0:n.event)===t.AuthsignalWindowMessage.AUTHSIGNAL_CLOSE_POPUP&&(o._token=n.token,r.close())}),!1)}))},e.prototype.launchWithWindow=function(e,n){var o=this,i=n.windowOptions,r=new O,a="".concat(e,"&mode=popup");return r.show({url:a,width:null==i?void 0:i.width,height:null==i?void 0:i.height}),new Promise((function(e){window.addEventListener("message",(function(n){var i=null;try{i=JSON.parse(n.data)}catch(t){}(null==i?void 0:i.event)===t.AuthsignalWindowMessage.AUTHSIGNAL_CLOSE_POPUP&&(o._token=i.token,r.close(),e({token:o._token}))}),!1)}))},e}();return t.Authsignal=B,Object.defineProperty(t,"__esModule",{value:!0}),t}({});
package/dist/passkey.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { PasskeyApiClient } from "./api";
2
+ import { AuthenticatorAttachment } from "@simplewebauthn/types";
2
3
  declare type PasskeyOptions = {
3
4
  baseUrl: string;
4
5
  tenantId: string;
@@ -6,11 +7,13 @@ declare type PasskeyOptions = {
6
7
  declare type SignUpParams = {
7
8
  userName?: string;
8
9
  token: string;
10
+ authenticatorAttachment?: AuthenticatorAttachment | null;
9
11
  };
10
12
  export declare class Passkey {
11
13
  api: PasskeyApiClient;
14
+ private passkeyLocalStorageKey;
12
15
  constructor({ baseUrl, tenantId }: PasskeyOptions);
13
- signUp({ userName, token }: SignUpParams): Promise<string | undefined>;
16
+ signUp({ userName, token, authenticatorAttachment }: SignUpParams): Promise<string | undefined>;
14
17
  signIn(): Promise<string | undefined>;
15
18
  signIn(params?: {
16
19
  token: string;
@@ -18,5 +21,7 @@ export declare class Passkey {
18
21
  signIn(params?: {
19
22
  autofill: boolean;
20
23
  }): Promise<string | undefined>;
24
+ isAvailableOnDevice(): Promise<boolean>;
25
+ private storeCredentialAgainstDevice;
21
26
  }
22
27
  export {};
package/dist/types.d.ts CHANGED
@@ -19,6 +19,10 @@ export declare type PopupLaunchOptions = BaseLaunchOptions & {
19
19
  * @deprecated The popup will automatically resize to fit the content.
20
20
  */
21
21
  height?: string;
22
+ /**
23
+ * Whether the popup is closable with the escape key and by clicking the backdrop.
24
+ */
25
+ isClosable?: boolean;
22
26
  };
23
27
  };
24
28
  export declare type WindowLaunchOptions = BaseLaunchOptions & {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authsignal/browser",
3
- "version": "0.3.7",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -30,7 +30,7 @@
30
30
  "@fingerprintjs/fingerprintjs": "^3.3.6",
31
31
  "@simplewebauthn/browser": "^9.0.1",
32
32
  "@simplewebauthn/types": "^9.0.1",
33
- "a11y-dialog": "^7.5.2",
33
+ "a11y-dialog": "8.0.4",
34
34
  "uuid": "^9.0.0"
35
35
  },
36
36
  "devDependencies": {