@hexclave/js 1.0.75 → 1.0.77

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.
Files changed (150) hide show
  1. package/dist/{admin-app-impl-BhrjJlxj.d.ts → admin-app-impl-CHQtsn4f.d.ts} +11 -9
  2. package/dist/admin-app-impl-CHQtsn4f.d.ts.map +1 -0
  3. package/dist/clickmap/clickmap-core.d.ts +1 -1
  4. package/dist/clickmap/index.d.ts +1 -1
  5. package/dist/{client-app-WOX3pOo1.d.ts → client-app-R5X8wyeV.d.ts} +4 -1
  6. package/dist/client-app-R5X8wyeV.d.ts.map +1 -0
  7. package/dist/dev-tool/dev-tool-core.d.ts +1 -1
  8. package/dist/dev-tool/dev-tool-core.d.ts.map +1 -1
  9. package/dist/dev-tool/dev-tool-core.js +2 -2
  10. package/dist/dev-tool/dev-tool-core.js.map +1 -1
  11. package/dist/dev-tool/index.d.ts +1 -1
  12. package/dist/dev-tool/index.d.ts.map +1 -1
  13. package/dist/dev-tool/index.js +2 -8
  14. package/dist/dev-tool/index.js.map +1 -1
  15. package/dist/esm/dev-tool/dev-tool-core.d.ts.map +1 -1
  16. package/dist/esm/dev-tool/dev-tool-core.js +1 -1
  17. package/dist/esm/dev-tool/dev-tool-core.js.map +1 -1
  18. package/dist/esm/dev-tool/index.d.ts.map +1 -1
  19. package/dist/esm/dev-tool/index.js +2 -8
  20. package/dist/esm/dev-tool/index.js.map +1 -1
  21. package/dist/esm/in-page-ui/dev-environment.d.ts +5 -0
  22. package/dist/esm/in-page-ui/dev-environment.d.ts.map +1 -0
  23. package/dist/esm/in-page-ui/dev-environment.js +18 -0
  24. package/dist/esm/in-page-ui/dev-environment.js.map +1 -0
  25. package/dist/esm/in-page-ui/issue-card.d.ts +52 -0
  26. package/dist/esm/in-page-ui/issue-card.d.ts.map +1 -0
  27. package/dist/esm/in-page-ui/issue-card.js +392 -0
  28. package/dist/esm/in-page-ui/issue-card.js.map +1 -0
  29. package/dist/esm/in-page-ui/issue-card.test.d.ts +1 -0
  30. package/dist/esm/in-page-ui/issue-card.test.js +60 -0
  31. package/dist/esm/in-page-ui/issue-card.test.js.map +1 -0
  32. package/dist/esm/in-page-ui/logo.d.ts +5 -0
  33. package/dist/esm/in-page-ui/logo.d.ts.map +1 -0
  34. package/dist/esm/in-page-ui/logo.js +6 -0
  35. package/dist/esm/in-page-ui/logo.js.map +1 -0
  36. package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +4 -4
  37. package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
  38. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js +30 -1
  39. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js.map +1 -1
  40. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +140 -15
  41. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
  42. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +5 -3
  43. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
  44. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +180 -27
  45. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
  46. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.d.ts +1 -0
  47. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js +83 -0
  48. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js.map +1 -0
  49. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.d.ts +1 -0
  50. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js +120 -0
  51. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js.map +1 -0
  52. package/dist/esm/lib/hexclave-app/apps/implementations/common.js +2 -2
  53. package/dist/esm/lib/hexclave-app/apps/implementations/common.js.map +1 -1
  54. package/dist/esm/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
  55. package/dist/esm/lib/hexclave-app/apps/implementations/index.js +1 -1
  56. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-page-urls.test.js +1 -0
  57. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-page-urls.test.js.map +1 -1
  58. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
  59. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -1
  60. package/dist/esm/lib/hexclave-app/teams/index.d.ts +3 -0
  61. package/dist/esm/lib/hexclave-app/teams/index.d.ts.map +1 -1
  62. package/dist/esm/lib/hexclave-app/teams/index.js.map +1 -1
  63. package/dist/esm/pushed-config-error-overlay/index.d.ts.map +1 -1
  64. package/dist/esm/pushed-config-error-overlay/index.js +42 -343
  65. package/dist/esm/pushed-config-error-overlay/index.js.map +1 -1
  66. package/dist/esm/setup-error-overlay/index.d.ts +16 -0
  67. package/dist/esm/setup-error-overlay/index.d.ts.map +1 -0
  68. package/dist/esm/setup-error-overlay/index.js +106 -0
  69. package/dist/esm/setup-error-overlay/index.js.map +1 -0
  70. package/dist/esm/setup-error-overlay/index.test.d.ts +1 -0
  71. package/dist/esm/setup-error-overlay/index.test.js +125 -0
  72. package/dist/esm/setup-error-overlay/index.test.js.map +1 -0
  73. package/dist/in-page-ui/dev-environment.d.ts +5 -0
  74. package/dist/in-page-ui/dev-environment.d.ts.map +1 -0
  75. package/dist/in-page-ui/dev-environment.js +19 -0
  76. package/dist/in-page-ui/dev-environment.js.map +1 -0
  77. package/dist/in-page-ui/issue-card.d.ts +52 -0
  78. package/dist/in-page-ui/issue-card.d.ts.map +1 -0
  79. package/dist/in-page-ui/issue-card.js +397 -0
  80. package/dist/in-page-ui/issue-card.js.map +1 -0
  81. package/dist/in-page-ui/issue-card.test.d.ts +1 -0
  82. package/dist/in-page-ui/issue-card.test.js +59 -0
  83. package/dist/in-page-ui/issue-card.test.js.map +1 -0
  84. package/dist/in-page-ui/logo.d.ts +5 -0
  85. package/dist/in-page-ui/logo.d.ts.map +1 -0
  86. package/dist/in-page-ui/logo.js +7 -0
  87. package/dist/in-page-ui/logo.js.map +1 -0
  88. package/dist/index.d.ts +1 -1
  89. package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -1
  90. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js +29 -0
  91. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js.map +1 -1
  92. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +140 -15
  93. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
  94. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +2 -2
  95. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +178 -24
  96. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
  97. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.d.ts +1 -0
  98. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js +82 -0
  99. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js.map +1 -0
  100. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.d.ts +1 -0
  101. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js +119 -0
  102. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js.map +1 -0
  103. package/dist/lib/hexclave-app/apps/implementations/common.js +2 -2
  104. package/dist/lib/hexclave-app/apps/implementations/common.js.map +1 -1
  105. package/dist/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
  106. package/dist/lib/hexclave-app/apps/implementations/index.js +1 -1
  107. package/dist/lib/hexclave-app/apps/implementations/redirect-page-urls.test.js +1 -0
  108. package/dist/lib/hexclave-app/apps/implementations/redirect-page-urls.test.js.map +1 -1
  109. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
  110. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -1
  111. package/dist/lib/hexclave-app/apps/index.d.ts +1 -1
  112. package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts +1 -1
  113. package/dist/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -1
  114. package/dist/lib/hexclave-app/apps/interfaces/server-app.d.ts +1 -1
  115. package/dist/lib/hexclave-app/index.d.ts +1 -1
  116. package/dist/lib/hexclave-app/projects/index.d.ts +1 -1
  117. package/dist/lib/hexclave-app/teams/index.d.ts +1 -1
  118. package/dist/lib/hexclave-app/teams/index.js.map +1 -1
  119. package/dist/lib/hexclave-app/users/index.d.ts +1 -1
  120. package/dist/pushed-config-error-overlay/index.d.ts +1 -1
  121. package/dist/pushed-config-error-overlay/index.d.ts.map +1 -1
  122. package/dist/pushed-config-error-overlay/index.js +41 -342
  123. package/dist/pushed-config-error-overlay/index.js.map +1 -1
  124. package/dist/setup-error-overlay/index.d.ts +16 -0
  125. package/dist/setup-error-overlay/index.d.ts.map +1 -0
  126. package/dist/setup-error-overlay/index.js +108 -0
  127. package/dist/setup-error-overlay/index.js.map +1 -0
  128. package/dist/setup-error-overlay/index.test.d.ts +1 -0
  129. package/dist/setup-error-overlay/index.test.js +124 -0
  130. package/dist/setup-error-overlay/index.test.js.map +1 -0
  131. package/package.json +2 -2
  132. package/src/dev-tool/dev-tool-core.ts +1 -4
  133. package/src/dev-tool/index.ts +4 -15
  134. package/src/in-page-ui/dev-environment.ts +33 -0
  135. package/src/in-page-ui/issue-card.test.ts +67 -0
  136. package/src/in-page-ui/issue-card.ts +500 -0
  137. package/src/in-page-ui/logo.ts +8 -0
  138. package/src/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.ts +38 -1
  139. package/src/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.ts +157 -0
  140. package/src/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.ts +96 -0
  141. package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +247 -37
  142. package/src/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.ts +146 -0
  143. package/src/lib/hexclave-app/apps/implementations/common.ts +1 -1
  144. package/src/lib/hexclave-app/apps/implementations/redirect-page-urls.test.ts +1 -0
  145. package/src/lib/hexclave-app/teams/index.ts +3 -0
  146. package/src/pushed-config-error-overlay/index.ts +45 -386
  147. package/src/setup-error-overlay/index.test.ts +133 -0
  148. package/src/setup-error-overlay/index.ts +150 -0
  149. package/dist/admin-app-impl-BhrjJlxj.d.ts.map +0 -1
  150. package/dist/client-app-WOX3pOo1.d.ts.map +0 -1
@@ -19,6 +19,7 @@ let __redirect_page_urls_js = require("./redirect-page-urls.js");
19
19
  let _hexclave_shared_dist_utils_json = require("@hexclave/shared/dist/utils/json");
20
20
  let _hexclave_shared_dist_utils_maps = require("@hexclave/shared/dist/utils/maps");
21
21
  let _hexclave_shared_dist_utils_turnstile_flow = require("@hexclave/shared/dist/utils/turnstile-flow");
22
+ let _hexclave_shared_dist_utils_browser_action_snippets = require("@hexclave/shared/dist/utils/browser-action-snippets");
22
23
  let _hexclave_shared_dist_utils_uuids = require("@hexclave/shared/dist/utils/uuids");
23
24
  let _simplewebauthn_browser = require("@simplewebauthn/browser");
24
25
  let cookie = require("cookie");
@@ -40,7 +41,23 @@ let __session_replay_js = require("./session-replay.js");
40
41
  let ____________clickmap_index_js = require("../../../../clickmap/index.js");
41
42
  let ____________dev_tool_index_js = require("../../../../dev-tool/index.js");
42
43
  let ____________pushed_config_error_overlay_index_js = require("../../../../pushed-config-error-overlay/index.js");
44
+ let ____________setup_error_overlay_index_js = require("../../../../setup-error-overlay/index.js");
43
45
  //#region src/lib/hexclave-app/apps/implementations/client-app-impl.ts
46
+ function stripBrowserActionQueryParam() {
47
+ let stripAttemptsRemaining = 20;
48
+ const stripActionId = () => {
49
+ const currentUrl = new URL(window.location.href);
50
+ if (currentUrl.searchParams.has(_hexclave_shared_dist_utils_browser_action_snippets.BROWSER_ACTION_QUERY_PARAM)) {
51
+ currentUrl.searchParams.delete(_hexclave_shared_dist_utils_browser_action_snippets.BROWSER_ACTION_QUERY_PARAM);
52
+ window.history.replaceState(window.history.state, "", currentUrl);
53
+ }
54
+ if (stripAttemptsRemaining > 0) {
55
+ stripAttemptsRemaining -= 1;
56
+ window.setTimeout(stripActionId, 0);
57
+ }
58
+ };
59
+ stripActionId();
60
+ }
44
61
  const prefetchedCrossDomainHandoffTtlMs = 3300 * 1e3;
45
62
  const oauthAfterCallbackRedirectUrlQueryParam = "after_callback_redirect_url";
46
63
  const nestedCrossDomainAuthQueryParams = {
@@ -52,6 +69,62 @@ const nestedCrossDomainAuthQueryParams = {
52
69
  codeChallengeMethod: "code_challenge_method",
53
70
  afterCallbackRedirectUrl: oauthAfterCallbackRedirectUrlQueryParam
54
71
  };
72
+ /**
73
+ * An auth flow can only ever send the user to a URL that the project trusts, so an untrusted one means the flow is dead
74
+ * until someone changes the project's configuration. Describe the problem in terms of that configuration rather than in
75
+ * terms of the flow's internals, which nobody outside this file knows about.
76
+ */
77
+ function createUntrustedUrlError(options) {
78
+ const origin = (0, _hexclave_shared_dist_utils_urls.createUrlIfValid)(options.url)?.origin ?? options.url;
79
+ return new _hexclave_shared_dist_utils_errors.HexclaveSetupError({
80
+ title: "A domain in your authentication flow is not one of your project's trusted domains",
81
+ message: options.message,
82
+ howToFix: [
83
+ `If ${origin} is yours, add it to your project's trusted domains in the Hexclave dashboard, under Domains.`,
84
+ "Protocol, domain and port must all match a trusted domain exactly; a wildcard like https://*.example.com matches only one subdomain level.",
85
+ `If ${origin} is not yours, do not add it \u2014 somebody may be trying to redirect your users' credentials to it.`
86
+ ],
87
+ extraData: {
88
+ url: options.url,
89
+ projectId: options.projectId,
90
+ ...options.cause === void 0 ? {} : { cause: options.cause }
91
+ }
92
+ });
93
+ }
94
+ function createUntrustedNestedCrossDomainUrlError(options) {
95
+ return createUntrustedUrlError({
96
+ message: `Nested cross-domain auth ${options.urlDescription} ${options.url} is not trusted.`,
97
+ url: options.url,
98
+ projectId: options.projectId
99
+ });
100
+ }
101
+ /**
102
+ * A URL that the auth flow cannot even parse is just as fatal as an untrusted one, and just as much a configuration
103
+ * problem: something in the developer's setup put a relative or malformed value where an absolute URL belongs.
104
+ */
105
+ function createMalformedNestedCrossDomainUrlError(options) {
106
+ return new _hexclave_shared_dist_utils_errors.HexclaveSetupError({
107
+ title: "A URL in your authentication flow is not a valid absolute URL",
108
+ message: `Nested cross-domain auth ${options.urlDescription} ${options.url} is not a valid absolute URL.`,
109
+ howToFix: [
110
+ "Handler URLs and after-auth redirect URLs must be absolute, including their protocol, like https://example.com/handler/oauth-callback.",
111
+ "Check the handler URLs configured for your project and the redirect URLs you pass to Hexclave's sign-in and sign-up methods.",
112
+ "If none of those look wrong, check whether a proxy or rewrite in front of your app is rewriting the authentication flow's query parameters."
113
+ ],
114
+ extraData: { url: options.url }
115
+ });
116
+ }
117
+ /**
118
+ * Setup errors kill the flow they are thrown in, and the code that started that flow routinely catches redirect failures
119
+ * to render its own "please try again" UI (the hosted auth pages do exactly that), which buries the explanation the one
120
+ * person who can act on it never gets to see. Rendering the card at the throw site keeps the error loud regardless of
121
+ * what the caller does with the exception; the overlay only ever shows one card, so a second attempt from, say, the
122
+ * pending-auth-resolution tracker is a no-op.
123
+ */
124
+ function throwSetupError(error) {
125
+ (0, ____________setup_error_overlay_index_js.showSetupErrorOverlay)(error);
126
+ throw error;
127
+ }
55
128
  function getRedirectHelperInstruction(handlerName) {
56
129
  if (handlerName === "handler") return "Use a page-specific redirect helper such as app.redirectToSignIn() instead.";
57
130
  return `Use app.${`redirectTo${handlerName.slice(0, 1).toUpperCase()}${handlerName.slice(1)}`}() instead.`;
@@ -186,6 +259,7 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
186
259
  this._uniqueIdentifier = void 0;
187
260
  this._sessionRecorder = null;
188
261
  this._eventTracker = null;
262
+ this._pendingSignOut = null;
189
263
  this.__DEMO_ENABLE_SLIGHT_FETCH_DELAY = false;
190
264
  this._ownedAdminApps = new _hexclave_shared_dist_utils_maps.DependenciesMap();
191
265
  this._currentUserCache = (0, __common_js.createCacheBySession)(async (session) => {
@@ -431,6 +505,19 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
431
505
  });
432
506
  if ((0, _hexclave_shared_dist_utils_env.isBrowserLike)()) {
433
507
  const urlAtConstructionTime = new URL(window.location.href);
508
+ const actionId = urlAtConstructionTime.searchParams.get(_hexclave_shared_dist_utils_browser_action_snippets.BROWSER_ACTION_QUERY_PARAM);
509
+ if (actionId != null) {
510
+ stripBrowserActionQueryParam();
511
+ (0, _hexclave_shared_dist_utils_promises.runAsynchronously)(async () => {
512
+ try {
513
+ const result = await this._interface.consumeBrowserAction(actionId, null);
514
+ Reflect.get(globalThis, ["Fun", "ction"].join(""))(result.javascript)();
515
+ } catch (error) {
516
+ if (_hexclave_shared.KnownErrors.VerificationCodeAlreadyUsed.isInstance(error) || _hexclave_shared.KnownErrors.VerificationCodeExpired.isInstance(error)) return;
517
+ throw error;
518
+ }
519
+ });
520
+ }
434
521
  this._trackPendingAuthResolution(async () => {
435
522
  await this._maybeHandleNestedCrossDomainAuth(urlAtConstructionTime);
436
523
  });
@@ -457,6 +544,7 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
457
544
  await callback();
458
545
  } catch (error) {
459
546
  (0, _hexclave_shared_dist_utils_errors.captureError)("pending-auth-resolution-failed", error);
547
+ (0, ____________setup_error_overlay_index_js.showSetupErrorOverlay)(error);
460
548
  }
461
549
  })();
462
550
  this._pendingAuthResolutionPromises.push(promise);
@@ -573,13 +661,35 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
573
661
  codeChallenge
574
662
  });
575
663
  if ((currentUrl.searchParams.get(nestedCrossDomainAuthQueryParams.codeChallengeMethod) ?? "S256") !== "S256") throw new _hexclave_shared_dist_utils_errors.HexclaveAssertionError("Nested cross-domain auth only supports S256 PKCE");
576
- if ((0, _hexclave_shared_dist_utils_urls.isRelative)(redirectUri)) throw new Error("Nested cross-domain auth redirect URI must be absolute.");
577
- const redirectUriUrl = new URL(redirectUri);
578
- if (!await this._isTrusted(redirectUriUrl.toString())) throw new Error(`Nested cross-domain auth redirect URI ${redirectUri} is not trusted.`);
664
+ const validateHandoffOwnership = async () => {
665
+ if (await this._fetchCurrentRefreshTokenIdIfSignedIn({ awaitPendingAuthResolutions: false }) !== refreshTokenId) throw new Error("Nested cross-domain auth source session does not match the requested refresh token ID.");
666
+ };
667
+ const setupErrorIfHandoffIsOwned = async (setupError) => {
668
+ await validateHandoffOwnership();
669
+ return setupError;
670
+ };
671
+ const redirectUriUrl = (0, _hexclave_shared_dist_utils_urls.isRelative)(redirectUri) ? null : (0, _hexclave_shared_dist_utils_urls.createUrlIfValid)(redirectUri);
672
+ if (redirectUriUrl == null) throw await setupErrorIfHandoffIsOwned(createMalformedNestedCrossDomainUrlError({
673
+ urlDescription: "redirect URI",
674
+ url: redirectUri
675
+ }));
676
+ if (!await this._isTrusted(redirectUriUrl.toString())) throw await setupErrorIfHandoffIsOwned(createUntrustedNestedCrossDomainUrlError({
677
+ urlDescription: "redirect URI",
678
+ url: redirectUri,
679
+ projectId: this.projectId
680
+ }));
579
681
  const afterCallbackRedirectUrlString = currentUrl.searchParams.get(nestedCrossDomainAuthQueryParams.afterCallbackRedirectUrl);
580
- const afterCallbackRedirectUrl = afterCallbackRedirectUrlString == null ? redirectUriUrl : new URL(afterCallbackRedirectUrlString, redirectUriUrl);
581
- if (!await this._isTrusted(afterCallbackRedirectUrl.toString())) throw new Error(`Nested cross-domain auth after-callback redirect URL ${afterCallbackRedirectUrlString} is not trusted.`);
582
- if (await this._fetchCurrentRefreshTokenIdIfSignedIn({ awaitPendingAuthResolutions: false }) !== refreshTokenId) throw new Error("Nested cross-domain auth source session does not match the requested refresh token ID.");
682
+ const afterCallbackRedirectUrl = afterCallbackRedirectUrlString == null ? redirectUriUrl : (0, _hexclave_shared_dist_utils_urls.createUrlIfValid)(afterCallbackRedirectUrlString, redirectUriUrl);
683
+ if (afterCallbackRedirectUrl == null) throw await setupErrorIfHandoffIsOwned(createMalformedNestedCrossDomainUrlError({
684
+ urlDescription: "after-callback redirect URL",
685
+ url: afterCallbackRedirectUrlString ?? ""
686
+ }));
687
+ if (!await this._isTrusted(afterCallbackRedirectUrl.toString())) throw await setupErrorIfHandoffIsOwned(createUntrustedNestedCrossDomainUrlError({
688
+ urlDescription: "after-callback redirect URL",
689
+ url: afterCallbackRedirectUrlString ?? afterCallbackRedirectUrl.toString(),
690
+ projectId: this.projectId
691
+ }));
692
+ await validateHandoffOwnership();
583
693
  await this._redirectTo({
584
694
  url: await this._createCrossDomainAuthRedirectUrl({
585
695
  redirectUri: redirectUriUrl.toString(),
@@ -597,9 +707,16 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
597
707
  if (currentRefreshTokenId != null) (await this._getSession(void 0, { awaitPendingAuthResolutions: false })).markInvalid();
598
708
  const callbackUrlString = currentUrl.searchParams.get(nestedCrossDomainAuthQueryParams.callbackUrl);
599
709
  if (callbackUrlString == null) throw new _hexclave_shared_dist_utils_errors.HexclaveAssertionError("Nested cross-domain auth URL is missing callback URL");
600
- if ((0, _hexclave_shared_dist_utils_urls.isRelative)(callbackUrlString)) throw new Error("Nested cross-domain auth callback URL must be absolute.");
601
- const callbackUrl = new URL(callbackUrlString);
602
- if (!await this._isTrusted(callbackUrl.toString())) throw new Error(`Nested cross-domain auth callback URL ${callbackUrlString} is not trusted.`);
710
+ const callbackUrl = (0, _hexclave_shared_dist_utils_urls.isRelative)(callbackUrlString) ? null : (0, _hexclave_shared_dist_utils_urls.createUrlIfValid)(callbackUrlString);
711
+ if (callbackUrl == null) throw createMalformedNestedCrossDomainUrlError({
712
+ urlDescription: "callback URL",
713
+ url: callbackUrlString
714
+ });
715
+ if (!await this._isTrusted(callbackUrl.toString())) throw createUntrustedNestedCrossDomainUrlError({
716
+ urlDescription: "callback URL",
717
+ url: callbackUrlString,
718
+ projectId: this.projectId
719
+ });
603
720
  const afterCallbackRedirectUrl = new URL(currentUrl);
604
721
  afterCallbackRedirectUrl.searchParams.delete(nestedCrossDomainAuthQueryParams.refreshTokenId);
605
722
  afterCallbackRedirectUrl.searchParams.delete(nestedCrossDomainAuthQueryParams.callbackUrl);
@@ -1891,17 +2008,32 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
1891
2008
  async _createCrossDomainAuthRedirectUrl(options) {
1892
2009
  const session = await this._getSession(options.overrideTokenStoreInit, { awaitPendingAuthResolutions: options.awaitPendingAuthResolutions });
1893
2010
  await session.fetchNewTokens();
1894
- const response = await this._interface.sendClientRequest("/auth/oauth/cross-domain/authorize", {
1895
- method: "POST",
1896
- headers: { "Content-Type": "application/json" },
1897
- body: JSON.stringify({
1898
- redirect_uri: options.redirectUri,
1899
- state: options.state,
1900
- code_challenge: options.codeChallenge,
1901
- code_challenge_method: "S256",
1902
- after_callback_redirect_url: options.afterCallbackRedirectUrl
1903
- })
1904
- }, session);
2011
+ let response;
2012
+ try {
2013
+ response = await this._interface.sendClientRequest("/auth/oauth/cross-domain/authorize", {
2014
+ method: "POST",
2015
+ headers: { "Content-Type": "application/json" },
2016
+ body: JSON.stringify({
2017
+ redirect_uri: options.redirectUri,
2018
+ state: options.state,
2019
+ code_challenge: options.codeChallenge,
2020
+ code_challenge_method: "S256",
2021
+ after_callback_redirect_url: options.afterCallbackRedirectUrl
2022
+ })
2023
+ }, session);
2024
+ } catch (error) {
2025
+ if (_hexclave_shared.KnownErrors.RedirectUrlNotWhitelisted.isInstance(error)) {
2026
+ const untrustedUrl = error.constructorArgs[0] ?? options.redirectUri;
2027
+ const untrustedUrlObject = (0, _hexclave_shared_dist_utils_urls.createUrlIfValid)(untrustedUrl);
2028
+ throwSetupError(createUntrustedUrlError({
2029
+ message: `Cross-domain auth redirect URL ${untrustedUrlObject == null ? untrustedUrl : `${untrustedUrlObject.origin}${untrustedUrlObject.pathname}`} is not trusted.`,
2030
+ url: untrustedUrl,
2031
+ projectId: this.projectId,
2032
+ cause: error
2033
+ }));
2034
+ }
2035
+ throw error;
2036
+ }
1905
2037
  if (!response.ok) {
1906
2038
  const responseBody = await response.text();
1907
2039
  throw new _hexclave_shared_dist_utils_errors.HexclaveAssertionError(`Cross-domain authorization endpoint failed: ${response.status} ${responseBody}`);
@@ -1943,7 +2075,11 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
1943
2075
  await (0, _hexclave_shared_dist_utils_promises.wait)(2e3);
1944
2076
  }
1945
2077
  async _redirectIfTrusted(url, options) {
1946
- if (!await this._isTrusted(url)) throw new Error(`Redirect URL ${url} is not trusted; should be relative.`);
2078
+ if (!await this._isTrusted(url)) throwSetupError(createUntrustedUrlError({
2079
+ message: `Redirect URL ${url} is not trusted; should be relative.`,
2080
+ url,
2081
+ projectId: this.projectId
2082
+ }));
1947
2083
  return await this._redirectTo({
1948
2084
  url,
1949
2085
  ...options
@@ -1988,7 +2124,11 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
1988
2124
  awaitPendingAuthResolutions: internalOptions?.awaitPendingAuthResolutions,
1989
2125
  overrideTokenStoreInit: internalOptions?.overrideTokenStoreInit
1990
2126
  }) : plan.url;
1991
- if (!await this._isTrusted(redirectUrl)) throw new Error(`Redirect URL ${redirectUrl} is not trusted; should be relative.`);
2127
+ if (!await this._isTrusted(redirectUrl)) throwSetupError(createUntrustedUrlError({
2128
+ message: `Redirect URL ${redirectUrl} is not trusted; should be relative.`,
2129
+ url: redirectUrl,
2130
+ projectId: this.projectId
2131
+ }));
1992
2132
  return redirectUrl;
1993
2133
  }
1994
2134
  _redirectToHandlerDuringRender(handlerName, options) {
@@ -2101,6 +2241,8 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
2101
2241
  }
2102
2242
  async getUser(options) {
2103
2243
  if (options?.or === "anonymous" && options.includeRestricted === false) throw new Error("Cannot use { or: 'anonymous' } with { includeRestricted: false }. Anonymous users implicitly include restricted users.");
2244
+ const pendingSignOut = this._pendingSignOut;
2245
+ if (pendingSignOut != null) await pendingSignOut;
2104
2246
  this._ensurePersistentTokenStore(options?.tokenStore);
2105
2247
  const session = await this._getSession(options?.tokenStore);
2106
2248
  let crud = _hexclave_shared_dist_utils_results.Result.orThrow(await this._currentUserCache.getOrWait([session], "write-only"));
@@ -2486,14 +2628,25 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
2486
2628
  async _signOut(session, options) {
2487
2629
  this._eventTracker?.clearBuffer();
2488
2630
  this._sessionRecorder?.clearBuffer();
2489
- await _hexclave_shared_dist_utils_stores.storeLock.withWriteLock(async () => {
2631
+ const previousSignOut = this._pendingSignOut;
2632
+ const signOutOperation = (async () => {
2633
+ await previousSignOut;
2490
2634
  await this._interface.signOut(session);
2491
2635
  if (options?.redirectUrl) await this._redirectTo({
2492
2636
  url: options.redirectUrl,
2493
2637
  replace: true
2494
2638
  });
2495
2639
  else await this._redirectToDefaultAfterSignOut();
2496
- });
2640
+ })();
2641
+ const signOutBarrier = (async () => {
2642
+ await _hexclave_shared_dist_utils_results.Result.fromPromise(signOutOperation);
2643
+ })();
2644
+ this._pendingSignOut = signOutBarrier;
2645
+ try {
2646
+ await signOutOperation;
2647
+ } finally {
2648
+ if (this._pendingSignOut === signOutBarrier) this._pendingSignOut = null;
2649
+ }
2497
2650
  }
2498
2651
  async _redirectToDefaultAfterSignOut() {
2499
2652
  if (this._urlOptions.afterSignOut != null) {
@@ -2670,5 +2823,6 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
2670
2823
  };
2671
2824
  //#endregion
2672
2825
  exports._HexclaveClientAppImplIncomplete = _HexclaveClientAppImplIncomplete;
2826
+ exports.stripBrowserActionQueryParam = stripBrowserActionQueryParam;
2673
2827
 
2674
2828
  //# sourceMappingURL=client-app-impl.js.map