@hexclave/next 1.0.125 → 1.0.127
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-Bxad6t8m.d.ts → admin-app-impl-CjCeXtJd.d.ts} +29 -13
- package/dist/admin-app-impl-CjCeXtJd.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-B0cTytS5.d.ts → client-app-jAjrgwSh.d.ts} +34 -6
- package/dist/client-app-jAjrgwSh.d.ts.map +1 -0
- package/dist/{common-B2gkWlXX.d.ts → common-BjAI7WW6.d.ts} +15 -3
- package/dist/common-BjAI7WW6.d.ts.map +1 -0
- package/dist/{common-BK_sydxF.d.ts → common-DWfeER0s.d.ts} +2 -2
- package/dist/{common-BK_sydxF.d.ts.map → common-DWfeER0s.d.ts.map} +1 -1
- package/dist/components/message-cards/predefined-message-card.js +4 -2
- package/dist/components/message-cards/predefined-message-card.js.map +1 -1
- package/dist/components/selected-team-switcher.d.ts +1 -1
- package/dist/components/team-icon.d.ts +1 -1
- package/dist/components/team-switcher.d.ts +1 -1
- package/dist/components/user-button.js +9 -9
- package/dist/components/user-button.js.map +1 -1
- package/dist/components/user-button.test.d.ts +1 -0
- package/dist/components/user-button.test.js +71 -0
- package/dist/components/user-button.test.js.map +1 -0
- package/dist/components-page/account-settings/payments/payments-page.d.ts +1 -1
- package/dist/components-page/account-settings/payments/payments-panel.d.ts +1 -1
- package/dist/components-page/account-settings/settings/sign-out-section.d.ts +1 -1
- package/dist/components-page/account-settings/settings/sign-out-section.d.ts.map +1 -1
- package/dist/components-page/account-settings/settings/sign-out-section.js +4 -1
- package/dist/components-page/account-settings/settings/sign-out-section.js.map +1 -1
- package/dist/components-page/account-settings/teams/leave-team-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-api-keys-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-display-name-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-member-invitation-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-member-list-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-page.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-profile-image-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-profile-user-section.d.ts +1 -1
- package/dist/components-page/hexclave-handler-client.d.ts +1 -1
- package/dist/components-page/hexclave-handler.d.ts +1 -1
- package/dist/components-page/onboarding.js +15 -13
- package/dist/components-page/onboarding.js.map +1 -1
- package/dist/components-page/onboarding.test.js +2 -1
- package/dist/components-page/onboarding.test.js.map +1 -1
- package/dist/dev-tool/dev-tool-core.d.ts +1 -1
- package/dist/dev-tool/index.d.ts +1 -1
- package/dist/esm/components/message-cards/predefined-message-card.js +4 -2
- package/dist/esm/components/message-cards/predefined-message-card.js.map +1 -1
- package/dist/esm/components/user-button.js +9 -9
- package/dist/esm/components/user-button.js.map +1 -1
- package/dist/esm/components/user-button.test.d.ts +1 -0
- package/dist/esm/components/user-button.test.js +70 -0
- package/dist/esm/components/user-button.test.js.map +1 -0
- package/dist/esm/components-page/account-settings/settings/sign-out-section.d.ts +1 -1
- package/dist/esm/components-page/account-settings/settings/sign-out-section.d.ts.map +1 -1
- package/dist/esm/components-page/account-settings/settings/sign-out-section.js +4 -1
- package/dist/esm/components-page/account-settings/settings/sign-out-section.js.map +1 -1
- package/dist/esm/components-page/onboarding.js +15 -13
- package/dist/esm/components-page/onboarding.js.map +1 -1
- package/dist/esm/components-page/onboarding.test.js +2 -1
- package/dist/esm/components-page/onboarding.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +4 -4
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +18 -2
- 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.external-token-store.test.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js +382 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.js +66 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.js.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +66 -14
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js +2 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.d.ts +31 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.d.ts.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.js +115 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.js.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts +16 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/esm/lib/hexclave-app/common.d.ts +14 -2
- package/dist/esm/lib/hexclave-app/common.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/common.js +17 -1
- package/dist/esm/lib/hexclave-app/common.js.map +1 -1
- package/dist/esm/lib/hexclave-app/index.d.ts +2 -2
- package/dist/esm/lib/hexclave-app/index.js +2 -2
- package/dist/esm/lib/hexclave-app/users/index.d.ts +14 -1
- package/dist/esm/lib/hexclave-app/users/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/users/index.js.map +1 -1
- package/dist/{hexclave-provider-w8nX8KDv.d.ts → hexclave-provider-Br8j6WHN.d.ts} +2 -2
- package/dist/{hexclave-provider-w8nX8KDv.d.ts.map → hexclave-provider-Br8j6WHN.d.ts.map} +1 -1
- package/dist/{hooks-nqzRtl9S.d.ts → hooks-DyC8t1Sh.d.ts} +3 -3
- package/dist/{hooks-nqzRtl9S.d.ts.map → hooks-DyC8t1Sh.d.ts.map} +1 -1
- package/dist/{index-DbiyXho6.d.ts → index-CQ4kC6Cu.d.ts} +2 -2
- package/dist/{index-DbiyXho6.d.ts.map → index-CQ4kC6Cu.d.ts.map} +1 -1
- package/dist/{index-BDzsJvkD.d.ts → index-CWaynElx.d.ts} +3 -3
- package/dist/{index-BDzsJvkD.d.ts.map → index-CWaynElx.d.ts.map} +1 -1
- package/dist/{index-BSr2pd22.d.ts → index-FsS-NA9c.d.ts} +2 -2
- package/dist/{index-BSr2pd22.d.ts.map → index-FsS-NA9c.d.ts.map} +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.d.ts +1 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js +381 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.d.ts +1 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.js +65 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.js.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +65 -13
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/common.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/common.js +2 -2
- package/dist/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.d.ts +31 -0
- package/dist/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.d.ts.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.js +117 -0
- package/dist/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.js.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/redirect-page-urls.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/index.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/server-app.d.ts +1 -1
- package/dist/lib/hexclave-app/common.d.ts +2 -2
- package/dist/lib/hexclave-app/common.js +19 -0
- package/dist/lib/hexclave-app/common.js.map +1 -1
- package/dist/lib/hexclave-app/customers/index.d.ts +1 -1
- package/dist/lib/hexclave-app/data-vault/index.d.ts +1 -1
- package/dist/lib/hexclave-app/index.d.ts +4 -4
- package/dist/lib/hexclave-app/index.js +18 -0
- package/dist/lib/hexclave-app/projects/index.d.ts +1 -1
- package/dist/lib/hexclave-app/teams/index.d.ts +1 -1
- package/dist/lib/hexclave-app/url-targets.d.ts +1 -1
- package/dist/lib/hexclave-app/users/index.d.ts +1 -1
- package/dist/lib/hexclave-app/users/index.js.map +1 -1
- package/dist/lib/hooks.d.ts +1 -1
- package/dist/providers/hexclave-context.d.ts +1 -1
- package/dist/providers/hexclave-provider-client.d.ts +1 -1
- package/dist/providers/hexclave-provider.d.ts +1 -1
- package/dist/pushed-config-error-overlay/index.d.ts +1 -1
- package/dist/{redirect-page-urls-ofcg3AYr.d.ts → redirect-page-urls-CnGvn0UJ.d.ts} +2 -2
- package/dist/{redirect-page-urls-ofcg3AYr.d.ts.map → redirect-page-urls-CnGvn0UJ.d.ts.map} +1 -1
- package/dist/tanstack-start-server-context.d.ts.map +1 -1
- package/dist/{url-targets-CWO0mjTr.d.ts → url-targets-DbhzuQtY.d.ts} +2 -2
- package/dist/{url-targets-CWO0mjTr.d.ts.map → url-targets-DbhzuQtY.d.ts.map} +1 -1
- package/package.json +4 -4
- package/src/components/message-cards/predefined-message-card.tsx +4 -2
- package/src/components/user-button.test.tsx +87 -0
- package/src/components/user-button.tsx +9 -9
- package/src/components-page/account-settings/settings/sign-out-section.tsx +9 -1
- package/src/components-page/onboarding.test.tsx +1 -0
- package/src/components-page/onboarding.tsx +16 -13
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.ts +425 -0
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.ts +79 -0
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +124 -13
- package/src/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.ts +190 -0
- package/src/lib/hexclave-app/apps/interfaces/client-app.ts +17 -0
- package/src/lib/hexclave-app/common.ts +33 -0
- package/src/lib/hexclave-app/index.ts +4 -0
- package/src/lib/hexclave-app/users/index.ts +13 -1
- package/dist/admin-app-impl-Bxad6t8m.d.ts.map +0 -1
- package/dist/client-app-B0cTytS5.d.ts.map +0 -1
- package/dist/common-B2gkWlXX.d.ts.map +0 -1
package/dist/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
let _hexclave_shared = require("@hexclave/shared");
|
|
2
|
+
let vitest = require("vitest");
|
|
3
|
+
let ______common_js = require("../../common.js");
|
|
4
|
+
let ___interfaces_client_app_js = require("../interfaces/client-app.js");
|
|
5
|
+
//#region src/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.ts
|
|
6
|
+
function createAccessTokenString(refreshTokenId, issuedAtOffsetSeconds = 0) {
|
|
7
|
+
const encode = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
|
|
8
|
+
const nowSeconds = Math.floor(Date.now() / 1e3) + issuedAtOffsetSeconds;
|
|
9
|
+
return [
|
|
10
|
+
encode({
|
|
11
|
+
alg: "none",
|
|
12
|
+
typ: "JWT"
|
|
13
|
+
}),
|
|
14
|
+
encode({
|
|
15
|
+
sub: "user-id",
|
|
16
|
+
exp: nowSeconds + 60,
|
|
17
|
+
iat: nowSeconds,
|
|
18
|
+
iss: "http://localhost:12345",
|
|
19
|
+
aud: "00000000-0000-4000-8000-000000000000",
|
|
20
|
+
project_id: "00000000-0000-4000-8000-000000000000",
|
|
21
|
+
branch_id: "main",
|
|
22
|
+
refresh_token_id: refreshTokenId,
|
|
23
|
+
role: "authenticated",
|
|
24
|
+
name: "Test User",
|
|
25
|
+
email: "test@example.com",
|
|
26
|
+
email_verified: true,
|
|
27
|
+
selected_team_id: null,
|
|
28
|
+
signed_up_at: nowSeconds,
|
|
29
|
+
is_anonymous: false,
|
|
30
|
+
is_restricted: false,
|
|
31
|
+
restricted_reason: null,
|
|
32
|
+
requires_totp_mfa: false
|
|
33
|
+
}),
|
|
34
|
+
""
|
|
35
|
+
].join(".");
|
|
36
|
+
}
|
|
37
|
+
(0, vitest.describe)("StackClientApp external token stores", () => {
|
|
38
|
+
(0, vitest.it)("does not reuse a cached token when the provider session has ended", async () => {
|
|
39
|
+
let providerSessionId = "clerk-session";
|
|
40
|
+
const clientApp = new ___interfaces_client_app_js.StackClientApp({
|
|
41
|
+
automaticSideEffects: false,
|
|
42
|
+
baseUrl: "http://localhost:12345",
|
|
43
|
+
projectId: "00000000-0000-4000-8000-000000000000",
|
|
44
|
+
publishableClientKey: "stack-pk-test",
|
|
45
|
+
tokenStore: (0, ______common_js.clerkTokenStore)({
|
|
46
|
+
getSessionId: () => providerSessionId,
|
|
47
|
+
getToken: async () => "stale-provider-token"
|
|
48
|
+
}),
|
|
49
|
+
redirectMethod: "none"
|
|
50
|
+
});
|
|
51
|
+
const getSession = Reflect.get(clientApp, "_getSession");
|
|
52
|
+
const authenticatedSession = await getSession.call(clientApp);
|
|
53
|
+
providerSessionId = null;
|
|
54
|
+
const signedOutSession = await getSession.call(clientApp);
|
|
55
|
+
(0, vitest.expect)(signedOutSession.sessionKey).toBe("not-logged-in");
|
|
56
|
+
(0, vitest.expect)(signedOutSession).not.toBe(authenticatedSession);
|
|
57
|
+
(0, vitest.expect)(signedOutSession.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();
|
|
58
|
+
});
|
|
59
|
+
(0, vitest.it)("exchanges provider tokens and preserves the session across token rotations", async () => {
|
|
60
|
+
let providerSessionId = "clerk-session";
|
|
61
|
+
let notifyProviderChange;
|
|
62
|
+
let exchangeCount = 0;
|
|
63
|
+
const clientApp = new ___interfaces_client_app_js.StackClientApp({
|
|
64
|
+
automaticSideEffects: false,
|
|
65
|
+
baseUrl: "http://localhost:12345",
|
|
66
|
+
projectId: "00000000-0000-4000-8000-000000000000",
|
|
67
|
+
publishableClientKey: "stack-pk-test",
|
|
68
|
+
tokenStore: (0, ______common_js.clerkTokenStore)({
|
|
69
|
+
getSessionId: () => providerSessionId,
|
|
70
|
+
getToken: async () => "clerk-token",
|
|
71
|
+
subscribe: (callback) => {
|
|
72
|
+
notifyProviderChange = callback;
|
|
73
|
+
return () => {};
|
|
74
|
+
}
|
|
75
|
+
}),
|
|
76
|
+
redirectMethod: "none"
|
|
77
|
+
});
|
|
78
|
+
const clientInterface = Reflect.get(clientApp, "_interface");
|
|
79
|
+
Reflect.set(clientInterface, "exchangeExternalAuthToken", async () => {
|
|
80
|
+
exchangeCount += 1;
|
|
81
|
+
return {
|
|
82
|
+
status: "ok",
|
|
83
|
+
data: {
|
|
84
|
+
accessToken: createAccessTokenString("hexclave-external-session", exchangeCount),
|
|
85
|
+
sessionId: "hexclave-external-session",
|
|
86
|
+
userId: "user-id"
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
const getSession = Reflect.get(clientApp, "_getSession");
|
|
91
|
+
const firstSession = await getSession.call(clientApp);
|
|
92
|
+
(0, vitest.expect)(firstSession.sessionKey).toBe("external-clerk-integration-clerk-session");
|
|
93
|
+
await firstSession.getOrFetchLikelyValidTokens(2e4, null);
|
|
94
|
+
(0, vitest.expect)(exchangeCount).toBe(1);
|
|
95
|
+
notifyProviderChange?.();
|
|
96
|
+
const refreshedSession = await getSession.call(clientApp);
|
|
97
|
+
(0, vitest.expect)(refreshedSession).toBe(firstSession);
|
|
98
|
+
await refreshedSession.getOrFetchLikelyValidTokens(2e4, null);
|
|
99
|
+
(0, vitest.expect)(exchangeCount).toBe(2);
|
|
100
|
+
providerSessionId = "another-clerk-session";
|
|
101
|
+
notifyProviderChange?.();
|
|
102
|
+
const switchedSession = await getSession.call(clientApp);
|
|
103
|
+
(0, vitest.expect)(switchedSession).not.toBe(firstSession);
|
|
104
|
+
(0, vitest.expect)(switchedSession.sessionKey).toBe("external-clerk-integration-another-clerk-session");
|
|
105
|
+
});
|
|
106
|
+
(0, vitest.it)("retries a rejected exchange once with a fresh provider token before giving up", async () => {
|
|
107
|
+
let tokenCounter = 0;
|
|
108
|
+
const exchangedTokens = [];
|
|
109
|
+
const clientApp = new ___interfaces_client_app_js.StackClientApp({
|
|
110
|
+
automaticSideEffects: false,
|
|
111
|
+
baseUrl: "http://localhost:12345",
|
|
112
|
+
projectId: "00000000-0000-4000-8000-000000000000",
|
|
113
|
+
publishableClientKey: "stack-pk-test",
|
|
114
|
+
tokenStore: (0, ______common_js.clerkTokenStore)({
|
|
115
|
+
getSessionId: () => "clerk-session",
|
|
116
|
+
getToken: async () => `clerk-token-${++tokenCounter}`
|
|
117
|
+
}),
|
|
118
|
+
redirectMethod: "none"
|
|
119
|
+
});
|
|
120
|
+
const clientInterface = Reflect.get(clientApp, "_interface");
|
|
121
|
+
Reflect.set(clientInterface, "exchangeExternalAuthToken", async (_providerId, token) => {
|
|
122
|
+
exchangedTokens.push(token);
|
|
123
|
+
if (exchangedTokens.length === 1) return {
|
|
124
|
+
status: "error",
|
|
125
|
+
error: new _hexclave_shared.KnownErrors.InvalidExternalAuthToken("malformed_token")
|
|
126
|
+
};
|
|
127
|
+
return {
|
|
128
|
+
status: "ok",
|
|
129
|
+
data: {
|
|
130
|
+
accessToken: createAccessTokenString("hexclave-external-session"),
|
|
131
|
+
sessionId: "hexclave-external-session",
|
|
132
|
+
userId: "user-id"
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
const session = await Reflect.get(clientApp, "_getSession").call(clientApp);
|
|
137
|
+
(0, vitest.expect)(await session.getOrFetchLikelyValidTokens(2e4, null)).not.toBeNull();
|
|
138
|
+
(0, vitest.expect)(exchangedTokens).toEqual(["clerk-token-1", "clerk-token-2"]);
|
|
139
|
+
(0, vitest.expect)(session.isKnownToBeInvalid()).toBe(false);
|
|
140
|
+
});
|
|
141
|
+
(0, vitest.it)("does not retry into a different provider session after an exchange rejection", async () => {
|
|
142
|
+
let providerSessionId = "clerk-session-a";
|
|
143
|
+
let exchangeCount = 0;
|
|
144
|
+
const clientApp = new ___interfaces_client_app_js.StackClientApp({
|
|
145
|
+
automaticSideEffects: false,
|
|
146
|
+
baseUrl: "http://localhost:12345",
|
|
147
|
+
projectId: "00000000-0000-4000-8000-000000000000",
|
|
148
|
+
publishableClientKey: "stack-pk-test",
|
|
149
|
+
tokenStore: (0, ______common_js.clerkTokenStore)({
|
|
150
|
+
getSessionId: () => providerSessionId,
|
|
151
|
+
getToken: async () => "clerk-token"
|
|
152
|
+
}),
|
|
153
|
+
redirectMethod: "none"
|
|
154
|
+
});
|
|
155
|
+
const clientInterface = Reflect.get(clientApp, "_interface");
|
|
156
|
+
Reflect.set(clientInterface, "exchangeExternalAuthToken", async () => {
|
|
157
|
+
exchangeCount += 1;
|
|
158
|
+
providerSessionId = "clerk-session-b";
|
|
159
|
+
return {
|
|
160
|
+
status: "error",
|
|
161
|
+
error: new _hexclave_shared.KnownErrors.InvalidExternalAuthToken("malformed_token")
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
const session = await Reflect.get(clientApp, "_getSession").call(clientApp);
|
|
165
|
+
await (0, vitest.expect)(session.getOrFetchLikelyValidTokens(2e4, null)).rejects.toThrow("provider session changed");
|
|
166
|
+
(0, vitest.expect)(exchangeCount).toBe(1);
|
|
167
|
+
(0, vitest.expect)(session.isKnownToBeInvalid()).toBe(false);
|
|
168
|
+
(0, vitest.expect)(session.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();
|
|
169
|
+
});
|
|
170
|
+
(0, vitest.it)("does not refresh a session after the provider switches before refresh starts", async () => {
|
|
171
|
+
let providerSessionId = "clerk-session-a";
|
|
172
|
+
let exchangeCount = 0;
|
|
173
|
+
const clientApp = new ___interfaces_client_app_js.StackClientApp({
|
|
174
|
+
automaticSideEffects: false,
|
|
175
|
+
baseUrl: "http://localhost:12345",
|
|
176
|
+
projectId: "00000000-0000-4000-8000-000000000000",
|
|
177
|
+
publishableClientKey: "stack-pk-test",
|
|
178
|
+
tokenStore: (0, ______common_js.clerkTokenStore)({
|
|
179
|
+
getSessionId: () => providerSessionId,
|
|
180
|
+
getToken: async () => "clerk-token"
|
|
181
|
+
}),
|
|
182
|
+
redirectMethod: "none"
|
|
183
|
+
});
|
|
184
|
+
const clientInterface = Reflect.get(clientApp, "_interface");
|
|
185
|
+
Reflect.set(clientInterface, "exchangeExternalAuthToken", async () => {
|
|
186
|
+
exchangeCount += 1;
|
|
187
|
+
return {
|
|
188
|
+
status: "ok",
|
|
189
|
+
data: {
|
|
190
|
+
accessToken: createAccessTokenString("hexclave-external-session"),
|
|
191
|
+
sessionId: "hexclave-external-session",
|
|
192
|
+
userId: "user-id"
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
const session = await Reflect.get(clientApp, "_getSession").call(clientApp);
|
|
197
|
+
providerSessionId = "clerk-session-b";
|
|
198
|
+
await (0, vitest.expect)(session.getOrFetchLikelyValidTokens(2e4, null)).rejects.toThrow("provider session changed");
|
|
199
|
+
(0, vitest.expect)(exchangeCount).toBe(0);
|
|
200
|
+
(0, vitest.expect)(session.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();
|
|
201
|
+
(0, vitest.expect)(session.isKnownToBeInvalid()).toBe(false);
|
|
202
|
+
});
|
|
203
|
+
(0, vitest.it)("does not install a token when the provider switches accounts mid-exchange", async () => {
|
|
204
|
+
let providerSessionId = "clerk-session-a";
|
|
205
|
+
let resolveExchange;
|
|
206
|
+
const clientApp = new ___interfaces_client_app_js.StackClientApp({
|
|
207
|
+
automaticSideEffects: false,
|
|
208
|
+
baseUrl: "http://localhost:12345",
|
|
209
|
+
projectId: "00000000-0000-4000-8000-000000000000",
|
|
210
|
+
publishableClientKey: "stack-pk-test",
|
|
211
|
+
tokenStore: (0, ______common_js.clerkTokenStore)({
|
|
212
|
+
getSessionId: () => providerSessionId,
|
|
213
|
+
getToken: async () => "clerk-token"
|
|
214
|
+
}),
|
|
215
|
+
redirectMethod: "none"
|
|
216
|
+
});
|
|
217
|
+
const clientInterface = Reflect.get(clientApp, "_interface");
|
|
218
|
+
Reflect.set(clientInterface, "exchangeExternalAuthToken", () => new Promise((resolve) => {
|
|
219
|
+
resolveExchange = (accessToken) => resolve({
|
|
220
|
+
status: "ok",
|
|
221
|
+
data: {
|
|
222
|
+
accessToken,
|
|
223
|
+
sessionId: "hexclave-external-session",
|
|
224
|
+
userId: "user-id"
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}));
|
|
228
|
+
const session = await Reflect.get(clientApp, "_getSession").call(clientApp);
|
|
229
|
+
const pending = session.getOrFetchLikelyValidTokens(2e4, null);
|
|
230
|
+
await vitest.vi.waitFor(() => (0, vitest.expect)(resolveExchange).toBeDefined());
|
|
231
|
+
providerSessionId = "clerk-session-b";
|
|
232
|
+
resolveExchange?.(createAccessTokenString("hexclave-external-session"));
|
|
233
|
+
await (0, vitest.expect)(pending).rejects.toThrow("provider session changed");
|
|
234
|
+
(0, vitest.expect)(session.isKnownToBeInvalid()).toBe(false);
|
|
235
|
+
(0, vitest.expect)(session.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();
|
|
236
|
+
});
|
|
237
|
+
(0, vitest.it)("does not install a token when the provider switches away and back during token retrieval", async () => {
|
|
238
|
+
let providerSessionId = "clerk-session-a";
|
|
239
|
+
let notifyProviderChange;
|
|
240
|
+
let resolveProviderToken;
|
|
241
|
+
const clientApp = new ___interfaces_client_app_js.StackClientApp({
|
|
242
|
+
automaticSideEffects: false,
|
|
243
|
+
baseUrl: "http://localhost:12345",
|
|
244
|
+
projectId: "00000000-0000-4000-8000-000000000000",
|
|
245
|
+
publishableClientKey: "stack-pk-test",
|
|
246
|
+
tokenStore: (0, ______common_js.clerkTokenStore)({
|
|
247
|
+
getSessionId: () => providerSessionId,
|
|
248
|
+
getToken: () => new Promise((resolve) => {
|
|
249
|
+
resolveProviderToken = resolve;
|
|
250
|
+
}),
|
|
251
|
+
subscribe: (callback) => {
|
|
252
|
+
notifyProviderChange = callback;
|
|
253
|
+
return () => {};
|
|
254
|
+
}
|
|
255
|
+
}),
|
|
256
|
+
redirectMethod: "none"
|
|
257
|
+
});
|
|
258
|
+
const clientInterface = Reflect.get(clientApp, "_interface");
|
|
259
|
+
Reflect.set(clientInterface, "exchangeExternalAuthToken", async () => ({
|
|
260
|
+
status: "ok",
|
|
261
|
+
data: {
|
|
262
|
+
accessToken: createAccessTokenString("hexclave-external-session"),
|
|
263
|
+
sessionId: "hexclave-external-session",
|
|
264
|
+
userId: "user-id"
|
|
265
|
+
}
|
|
266
|
+
}));
|
|
267
|
+
const session = await Reflect.get(clientApp, "_getSession").call(clientApp);
|
|
268
|
+
const pending = session.getOrFetchLikelyValidTokens(2e4, null);
|
|
269
|
+
await vitest.vi.waitFor(() => (0, vitest.expect)(resolveProviderToken).toBeDefined());
|
|
270
|
+
providerSessionId = "clerk-session-b";
|
|
271
|
+
notifyProviderChange?.();
|
|
272
|
+
providerSessionId = "clerk-session-a";
|
|
273
|
+
notifyProviderChange?.();
|
|
274
|
+
resolveProviderToken?.("clerk-token");
|
|
275
|
+
await (0, vitest.expect)(pending).rejects.toThrow("provider session changed");
|
|
276
|
+
(0, vitest.expect)(session.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();
|
|
277
|
+
(0, vitest.expect)(session.isKnownToBeInvalid()).toBe(false);
|
|
278
|
+
});
|
|
279
|
+
(0, vitest.it)("surfaces the rejection without invalidating the session when the retried exchange is rejected too", async () => {
|
|
280
|
+
let exchangeCount = 0;
|
|
281
|
+
const clientApp = new ___interfaces_client_app_js.StackClientApp({
|
|
282
|
+
automaticSideEffects: false,
|
|
283
|
+
baseUrl: "http://localhost:12345",
|
|
284
|
+
projectId: "00000000-0000-4000-8000-000000000000",
|
|
285
|
+
publishableClientKey: "stack-pk-test",
|
|
286
|
+
tokenStore: (0, ______common_js.clerkTokenStore)({
|
|
287
|
+
getSessionId: () => "clerk-session",
|
|
288
|
+
getToken: async () => "clerk-token"
|
|
289
|
+
}),
|
|
290
|
+
redirectMethod: "none"
|
|
291
|
+
});
|
|
292
|
+
const clientInterface = Reflect.get(clientApp, "_interface");
|
|
293
|
+
Reflect.set(clientInterface, "exchangeExternalAuthToken", async () => {
|
|
294
|
+
exchangeCount += 1;
|
|
295
|
+
return {
|
|
296
|
+
status: "error",
|
|
297
|
+
error: new _hexclave_shared.KnownErrors.InvalidExternalAuthToken("malformed_token")
|
|
298
|
+
};
|
|
299
|
+
});
|
|
300
|
+
const session = await Reflect.get(clientApp, "_getSession").call(clientApp);
|
|
301
|
+
await (0, vitest.expect)(session.getOrFetchLikelyValidTokens(2e4, null)).rejects.toBeInstanceOf(_hexclave_shared.KnownErrors.InvalidExternalAuthToken);
|
|
302
|
+
(0, vitest.expect)(exchangeCount).toBe(2);
|
|
303
|
+
(0, vitest.expect)(session.isKnownToBeInvalid()).toBe(false);
|
|
304
|
+
});
|
|
305
|
+
(0, vitest.it)("rejects first-party sign-in with Hexclave tokens for an app with an external token store", async () => {
|
|
306
|
+
const clientApp = new ___interfaces_client_app_js.StackClientApp({
|
|
307
|
+
automaticSideEffects: false,
|
|
308
|
+
baseUrl: "http://localhost:12345",
|
|
309
|
+
projectId: "00000000-0000-4000-8000-000000000000",
|
|
310
|
+
publishableClientKey: "stack-pk-test",
|
|
311
|
+
tokenStore: (0, ______common_js.clerkTokenStore)({
|
|
312
|
+
getSessionId: () => "clerk-session",
|
|
313
|
+
getToken: async () => "clerk-token"
|
|
314
|
+
}),
|
|
315
|
+
redirectMethod: "none"
|
|
316
|
+
});
|
|
317
|
+
await (0, vitest.expect)(Reflect.get(clientApp, "_signInToAccountWithTokens").call(clientApp, {
|
|
318
|
+
accessToken: createAccessTokenString("unrelated-session"),
|
|
319
|
+
refreshToken: "unrelated-refresh-token"
|
|
320
|
+
})).rejects.toThrow("cannot sign in with Hexclave tokens");
|
|
321
|
+
});
|
|
322
|
+
(0, vitest.it)("treats a missing provider token as transient while the provider still reports a session", async () => {
|
|
323
|
+
let providerToken = null;
|
|
324
|
+
let exchangeCount = 0;
|
|
325
|
+
const clientApp = new ___interfaces_client_app_js.StackClientApp({
|
|
326
|
+
automaticSideEffects: false,
|
|
327
|
+
baseUrl: "http://localhost:12345",
|
|
328
|
+
projectId: "00000000-0000-4000-8000-000000000000",
|
|
329
|
+
publishableClientKey: "stack-pk-test",
|
|
330
|
+
tokenStore: (0, ______common_js.clerkTokenStore)({
|
|
331
|
+
getSessionId: () => "clerk-session",
|
|
332
|
+
getToken: async () => providerToken
|
|
333
|
+
}),
|
|
334
|
+
redirectMethod: "none"
|
|
335
|
+
});
|
|
336
|
+
const clientInterface = Reflect.get(clientApp, "_interface");
|
|
337
|
+
Reflect.set(clientInterface, "exchangeExternalAuthToken", async () => {
|
|
338
|
+
exchangeCount += 1;
|
|
339
|
+
return {
|
|
340
|
+
status: "ok",
|
|
341
|
+
data: {
|
|
342
|
+
accessToken: createAccessTokenString("hexclave-external-session"),
|
|
343
|
+
sessionId: "hexclave-external-session",
|
|
344
|
+
userId: "user-id"
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
});
|
|
348
|
+
const session = await Reflect.get(clientApp, "_getSession").call(clientApp);
|
|
349
|
+
await (0, vitest.expect)(session.getOrFetchLikelyValidTokens(2e4, null)).rejects.toThrow("external token store returned no token");
|
|
350
|
+
(0, vitest.expect)(session.isKnownToBeInvalid()).toBe(false);
|
|
351
|
+
(0, vitest.expect)(exchangeCount).toBe(0);
|
|
352
|
+
providerToken = "clerk-token";
|
|
353
|
+
(0, vitest.expect)(await session.getOrFetchLikelyValidTokens(2e4, null)).not.toBeNull();
|
|
354
|
+
(0, vitest.expect)(exchangeCount).toBe(1);
|
|
355
|
+
});
|
|
356
|
+
(0, vitest.it)("rejects signing out an external session without revoking the Hexclave session", async () => {
|
|
357
|
+
const clientApp = new ___interfaces_client_app_js.StackClientApp({
|
|
358
|
+
automaticSideEffects: false,
|
|
359
|
+
baseUrl: "http://localhost:12345",
|
|
360
|
+
projectId: "00000000-0000-4000-8000-000000000000",
|
|
361
|
+
publishableClientKey: "stack-pk-test",
|
|
362
|
+
tokenStore: (0, ______common_js.clerkTokenStore)({
|
|
363
|
+
getSessionId: () => "clerk-session",
|
|
364
|
+
getToken: async () => "clerk-token"
|
|
365
|
+
}),
|
|
366
|
+
redirectMethod: "none"
|
|
367
|
+
});
|
|
368
|
+
const clientInterface = Reflect.get(clientApp, "_interface");
|
|
369
|
+
let hexclaveSignOutCount = 0;
|
|
370
|
+
Reflect.set(clientInterface, "signOut", async () => {
|
|
371
|
+
hexclaveSignOutCount += 1;
|
|
372
|
+
});
|
|
373
|
+
Reflect.set(clientApp, "_redirectToDefaultAfterSignOut", async () => {});
|
|
374
|
+
const session = await Reflect.get(clientApp, "_getSession").call(clientApp);
|
|
375
|
+
await (0, vitest.expect)(Reflect.get(clientApp, "_signOut").call(clientApp, session)).rejects.toThrow("Cannot sign out an externally authenticated session through Hexclave");
|
|
376
|
+
(0, vitest.expect)(hexclaveSignOutCount).toBe(0);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
//#endregion
|
|
380
|
+
|
|
381
|
+
//# sourceMappingURL=client-app-impl.external-token-store.test.js.map
|
package/dist/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-app-impl.external-token-store.test.js","names":["StackClientApp","KnownErrors","vi"],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { KnownErrors } from \"@hexclave/shared\";\nimport { describe, expect, it, vi } from \"vitest\";\nimport { clerkTokenStore } from \"../../common\";\nimport { StackClientApp } from \"../interfaces/client-app\";\n\nfunction createAccessTokenString(refreshTokenId: string, issuedAtOffsetSeconds = 0): string {\n const encode = (value: unknown) => Buffer.from(JSON.stringify(value)).toString(\"base64url\");\n const nowSeconds = Math.floor(Date.now() / 1000) + issuedAtOffsetSeconds;\n // The payload must satisfy accessTokenPayloadSchema, otherwise AccessToken.createIfValid rejects it.\n return [\n encode({ alg: \"none\", typ: \"JWT\" }),\n encode({\n sub: \"user-id\",\n exp: nowSeconds + 60,\n iat: nowSeconds,\n iss: \"http://localhost:12345\",\n aud: \"00000000-0000-4000-8000-000000000000\",\n project_id: \"00000000-0000-4000-8000-000000000000\",\n branch_id: \"main\",\n refresh_token_id: refreshTokenId,\n role: \"authenticated\",\n name: \"Test User\",\n email: \"test@example.com\",\n email_verified: true,\n selected_team_id: null,\n signed_up_at: nowSeconds,\n is_anonymous: false,\n is_restricted: false,\n restricted_reason: null,\n requires_totp_mfa: false,\n }),\n \"\",\n ].join(\".\");\n}\n\ndescribe(\"StackClientApp external token stores\", () => {\n it(\"does not reuse a cached token when the provider session has ended\", async () => {\n let providerSessionId: string | null = \"clerk-session\";\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => providerSessionId,\n getToken: async () => \"stale-provider-token\",\n }),\n redirectMethod: \"none\",\n });\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const authenticatedSession = await getSession.call(clientApp);\n providerSessionId = null;\n const signedOutSession = await getSession.call(clientApp);\n\n expect(signedOutSession.sessionKey).toBe(\"not-logged-in\");\n expect(signedOutSession).not.toBe(authenticatedSession);\n expect(signedOutSession.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();\n });\n\n it(\"exchanges provider tokens and preserves the session across token rotations\", async () => {\n let providerSessionId = \"clerk-session\";\n let notifyProviderChange: (() => void) | undefined;\n let exchangeCount = 0;\n const externalTokenStore = clerkTokenStore({\n getSessionId: () => providerSessionId,\n getToken: async () => \"clerk-token\",\n subscribe: (callback) => {\n notifyProviderChange = callback;\n return () => {};\n },\n });\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: externalTokenStore,\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", async () => {\n exchangeCount += 1;\n return {\n status: \"ok\",\n data: {\n accessToken: createAccessTokenString(\"hexclave-external-session\", exchangeCount),\n sessionId: \"hexclave-external-session\",\n userId: \"user-id\",\n },\n };\n });\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const firstSession = await getSession.call(clientApp);\n expect(firstSession.sessionKey).toBe(\"external-clerk-integration-clerk-session\");\n await firstSession.getOrFetchLikelyValidTokens(20_000, null);\n expect(exchangeCount).toBe(1);\n\n notifyProviderChange?.();\n const refreshedSession = await getSession.call(clientApp);\n expect(refreshedSession).toBe(firstSession);\n await refreshedSession.getOrFetchLikelyValidTokens(20_000, null);\n expect(exchangeCount).toBe(2);\n\n providerSessionId = \"another-clerk-session\";\n notifyProviderChange?.();\n const switchedSession = await getSession.call(clientApp);\n expect(switchedSession).not.toBe(firstSession);\n expect(switchedSession.sessionKey).toBe(\"external-clerk-integration-another-clerk-session\");\n });\n\n it(\"retries a rejected exchange once with a fresh provider token before giving up\", async () => {\n let tokenCounter = 0;\n const exchangedTokens: string[] = [];\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => \"clerk-session\",\n getToken: async () => `clerk-token-${++tokenCounter}`,\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", async (_providerId: string, token: string) => {\n exchangedTokens.push(token);\n // The first provider token is treated as expired-in-flight; the retried, freshly fetched one succeeds.\n if (exchangedTokens.length === 1) {\n return { status: \"error\", error: new KnownErrors.InvalidExternalAuthToken(\"malformed_token\") };\n }\n return {\n status: \"ok\",\n data: {\n accessToken: createAccessTokenString(\"hexclave-external-session\"),\n sessionId: \"hexclave-external-session\",\n userId: \"user-id\",\n },\n };\n });\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n const tokens = await session.getOrFetchLikelyValidTokens(20_000, null);\n expect(tokens).not.toBeNull();\n expect(exchangedTokens).toEqual([\"clerk-token-1\", \"clerk-token-2\"]);\n expect(session.isKnownToBeInvalid()).toBe(false);\n });\n\n it(\"does not retry into a different provider session after an exchange rejection\", async () => {\n let providerSessionId: string | null = \"clerk-session-a\";\n let exchangeCount = 0;\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => providerSessionId,\n getToken: async () => \"clerk-token\",\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", async () => {\n exchangeCount += 1;\n providerSessionId = \"clerk-session-b\";\n return { status: \"error\", error: new KnownErrors.InvalidExternalAuthToken(\"malformed_token\") };\n });\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n await expect(session.getOrFetchLikelyValidTokens(20_000, null)).rejects.toThrow(\"provider session changed\");\n expect(exchangeCount).toBe(1);\n expect(session.isKnownToBeInvalid()).toBe(false);\n expect(session.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();\n });\n\n it(\"does not refresh a session after the provider switches before refresh starts\", async () => {\n let providerSessionId: string | null = \"clerk-session-a\";\n let exchangeCount = 0;\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => providerSessionId,\n getToken: async () => \"clerk-token\",\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", async () => {\n exchangeCount += 1;\n return {\n status: \"ok\",\n data: {\n accessToken: createAccessTokenString(\"hexclave-external-session\"),\n sessionId: \"hexclave-external-session\",\n userId: \"user-id\",\n },\n };\n });\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n providerSessionId = \"clerk-session-b\";\n\n await expect(session.getOrFetchLikelyValidTokens(20_000, null)).rejects.toThrow(\"provider session changed\");\n expect(exchangeCount).toBe(0);\n expect(session.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();\n expect(session.isKnownToBeInvalid()).toBe(false);\n });\n\n it(\"does not install a token when the provider switches accounts mid-exchange\", async () => {\n let providerSessionId: string | null = \"clerk-session-a\";\n let resolveExchange: ((value: string) => void) | undefined;\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => providerSessionId,\n getToken: async () => \"clerk-token\",\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", () => new Promise((resolve) => {\n resolveExchange = (accessToken) => resolve({\n status: \"ok\",\n data: {\n accessToken,\n sessionId: \"hexclave-external-session\",\n userId: \"user-id\",\n },\n });\n }));\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n const pending = session.getOrFetchLikelyValidTokens(20_000, null);\n await vi.waitFor(() => expect(resolveExchange).toBeDefined());\n providerSessionId = \"clerk-session-b\";\n resolveExchange?.(createAccessTokenString(\"hexclave-external-session\"));\n\n await expect(pending).rejects.toThrow(\"provider session changed\");\n expect(session.isKnownToBeInvalid()).toBe(false);\n // The rejected exchange must not have left the other account's token behind on this session.\n expect(session.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();\n });\n\n it(\"does not install a token when the provider switches away and back during token retrieval\", async () => {\n let providerSessionId = \"clerk-session-a\";\n let notifyProviderChange: (() => void) | undefined;\n let resolveProviderToken: ((token: string) => void) | undefined;\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => providerSessionId,\n getToken: () => new Promise(resolve => {\n resolveProviderToken = resolve;\n }),\n subscribe: callback => {\n notifyProviderChange = callback;\n return () => {};\n },\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", async () => ({\n status: \"ok\",\n data: {\n accessToken: createAccessTokenString(\"hexclave-external-session\"),\n sessionId: \"hexclave-external-session\",\n userId: \"user-id\",\n },\n }));\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n const pending = session.getOrFetchLikelyValidTokens(20_000, null);\n await vi.waitFor(() => expect(resolveProviderToken).toBeDefined());\n\n providerSessionId = \"clerk-session-b\";\n notifyProviderChange?.();\n providerSessionId = \"clerk-session-a\";\n notifyProviderChange?.();\n resolveProviderToken?.(\"clerk-token\");\n\n await expect(pending).rejects.toThrow(\"provider session changed\");\n expect(session.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();\n expect(session.isKnownToBeInvalid()).toBe(false);\n });\n\n it(\"surfaces the rejection without invalidating the session when the retried exchange is rejected too\", async () => {\n let exchangeCount = 0;\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => \"clerk-session\",\n getToken: async () => \"clerk-token\",\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", async () => {\n exchangeCount += 1;\n return { status: \"error\", error: new KnownErrors.InvalidExternalAuthToken(\"malformed_token\") };\n });\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n await expect(session.getOrFetchLikelyValidTokens(20_000, null)).rejects.toBeInstanceOf(KnownErrors.InvalidExternalAuthToken);\n expect(exchangeCount).toBe(2);\n // A repeated rejection is a misconfiguration signal, not a sign-out: the session must stay usable\n // so that fixing the configuration recovers it.\n expect(session.isKnownToBeInvalid()).toBe(false);\n });\n\n it(\"rejects first-party sign-in with Hexclave tokens for an app with an external token store\", async () => {\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => \"clerk-session\",\n getToken: async () => \"clerk-token\",\n }),\n redirectMethod: \"none\",\n });\n\n const signInToAccountWithTokens = Reflect.get(clientApp, \"_signInToAccountWithTokens\");\n await expect(signInToAccountWithTokens.call(clientApp, {\n accessToken: createAccessTokenString(\"unrelated-session\"),\n refreshToken: \"unrelated-refresh-token\",\n })).rejects.toThrow(\"cannot sign in with Hexclave tokens\");\n });\n\n it(\"treats a missing provider token as transient while the provider still reports a session\", async () => {\n let providerToken: string | null = null;\n let exchangeCount = 0;\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => \"clerk-session\",\n getToken: async () => providerToken,\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", async () => {\n exchangeCount += 1;\n return {\n status: \"ok\",\n data: {\n accessToken: createAccessTokenString(\"hexclave-external-session\"),\n sessionId: \"hexclave-external-session\",\n userId: \"user-id\",\n },\n };\n });\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n // The provider SDK is still initializing (session reported, but no token yet): the fetch must\n // fail loudly instead of permanently invalidating the session.\n await expect(session.getOrFetchLikelyValidTokens(20_000, null)).rejects.toThrow(\"external token store returned no token\");\n expect(session.isKnownToBeInvalid()).toBe(false);\n expect(exchangeCount).toBe(0);\n\n providerToken = \"clerk-token\";\n const tokens = await session.getOrFetchLikelyValidTokens(20_000, null);\n expect(tokens).not.toBeNull();\n expect(exchangeCount).toBe(1);\n });\n\n it(\"rejects signing out an external session without revoking the Hexclave session\", async () => {\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => \"clerk-session\",\n getToken: async () => \"clerk-token\",\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n let hexclaveSignOutCount = 0;\n Reflect.set(clientInterface, \"signOut\", async () => {\n hexclaveSignOutCount += 1;\n });\n Reflect.set(clientApp, \"_redirectToDefaultAfterSignOut\", async () => {});\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n const signOut = Reflect.get(clientApp, \"_signOut\");\n await expect(signOut.call(clientApp, session)).rejects.toThrow(\n \"Cannot sign out an externally authenticated session through Hexclave\",\n );\n\n expect(hexclaveSignOutCount).toBe(0);\n });\n});\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
let _hexclave_shared = require("@hexclave/shared");
|
|
2
|
+
let vitest = require("vitest");
|
|
3
|
+
let _hexclave_shared_dist_utils_results = require("@hexclave/shared/dist/utils/results");
|
|
4
|
+
let ___interfaces_client_app_js = require("../interfaces/client-app.js");
|
|
5
|
+
//#region src/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.ts
|
|
6
|
+
const baseOptions = {
|
|
7
|
+
baseUrl: "http://localhost:12345",
|
|
8
|
+
projectId: "00000000-0000-4000-8000-000000000000",
|
|
9
|
+
publishableClientKey: "stack-pk-test",
|
|
10
|
+
tokenStore: "memory",
|
|
11
|
+
redirectMethod: "none",
|
|
12
|
+
noAutomaticPrefetch: true
|
|
13
|
+
};
|
|
14
|
+
const analyticsAppDisabledMessage = "initAnalytics() was called, but the Analytics app is not enabled on this Hexclave project";
|
|
15
|
+
function simulateAnalyticsUploadResult(app, result) {
|
|
16
|
+
const handler = Reflect.get(app, "_handleAnalyticsUploadResult");
|
|
17
|
+
if (typeof handler !== "function") throw new Error("Expected HexclaveClientApp to have a _handleAnalyticsUploadResult method");
|
|
18
|
+
handler.call(app, result);
|
|
19
|
+
}
|
|
20
|
+
(0, vitest.describe)("HexclaveClientApp.initAnalytics", () => {
|
|
21
|
+
(0, vitest.afterEach)(() => {
|
|
22
|
+
vitest.vi.restoreAllMocks();
|
|
23
|
+
});
|
|
24
|
+
(0, vitest.it)("does not throw when analytics are configured", () => {
|
|
25
|
+
const app = new ___interfaces_client_app_js.HexclaveClientApp(baseOptions);
|
|
26
|
+
(0, vitest.expect)(() => app.initAnalytics()).not.toThrow();
|
|
27
|
+
});
|
|
28
|
+
(0, vitest.it)("throws when analytics are disabled with the analytics option", () => {
|
|
29
|
+
const app = new ___interfaces_client_app_js.HexclaveClientApp({
|
|
30
|
+
...baseOptions,
|
|
31
|
+
analytics: { enabled: false }
|
|
32
|
+
});
|
|
33
|
+
(0, vitest.expect)(() => app.initAnalytics()).toThrow("`analytics.enabled` constructor option is set to false");
|
|
34
|
+
});
|
|
35
|
+
(0, vitest.it)("does not throw when only session replays are disabled", () => {
|
|
36
|
+
const app = new ___interfaces_client_app_js.HexclaveClientApp({
|
|
37
|
+
...baseOptions,
|
|
38
|
+
analytics: { replays: { enabled: false } }
|
|
39
|
+
});
|
|
40
|
+
(0, vitest.expect)(() => app.initAnalytics()).not.toThrow();
|
|
41
|
+
});
|
|
42
|
+
(0, vitest.it)("reports an error when the backend rejects uploads after initAnalytics() was called", () => {
|
|
43
|
+
const consoleError = vitest.vi.spyOn(console, "error").mockImplementation(() => {});
|
|
44
|
+
const app = new ___interfaces_client_app_js.HexclaveClientApp(baseOptions);
|
|
45
|
+
app.initAnalytics();
|
|
46
|
+
simulateAnalyticsUploadResult(app, _hexclave_shared_dist_utils_results.Result.error(new _hexclave_shared.KnownErrors.AnalyticsNotEnabled()));
|
|
47
|
+
simulateAnalyticsUploadResult(app, _hexclave_shared_dist_utils_results.Result.error(new _hexclave_shared.KnownErrors.AnalyticsNotEnabled()));
|
|
48
|
+
(0, vitest.expect)(consoleError.mock.calls.filter((args) => args.some((arg) => String(arg).includes(analyticsAppDisabledMessage)))).toHaveLength(1);
|
|
49
|
+
});
|
|
50
|
+
(0, vitest.it)("throws from initAnalytics() when the backend already rejected uploads before it was called", () => {
|
|
51
|
+
const consoleError = vitest.vi.spyOn(console, "error").mockImplementation(() => {});
|
|
52
|
+
const app = new ___interfaces_client_app_js.HexclaveClientApp(baseOptions);
|
|
53
|
+
simulateAnalyticsUploadResult(app, _hexclave_shared_dist_utils_results.Result.error(new _hexclave_shared.KnownErrors.AnalyticsNotEnabled()));
|
|
54
|
+
(0, vitest.expect)(consoleError.mock.calls.some((args) => args.some((arg) => String(arg).includes(analyticsAppDisabledMessage)))).toBe(false);
|
|
55
|
+
(0, vitest.expect)(() => app.initAnalytics()).toThrow(analyticsAppDisabledMessage);
|
|
56
|
+
});
|
|
57
|
+
(0, vitest.it)("ignores upload failures unrelated to the Analytics app being disabled", () => {
|
|
58
|
+
const app = new ___interfaces_client_app_js.HexclaveClientApp(baseOptions);
|
|
59
|
+
simulateAnalyticsUploadResult(app, _hexclave_shared_dist_utils_results.Result.error(/* @__PURE__ */ new Error("network error")));
|
|
60
|
+
(0, vitest.expect)(() => app.initAnalytics()).not.toThrow();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
//#endregion
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=client-app-impl.init-analytics.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-app-impl.init-analytics.test.js","names":["HexclaveClientApp","vi","Result","KnownErrors"],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { KnownErrors } from \"@hexclave/shared\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport { afterEach, describe, expect, it, vi } from \"vitest\";\nimport { HexclaveClientApp, HexclaveClientAppConstructorOptions } from \"../interfaces/client-app\";\n\nconst baseOptions = {\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: \"memory\",\n redirectMethod: \"none\",\n noAutomaticPrefetch: true,\n} satisfies HexclaveClientAppConstructorOptions<true, string>;\n\nconst analyticsAppDisabledMessage = \"initAnalytics() was called, but the Analytics app is not enabled on this Hexclave project\";\n\n// The backend's AnalyticsNotEnabled rejection only reaches the client through the event tracker's and session\n// recorder's upload callbacks, which only exist in a browser; this simulates such a rejection arriving.\nfunction simulateAnalyticsUploadResult(app: HexclaveClientApp, result: Result<Response, Error>) {\n const handler: unknown = Reflect.get(app, \"_handleAnalyticsUploadResult\");\n if (typeof handler !== \"function\") {\n throw new Error(\"Expected HexclaveClientApp to have a _handleAnalyticsUploadResult method\");\n }\n handler.call(app, result);\n}\n\ndescribe(\"HexclaveClientApp.initAnalytics\", () => {\n afterEach(() => {\n vi.restoreAllMocks();\n });\n\n it(\"does not throw when analytics are configured\", () => {\n const app = new HexclaveClientApp(baseOptions);\n expect(() => app.initAnalytics()).not.toThrow();\n });\n\n it(\"throws when analytics are disabled with the analytics option\", () => {\n const app = new HexclaveClientApp({ ...baseOptions, analytics: { enabled: false } });\n expect(() => app.initAnalytics()).toThrow(\"`analytics.enabled` constructor option is set to false\");\n });\n\n it(\"does not throw when only session replays are disabled\", () => {\n const app = new HexclaveClientApp({ ...baseOptions, analytics: { replays: { enabled: false } } });\n expect(() => app.initAnalytics()).not.toThrow();\n });\n\n it(\"reports an error when the backend rejects uploads after initAnalytics() was called\", () => {\n const consoleError = vi.spyOn(console, \"error\").mockImplementation(() => {});\n const app = new HexclaveClientApp(baseOptions);\n app.initAnalytics();\n\n simulateAnalyticsUploadResult(app, Result.error(new KnownErrors.AnalyticsNotEnabled()));\n // Repeated rejections must not report the error again.\n simulateAnalyticsUploadResult(app, Result.error(new KnownErrors.AnalyticsNotEnabled()));\n\n const reports = consoleError.mock.calls.filter((args) => args.some((arg) => String(arg).includes(analyticsAppDisabledMessage)));\n expect(reports).toHaveLength(1);\n });\n\n it(\"throws from initAnalytics() when the backend already rejected uploads before it was called\", () => {\n const consoleError = vi.spyOn(console, \"error\").mockImplementation(() => {});\n const app = new HexclaveClientApp(baseOptions);\n\n simulateAnalyticsUploadResult(app, Result.error(new KnownErrors.AnalyticsNotEnabled()));\n expect(consoleError.mock.calls.some((args) => args.some((arg) => String(arg).includes(analyticsAppDisabledMessage)))).toBe(false);\n\n expect(() => app.initAnalytics()).toThrow(analyticsAppDisabledMessage);\n });\n\n it(\"ignores upload failures unrelated to the Analytics app being disabled\", () => {\n const app = new HexclaveClientApp(baseOptions);\n simulateAnalyticsUploadResult(app, Result.error(new Error(\"network error\")));\n expect(() => app.initAnalytics()).not.toThrow();\n });\n});\n"],"mappings":"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"}
|