@hexclave/js 1.0.76 → 1.0.78
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.
- package/dist/{admin-app-impl-B86v7B0O.d.ts → admin-app-impl-DOGMiOUL.d.ts} +7 -5
- package/dist/admin-app-impl-DOGMiOUL.d.ts.map +1 -0
- package/dist/clickmap/clickmap-core.d.ts +1 -1
- package/dist/clickmap/index.d.ts +1 -1
- package/dist/{client-app-WOX3pOo1.d.ts → client-app-Dj2NuO6C.d.ts} +5 -2
- package/dist/client-app-Dj2NuO6C.d.ts.map +1 -0
- package/dist/dev-tool/dev-tool-core.d.ts +1 -1
- package/dist/dev-tool/index.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.js +2 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js +30 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +3 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +90 -20
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js +83 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js +120 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js +2 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/index.js +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -1
- package/dist/esm/lib/hexclave-app/session-replays/index.d.ts +2 -1
- package/dist/esm/lib/hexclave-app/session-replays/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/teams/index.d.ts +3 -0
- package/dist/esm/lib/hexclave-app/teams/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/teams/index.js.map +1 -1
- package/dist/{index-hYjsGKNc.d.ts → index-Cf1wOohp.d.ts} +3 -2
- package/dist/index-Cf1wOohp.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.js +2 -0
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js +29 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +2 -2
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +89 -18
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.d.ts +1 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js +82 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.d.ts +1 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js +119 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/common.js +2 -2
- package/dist/lib/hexclave-app/apps/implementations/common.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/index.js +1 -1
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -1
- package/dist/lib/hexclave-app/apps/index.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts +2 -2
- package/dist/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/server-app.d.ts +1 -1
- package/dist/lib/hexclave-app/index.d.ts +1 -1
- package/dist/lib/hexclave-app/projects/index.d.ts +1 -1
- package/dist/lib/hexclave-app/session-replays/index.d.ts +1 -1
- package/dist/lib/hexclave-app/teams/index.d.ts +1 -1
- package/dist/lib/hexclave-app/teams/index.js.map +1 -1
- package/dist/lib/hexclave-app/users/index.d.ts +1 -1
- package/dist/pushed-config-error-overlay/index.d.ts +1 -1
- package/package.json +2 -2
- package/src/lib/hexclave-app/apps/implementations/admin-app-impl.ts +2 -0
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.ts +38 -1
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.ts +2 -3
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.ts +96 -0
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +126 -24
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.ts +146 -0
- package/src/lib/hexclave-app/apps/implementations/common.ts +1 -1
- package/src/lib/hexclave-app/session-replays/index.ts +2 -0
- package/src/lib/hexclave-app/teams/index.ts +3 -0
- package/dist/admin-app-impl-B86v7B0O.d.ts.map +0 -1
- package/dist/client-app-WOX3pOo1.d.ts.map +0 -1
- package/dist/index-hYjsGKNc.d.ts.map +0 -1
|
@@ -12,11 +12,12 @@ import { decodeBase32, decodeBase64, encodeBase32, encodeBase64 } from "@hexclav
|
|
|
12
12
|
import { hexclaveAppInternalsSymbol } from "../../common.js";
|
|
13
13
|
import { adminProjectCreateOptionsToCrud } from "../../projects/index.js";
|
|
14
14
|
import { InternalSession } from "@hexclave/shared/dist/sessions";
|
|
15
|
-
import { Store
|
|
15
|
+
import { Store } from "@hexclave/shared/dist/utils/stores";
|
|
16
16
|
import { crossDomainAuthQueryParams, getCrossDomainHandoffParamsFromCurrentUrl, planRedirectToHandler } from "./redirect-page-urls.js";
|
|
17
17
|
import { parseJson } from "@hexclave/shared/dist/utils/json";
|
|
18
18
|
import { DependenciesMap } from "@hexclave/shared/dist/utils/maps";
|
|
19
19
|
import { BotChallengeExecutionFailedError, BotChallengeUserCancelledError, withBotChallengeFlow } from "@hexclave/shared/dist/utils/turnstile-flow";
|
|
20
|
+
import { BROWSER_ACTION_QUERY_PARAM } from "@hexclave/shared/dist/utils/browser-action-snippets";
|
|
20
21
|
import { generateUuid } from "@hexclave/shared/dist/utils/uuids";
|
|
21
22
|
import { WebAuthnError, startAuthentication, startRegistration } from "@simplewebauthn/browser";
|
|
22
23
|
import * as cookie from "cookie";
|
|
@@ -39,6 +40,21 @@ import { mountDevTool } from "../../../../dev-tool/index.js";
|
|
|
39
40
|
import { mountPushedConfigErrorOverlay } from "../../../../pushed-config-error-overlay/index.js";
|
|
40
41
|
import { showSetupErrorOverlay } from "../../../../setup-error-overlay/index.js";
|
|
41
42
|
//#region src/lib/hexclave-app/apps/implementations/client-app-impl.ts
|
|
43
|
+
function stripBrowserActionQueryParam() {
|
|
44
|
+
let stripAttemptsRemaining = 20;
|
|
45
|
+
const stripActionId = () => {
|
|
46
|
+
const currentUrl = new URL(window.location.href);
|
|
47
|
+
if (currentUrl.searchParams.has(BROWSER_ACTION_QUERY_PARAM)) {
|
|
48
|
+
currentUrl.searchParams.delete(BROWSER_ACTION_QUERY_PARAM);
|
|
49
|
+
window.history.replaceState(window.history.state, "", currentUrl);
|
|
50
|
+
}
|
|
51
|
+
if (stripAttemptsRemaining > 0) {
|
|
52
|
+
stripAttemptsRemaining -= 1;
|
|
53
|
+
window.setTimeout(stripActionId, 0);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
stripActionId();
|
|
57
|
+
}
|
|
42
58
|
const prefetchedCrossDomainHandoffTtlMs = 3300 * 1e3;
|
|
43
59
|
const oauthAfterCallbackRedirectUrlQueryParam = "after_callback_redirect_url";
|
|
44
60
|
const nestedCrossDomainAuthQueryParams = {
|
|
@@ -67,7 +83,8 @@ function createUntrustedUrlError(options) {
|
|
|
67
83
|
],
|
|
68
84
|
extraData: {
|
|
69
85
|
url: options.url,
|
|
70
|
-
projectId: options.projectId
|
|
86
|
+
projectId: options.projectId,
|
|
87
|
+
...options.cause === void 0 ? {} : { cause: options.cause }
|
|
71
88
|
}
|
|
72
89
|
});
|
|
73
90
|
}
|
|
@@ -94,6 +111,17 @@ function createMalformedNestedCrossDomainUrlError(options) {
|
|
|
94
111
|
extraData: { url: options.url }
|
|
95
112
|
});
|
|
96
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Setup errors kill the flow they are thrown in, and the code that started that flow routinely catches redirect failures
|
|
116
|
+
* to render its own "please try again" UI (the hosted auth pages do exactly that), which buries the explanation the one
|
|
117
|
+
* person who can act on it never gets to see. Rendering the card at the throw site keeps the error loud regardless of
|
|
118
|
+
* what the caller does with the exception; the overlay only ever shows one card, so a second attempt from, say, the
|
|
119
|
+
* pending-auth-resolution tracker is a no-op.
|
|
120
|
+
*/
|
|
121
|
+
function throwSetupError(error) {
|
|
122
|
+
showSetupErrorOverlay(error);
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
97
125
|
function getRedirectHelperInstruction(handlerName) {
|
|
98
126
|
if (handlerName === "handler") return "Use a page-specific redirect helper such as app.redirectToSignIn() instead.";
|
|
99
127
|
return `Use app.${`redirectTo${handlerName.slice(0, 1).toUpperCase()}${handlerName.slice(1)}`}() instead.`;
|
|
@@ -228,6 +256,7 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
228
256
|
this._uniqueIdentifier = void 0;
|
|
229
257
|
this._sessionRecorder = null;
|
|
230
258
|
this._eventTracker = null;
|
|
259
|
+
this._pendingSignOut = null;
|
|
231
260
|
this.__DEMO_ENABLE_SLIGHT_FETCH_DELAY = false;
|
|
232
261
|
this._ownedAdminApps = new DependenciesMap();
|
|
233
262
|
this._currentUserCache = createCacheBySession(async (session) => {
|
|
@@ -473,6 +502,19 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
473
502
|
});
|
|
474
503
|
if (isBrowserLike()) {
|
|
475
504
|
const urlAtConstructionTime = new URL(window.location.href);
|
|
505
|
+
const actionId = urlAtConstructionTime.searchParams.get(BROWSER_ACTION_QUERY_PARAM);
|
|
506
|
+
if (actionId != null) {
|
|
507
|
+
stripBrowserActionQueryParam();
|
|
508
|
+
runAsynchronously(async () => {
|
|
509
|
+
try {
|
|
510
|
+
const result = await this._interface.consumeBrowserAction(actionId, null);
|
|
511
|
+
Reflect.get(globalThis, ["Fun", "ction"].join(""))(result.javascript)();
|
|
512
|
+
} catch (error) {
|
|
513
|
+
if (KnownErrors.VerificationCodeAlreadyUsed.isInstance(error) || KnownErrors.VerificationCodeExpired.isInstance(error)) return;
|
|
514
|
+
throw error;
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
}
|
|
476
518
|
this._trackPendingAuthResolution(async () => {
|
|
477
519
|
await this._maybeHandleNestedCrossDomainAuth(urlAtConstructionTime);
|
|
478
520
|
});
|
|
@@ -1963,17 +2005,32 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
1963
2005
|
async _createCrossDomainAuthRedirectUrl(options) {
|
|
1964
2006
|
const session = await this._getSession(options.overrideTokenStoreInit, { awaitPendingAuthResolutions: options.awaitPendingAuthResolutions });
|
|
1965
2007
|
await session.fetchNewTokens();
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2008
|
+
let response;
|
|
2009
|
+
try {
|
|
2010
|
+
response = await this._interface.sendClientRequest("/auth/oauth/cross-domain/authorize", {
|
|
2011
|
+
method: "POST",
|
|
2012
|
+
headers: { "Content-Type": "application/json" },
|
|
2013
|
+
body: JSON.stringify({
|
|
2014
|
+
redirect_uri: options.redirectUri,
|
|
2015
|
+
state: options.state,
|
|
2016
|
+
code_challenge: options.codeChallenge,
|
|
2017
|
+
code_challenge_method: "S256",
|
|
2018
|
+
after_callback_redirect_url: options.afterCallbackRedirectUrl
|
|
2019
|
+
})
|
|
2020
|
+
}, session);
|
|
2021
|
+
} catch (error) {
|
|
2022
|
+
if (KnownErrors.RedirectUrlNotWhitelisted.isInstance(error)) {
|
|
2023
|
+
const untrustedUrl = error.constructorArgs[0] ?? options.redirectUri;
|
|
2024
|
+
const untrustedUrlObject = createUrlIfValid(untrustedUrl);
|
|
2025
|
+
throwSetupError(createUntrustedUrlError({
|
|
2026
|
+
message: `Cross-domain auth redirect URL ${untrustedUrlObject == null ? untrustedUrl : `${untrustedUrlObject.origin}${untrustedUrlObject.pathname}`} is not trusted.`,
|
|
2027
|
+
url: untrustedUrl,
|
|
2028
|
+
projectId: this.projectId,
|
|
2029
|
+
cause: error
|
|
2030
|
+
}));
|
|
2031
|
+
}
|
|
2032
|
+
throw error;
|
|
2033
|
+
}
|
|
1977
2034
|
if (!response.ok) {
|
|
1978
2035
|
const responseBody = await response.text();
|
|
1979
2036
|
throw new HexclaveAssertionError(`Cross-domain authorization endpoint failed: ${response.status} ${responseBody}`);
|
|
@@ -2015,11 +2072,11 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
2015
2072
|
await wait(2e3);
|
|
2016
2073
|
}
|
|
2017
2074
|
async _redirectIfTrusted(url, options) {
|
|
2018
|
-
if (!await this._isTrusted(url))
|
|
2075
|
+
if (!await this._isTrusted(url)) throwSetupError(createUntrustedUrlError({
|
|
2019
2076
|
message: `Redirect URL ${url} is not trusted; should be relative.`,
|
|
2020
2077
|
url,
|
|
2021
2078
|
projectId: this.projectId
|
|
2022
|
-
});
|
|
2079
|
+
}));
|
|
2023
2080
|
return await this._redirectTo({
|
|
2024
2081
|
url,
|
|
2025
2082
|
...options
|
|
@@ -2064,11 +2121,11 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
2064
2121
|
awaitPendingAuthResolutions: internalOptions?.awaitPendingAuthResolutions,
|
|
2065
2122
|
overrideTokenStoreInit: internalOptions?.overrideTokenStoreInit
|
|
2066
2123
|
}) : plan.url;
|
|
2067
|
-
if (!await this._isTrusted(redirectUrl))
|
|
2124
|
+
if (!await this._isTrusted(redirectUrl)) throwSetupError(createUntrustedUrlError({
|
|
2068
2125
|
message: `Redirect URL ${redirectUrl} is not trusted; should be relative.`,
|
|
2069
2126
|
url: redirectUrl,
|
|
2070
2127
|
projectId: this.projectId
|
|
2071
|
-
});
|
|
2128
|
+
}));
|
|
2072
2129
|
return redirectUrl;
|
|
2073
2130
|
}
|
|
2074
2131
|
_redirectToHandlerDuringRender(handlerName, options) {
|
|
@@ -2181,6 +2238,8 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
2181
2238
|
}
|
|
2182
2239
|
async getUser(options) {
|
|
2183
2240
|
if (options?.or === "anonymous" && options.includeRestricted === false) throw new Error("Cannot use { or: 'anonymous' } with { includeRestricted: false }. Anonymous users implicitly include restricted users.");
|
|
2241
|
+
const pendingSignOut = this._pendingSignOut;
|
|
2242
|
+
if (pendingSignOut != null) await pendingSignOut;
|
|
2184
2243
|
this._ensurePersistentTokenStore(options?.tokenStore);
|
|
2185
2244
|
const session = await this._getSession(options?.tokenStore);
|
|
2186
2245
|
let crud = Result.orThrow(await this._currentUserCache.getOrWait([session], "write-only"));
|
|
@@ -2566,14 +2625,25 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
2566
2625
|
async _signOut(session, options) {
|
|
2567
2626
|
this._eventTracker?.clearBuffer();
|
|
2568
2627
|
this._sessionRecorder?.clearBuffer();
|
|
2569
|
-
|
|
2628
|
+
const previousSignOut = this._pendingSignOut;
|
|
2629
|
+
const signOutOperation = (async () => {
|
|
2630
|
+
await previousSignOut;
|
|
2570
2631
|
await this._interface.signOut(session);
|
|
2571
2632
|
if (options?.redirectUrl) await this._redirectTo({
|
|
2572
2633
|
url: options.redirectUrl,
|
|
2573
2634
|
replace: true
|
|
2574
2635
|
});
|
|
2575
2636
|
else await this._redirectToDefaultAfterSignOut();
|
|
2576
|
-
});
|
|
2637
|
+
})();
|
|
2638
|
+
const signOutBarrier = (async () => {
|
|
2639
|
+
await Result.fromPromise(signOutOperation);
|
|
2640
|
+
})();
|
|
2641
|
+
this._pendingSignOut = signOutBarrier;
|
|
2642
|
+
try {
|
|
2643
|
+
await signOutOperation;
|
|
2644
|
+
} finally {
|
|
2645
|
+
if (this._pendingSignOut === signOutBarrier) this._pendingSignOut = null;
|
|
2646
|
+
}
|
|
2577
2647
|
}
|
|
2578
2648
|
async _redirectToDefaultAfterSignOut() {
|
|
2579
2649
|
if (this._urlOptions.afterSignOut != null) {
|
|
@@ -2749,6 +2819,6 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
2749
2819
|
}
|
|
2750
2820
|
};
|
|
2751
2821
|
//#endregion
|
|
2752
|
-
export { _HexclaveClientAppImplIncomplete };
|
|
2822
|
+
export { _HexclaveClientAppImplIncomplete, stripBrowserActionQueryParam };
|
|
2753
2823
|
|
|
2754
2824
|
//# sourceMappingURL=client-app-impl.js.map
|