@turnkey/react-wallet-kit 1.0.0-beta.5 → 1.0.0

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 (123) hide show
  1. package/README.MD +5 -2
  2. package/dist/_virtual/_rollupPluginBabelHelpers.mjs +49 -1
  3. package/dist/_virtual/_rollupPluginBabelHelpers.mjs.map +1 -1
  4. package/dist/components/auth/Email.d.ts.map +1 -1
  5. package/dist/components/auth/Email.js +3 -0
  6. package/dist/components/auth/Email.js.map +1 -1
  7. package/dist/components/auth/Email.mjs +3 -0
  8. package/dist/components/auth/Email.mjs.map +1 -1
  9. package/dist/components/auth/OAuth.d.ts.map +1 -1
  10. package/dist/components/auth/OAuth.js +1 -0
  11. package/dist/components/auth/OAuth.js.map +1 -1
  12. package/dist/components/auth/OAuth.mjs +1 -0
  13. package/dist/components/auth/OAuth.mjs.map +1 -1
  14. package/dist/components/auth/OTP.d.ts +1 -0
  15. package/dist/components/auth/OTP.d.ts.map +1 -1
  16. package/dist/components/auth/OTP.js +5 -1
  17. package/dist/components/auth/OTP.js.map +1 -1
  18. package/dist/components/auth/OTP.mjs +6 -3
  19. package/dist/components/auth/OTP.mjs.map +1 -1
  20. package/dist/components/auth/Passkey.d.ts.map +1 -1
  21. package/dist/components/auth/Passkey.js +2 -0
  22. package/dist/components/auth/Passkey.js.map +1 -1
  23. package/dist/components/auth/Passkey.mjs +2 -0
  24. package/dist/components/auth/Passkey.mjs.map +1 -1
  25. package/dist/components/auth/Wallet.d.ts +4 -4
  26. package/dist/components/auth/Wallet.d.ts.map +1 -1
  27. package/dist/components/auth/Wallet.js +72 -50
  28. package/dist/components/auth/Wallet.js.map +1 -1
  29. package/dist/components/auth/Wallet.mjs +78 -57
  30. package/dist/components/auth/Wallet.mjs.map +1 -1
  31. package/dist/components/auth/index.d.ts +5 -1
  32. package/dist/components/auth/index.d.ts.map +1 -1
  33. package/dist/components/auth/index.js +100 -14
  34. package/dist/components/auth/index.js.map +1 -1
  35. package/dist/components/auth/index.mjs +187 -84
  36. package/dist/components/auth/index.mjs.map +1 -1
  37. package/dist/components/design/Buttons.d.ts +2 -0
  38. package/dist/components/design/Buttons.d.ts.map +1 -1
  39. package/dist/components/design/Buttons.js +8 -2
  40. package/dist/components/design/Buttons.js.map +1 -1
  41. package/dist/components/design/Buttons.mjs +9 -3
  42. package/dist/components/design/Buttons.mjs.map +1 -1
  43. package/dist/components/export/Export.d.ts +2 -1
  44. package/dist/components/export/Export.d.ts.map +1 -1
  45. package/dist/components/export/Export.js +8 -6
  46. package/dist/components/export/Export.js.map +1 -1
  47. package/dist/components/export/Export.mjs +8 -6
  48. package/dist/components/export/Export.mjs.map +1 -1
  49. package/dist/components/export/ExportWarning.d.ts +2 -1
  50. package/dist/components/export/ExportWarning.d.ts.map +1 -1
  51. package/dist/components/export/ExportWarning.js +4 -2
  52. package/dist/components/export/ExportWarning.js.map +1 -1
  53. package/dist/components/export/ExportWarning.mjs +4 -2
  54. package/dist/components/export/ExportWarning.mjs.map +1 -1
  55. package/dist/components/import/Import.d.ts +7 -3
  56. package/dist/components/import/Import.d.ts.map +1 -1
  57. package/dist/components/import/Import.js +77 -37
  58. package/dist/components/import/Import.js.map +1 -1
  59. package/dist/components/import/Import.mjs +120 -55
  60. package/dist/components/import/Import.mjs.map +1 -1
  61. package/dist/components/sign/Message.d.ts.map +1 -1
  62. package/dist/components/sign/Message.js +1 -0
  63. package/dist/components/sign/Message.js.map +1 -1
  64. package/dist/components/sign/Message.mjs +1 -0
  65. package/dist/components/sign/Message.mjs.map +1 -1
  66. package/dist/components/user/ConnectWallet.d.ts +8 -0
  67. package/dist/components/user/ConnectWallet.d.ts.map +1 -0
  68. package/dist/components/user/{LinkWallet.js → ConnectWallet.js} +18 -18
  69. package/dist/components/user/ConnectWallet.js.map +1 -0
  70. package/dist/components/user/{LinkWallet.mjs → ConnectWallet.mjs} +25 -25
  71. package/dist/components/user/ConnectWallet.mjs.map +1 -0
  72. package/dist/components/user/RemovePasskey.d.ts.map +1 -1
  73. package/dist/components/user/RemovePasskey.js +1 -0
  74. package/dist/components/user/RemovePasskey.js.map +1 -1
  75. package/dist/components/user/RemovePasskey.mjs +1 -0
  76. package/dist/components/user/RemovePasskey.mjs.map +1 -1
  77. package/dist/index.js +13 -0
  78. package/dist/index.js.map +1 -1
  79. package/dist/index.mjs +2 -1
  80. package/dist/index.mjs.map +1 -1
  81. package/dist/providers/client/Provider.d.ts +3 -3
  82. package/dist/providers/client/Provider.d.ts.map +1 -1
  83. package/dist/providers/client/Provider.js +760 -149
  84. package/dist/providers/client/Provider.js.map +1 -1
  85. package/dist/providers/client/Provider.mjs +2474 -1624
  86. package/dist/providers/client/Provider.mjs.map +1 -1
  87. package/dist/providers/client/Types.d.ts +116 -11
  88. package/dist/providers/client/Types.d.ts.map +1 -1
  89. package/dist/providers/client/Types.js.map +1 -1
  90. package/dist/providers/client/Types.mjs.map +1 -1
  91. package/dist/providers/modal/Provider.js +1 -1
  92. package/dist/providers/modal/Provider.mjs +1 -1
  93. package/dist/styles.css +1 -1
  94. package/dist/tests/timers-test.d.ts +2 -0
  95. package/dist/tests/timers-test.d.ts.map +1 -0
  96. package/dist/tests/utils-test.d.ts +2 -0
  97. package/dist/tests/utils-test.d.ts.map +1 -0
  98. package/dist/types/base.d.ts +31 -3
  99. package/dist/types/base.d.ts.map +1 -1
  100. package/dist/types/base.js +23 -0
  101. package/dist/types/base.js.map +1 -1
  102. package/dist/types/base.mjs +19 -1
  103. package/dist/types/base.mjs.map +1 -1
  104. package/dist/utils/timers.d.ts +73 -0
  105. package/dist/utils/timers.d.ts.map +1 -0
  106. package/dist/utils/timers.js +133 -0
  107. package/dist/utils/timers.js.map +1 -0
  108. package/dist/utils/timers.mjs +142 -0
  109. package/dist/utils/timers.mjs.map +1 -0
  110. package/dist/{utils.d.ts → utils/utils.d.ts} +8 -3
  111. package/dist/utils/utils.d.ts.map +1 -0
  112. package/dist/{utils.js → utils/utils.js} +12 -3
  113. package/dist/utils/utils.js.map +1 -0
  114. package/dist/{utils.mjs → utils/utils.mjs} +12 -5
  115. package/dist/utils/utils.mjs.map +1 -0
  116. package/package.json +6 -5
  117. package/dist/components/user/LinkWallet.d.ts +0 -8
  118. package/dist/components/user/LinkWallet.d.ts.map +0 -1
  119. package/dist/components/user/LinkWallet.js.map +0 -1
  120. package/dist/components/user/LinkWallet.mjs.map +0 -1
  121. package/dist/utils.d.ts.map +0 -1
  122. package/dist/utils.js.map +0 -1
  123. package/dist/utils.mjs.map +0 -1
@@ -4,7 +4,8 @@
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var sha2 = require('@noble/hashes/sha2');
6
6
  var utils$1 = require('@noble/hashes/utils');
7
- var utils = require('../../utils.js');
7
+ var utils = require('../../utils/utils.js');
8
+ var timers = require('../../utils/timers.js');
8
9
  var core = require('@turnkey/core');
9
10
  var react = require('react');
10
11
  var sdkTypes = require('@turnkey/sdk-types');
@@ -23,7 +24,7 @@ var UpdatePhoneNumber = require('../../components/user/UpdatePhoneNumber.js');
23
24
  var UpdateUserName = require('../../components/user/UpdateUserName.js');
24
25
  var RemoveOAuthProvider = require('../../components/user/RemoveOAuthProvider.js');
25
26
  var RemovePasskey = require('../../components/user/RemovePasskey.js');
26
- var LinkWallet = require('../../components/user/LinkWallet.js');
27
+ var ConnectWallet = require('../../components/user/ConnectWallet.js');
27
28
  var Types = require('./Types.js');
28
29
  var OTP = require('../../components/auth/OTP.js');
29
30
  var RemoveEmail = require('../../components/user/RemoveEmail.js');
@@ -32,7 +33,7 @@ var RemovePhoneNumber = require('../../components/user/RemovePhoneNumber.js');
32
33
  /**
33
34
  * Provides Turnkey client authentication, session management, wallet operations, and user profile management
34
35
  * for the React Wallet Kit SDK. This context provider encapsulates all core authentication flows (Passkey, Wallet, OTP, OAuth),
35
- * session lifecycle (creation, expiration, refresh), wallet linking/import/export, and user profile updates (email, phone, name).
36
+ * session lifecycle (creation, expiration, refresh), wallet connecting/import/export, and user profile updates (email, phone, name).
36
37
  *
37
38
  * The provider automatically initializes the Turnkey client, fetches configuration (including proxy auth config if needed),
38
39
  * and synchronizes session and authentication state. It exposes a comprehensive set of methods for authentication flows,
@@ -41,9 +42,9 @@ var RemovePhoneNumber = require('../../components/user/RemovePhoneNumber.js');
41
42
  * Features:
42
43
  * - Passkey, Wallet, OTP (Email/SMS), and OAuth (Google, Apple, Facebook) authentication and sign-up flows.
43
44
  * - Session management: creation, expiration scheduling, refresh, and clearing.
44
- * - Wallet management: fetch, link, import, export, account management.
45
+ * - Wallet management: fetch, connect, import, export, account management.
45
46
  * - User profile management: email, phone, name, OAuth provider, and passkey linking/removal.
46
- * - Modal-driven UI flows for authentication, wallet linking, and profile updates.
47
+ * - Modal-driven UI flows for authentication, wallet connecting, and profile updates.
47
48
  * - Error handling and callback integration for custom error and event responses.
48
49
  *
49
50
  * Usage:
@@ -74,6 +75,7 @@ const ClientProvider = ({
74
75
  const proxyAuthConfigRef = react.useRef(null);
75
76
  const [allSessions, setAllSessions] = react.useState(undefined);
76
77
  const {
78
+ isMobile,
77
79
  pushPage,
78
80
  closeModal
79
81
  } = Hook.useModal();
@@ -92,6 +94,7 @@ const ClientProvider = ({
92
94
  const flow = stateParams.get("flow");
93
95
  const publicKey = stateParams.get("publicKey");
94
96
  const openModal = stateParams.get("openModal");
97
+ const sessionKey = stateParams.get("sessionKey");
95
98
  if (provider === "facebook" && flow === "redirect" && publicKey) {
96
99
  // We have all the required parameters for a Facebook PKCE flow
97
100
  const clientId = masterConfig?.auth?.oauthConfig?.facebookClientId;
@@ -116,8 +119,13 @@ const ClientProvider = ({
116
119
  await completeOauth({
117
120
  oidcToken,
118
121
  publicKey,
119
- providerName: "facebook"
122
+ providerName: "facebook",
123
+ ...(sessionKey && {
124
+ sessionKey
125
+ })
120
126
  });
127
+ // Clean up the URL after processing
128
+ window.history.replaceState(null, document.title, window.location.pathname);
121
129
  resolve();
122
130
  } catch (err) {
123
131
  reject(err);
@@ -140,6 +148,116 @@ const ClientProvider = ({
140
148
  });
141
149
  }
142
150
  }
151
+ if (provider === "discord" && flow === "redirect" && publicKey) {
152
+ const clientId = masterConfig?.auth?.oauthConfig?.discordClientId;
153
+ const redirectURI = masterConfig?.auth?.oauthConfig?.oauthRedirectUri;
154
+ const verifier = sessionStorage.getItem("discord_verifier");
155
+ const nonce = stateParams.get("nonce");
156
+ const sessionKey = stateParams.get("sessionKey");
157
+ if (clientId && redirectURI && verifier && nonce) {
158
+ await new Promise((resolve, reject) => {
159
+ pushPage({
160
+ key: `Discord OAuth`,
161
+ content: jsxRuntime.jsx(Action.ActionPage, {
162
+ title: `Authenticating with Discord...`,
163
+ action: async () => {
164
+ try {
165
+ const resp = await client?.httpClient.proxyOAuth2Authenticate({
166
+ provider: "OAUTH2_PROVIDER_DISCORD",
167
+ authCode: code,
168
+ redirectUri: redirectURI,
169
+ codeVerifier: verifier,
170
+ clientId,
171
+ nonce: nonce
172
+ });
173
+ sessionStorage.removeItem("discord_verifier");
174
+ const oidcToken = resp?.oidcToken;
175
+ if (!oidcToken) {
176
+ throw new sdkTypes.TurnkeyError("Missing OIDC token", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
177
+ }
178
+ await completeOauth({
179
+ oidcToken,
180
+ publicKey,
181
+ providerName: "discord",
182
+ ...(sessionKey && {
183
+ sessionKey
184
+ })
185
+ });
186
+ // Clean up the URL after processing
187
+ window.history.replaceState(null, document.title, window.location.pathname);
188
+ resolve(null);
189
+ } catch (err) {
190
+ reject(err);
191
+ if (callbacks?.onError) {
192
+ callbacks.onError(err instanceof sdkTypes.TurnkeyError ? err : new sdkTypes.TurnkeyError("Discord authentication failed", sdkTypes.TurnkeyErrorCodes.OAUTH_SIGNUP_ERROR, err));
193
+ }
194
+ }
195
+ },
196
+ icon: jsxRuntime.jsx(reactFontawesome.FontAwesomeIcon, {
197
+ size: "3x",
198
+ icon: freeBrandsSvgIcons.faDiscord
199
+ })
200
+ }),
201
+ showTitle: false
202
+ });
203
+ });
204
+ }
205
+ }
206
+ if (provider === "twitter" && flow === "redirect" && publicKey) {
207
+ const clientId = masterConfig?.auth?.oauthConfig?.xClientId;
208
+ const redirectURI = masterConfig?.auth?.oauthConfig?.oauthRedirectUri;
209
+ const verifier = sessionStorage.getItem("twitter_verifier");
210
+ const nonce = stateParams.get("nonce");
211
+ const sessionKey = stateParams.get("sessionKey");
212
+ if (clientId && redirectURI && verifier && nonce) {
213
+ await new Promise((resolve, reject) => {
214
+ pushPage({
215
+ key: `Twitter OAuth`,
216
+ content: jsxRuntime.jsx(Action.ActionPage, {
217
+ title: `Authenticating with Twitter...`,
218
+ action: async () => {
219
+ try {
220
+ const resp = await client?.httpClient.proxyOAuth2Authenticate({
221
+ provider: "OAUTH2_PROVIDER_X",
222
+ authCode: code,
223
+ redirectUri: redirectURI,
224
+ codeVerifier: verifier,
225
+ clientId,
226
+ nonce: nonce
227
+ });
228
+ sessionStorage.removeItem("twitter_verifier");
229
+ const oidcToken = resp?.oidcToken;
230
+ if (!oidcToken) {
231
+ throw new sdkTypes.TurnkeyError("Missing OIDC token", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
232
+ }
233
+ await completeOauth({
234
+ oidcToken,
235
+ publicKey,
236
+ providerName: "twitter",
237
+ ...(sessionKey && {
238
+ sessionKey
239
+ })
240
+ });
241
+ // Clean up the URL after processing
242
+ window.history.replaceState(null, document.title, window.location.pathname);
243
+ resolve(null);
244
+ } catch (err) {
245
+ reject(err);
246
+ if (callbacks?.onError) {
247
+ callbacks.onError(err instanceof sdkTypes.TurnkeyError ? err : new sdkTypes.TurnkeyError("Twitter authentication failed", sdkTypes.TurnkeyErrorCodes.OAUTH_SIGNUP_ERROR, err));
248
+ }
249
+ }
250
+ },
251
+ icon: jsxRuntime.jsx(reactFontawesome.FontAwesomeIcon, {
252
+ size: "3x",
253
+ icon: freeBrandsSvgIcons.faXTwitter
254
+ })
255
+ }),
256
+ showTitle: false
257
+ });
258
+ });
259
+ }
260
+ }
143
261
  }
144
262
  }
145
263
  // Handle Google/Apple redirects (uses hash with id_token)
@@ -151,7 +269,8 @@ const ClientProvider = ({
151
269
  provider,
152
270
  flow,
153
271
  publicKey,
154
- openModal
272
+ openModal,
273
+ sessionKey
155
274
  } = utils.parseOAuthRedirect(hash);
156
275
  if (idToken && flow === "redirect" && publicKey) {
157
276
  if (openModal === "true") {
@@ -183,7 +302,10 @@ const ClientProvider = ({
183
302
  publicKey,
184
303
  ...(provider ? {
185
304
  providerName: provider
186
- } : {})
305
+ } : {}),
306
+ ...(sessionKey && {
307
+ sessionKey
308
+ })
187
309
  });
188
310
  resolve(null);
189
311
  } catch (err) {
@@ -223,11 +345,21 @@ const ClientProvider = ({
223
345
  passkeyAuthEnabled: config.auth?.methods?.passkeyAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("passkey"),
224
346
  walletAuthEnabled: config.auth?.methods?.walletAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("wallet"),
225
347
  googleOauthEnabled: config.auth?.methods?.googleOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("google"),
348
+ xOauthEnabled: config.auth?.methods?.xOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("x"),
349
+ discordOauthEnabled: config.auth?.methods?.discordOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("discord"),
226
350
  appleOauthEnabled: config.auth?.methods?.appleOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("apple"),
227
351
  facebookOauthEnabled: config.auth?.methods?.facebookOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("facebook")
228
352
  };
353
+ const resolvedClientIds = {
354
+ googleClientId: config.auth?.oauthConfig?.googleClientId ?? proxyAuthConfig?.oauthClientIds?.google,
355
+ appleClientId: config.auth?.oauthConfig?.appleClientId ?? proxyAuthConfig?.oauthClientIds?.apple,
356
+ facebookClientId: config.auth?.oauthConfig?.facebookClientId ?? proxyAuthConfig?.oauthClientIds?.facebook,
357
+ xClientId: config.auth?.oauthConfig?.xClientId ?? proxyAuthConfig?.oauthClientIds?.x,
358
+ discordClientId: config.auth?.oauthConfig?.discordClientId ?? proxyAuthConfig?.oauthClientIds?.discord
359
+ };
360
+ const redirectUrl = config.auth?.oauthConfig?.oauthRedirectUri ?? proxyAuthConfig?.oauthRedirectUrl;
229
361
  // Set a default ordering for the oAuth methods
230
- const oauthOrder = config.auth?.oauthOrder ?? ["google", "apple", "facebook"].filter(provider => resolvedMethods[`${provider}OauthEnabled`]);
362
+ const oauthOrder = config.auth?.oauthOrder ?? ["google", "apple", "x", "discord", "facebook"].filter(provider => resolvedMethods[`${provider}OauthEnabled`]);
231
363
  // Set a default ordering for the overall auth methods
232
364
  const methodOrder = config.auth?.methodOrder ?? [oauthOrder.length > 0 ? "socials" : null, resolvedMethods.emailOtpAuthEnabled ? "email" : null, resolvedMethods.smsOtpAuthEnabled ? "sms" : null, resolvedMethods.passkeyAuthEnabled ? "passkey" : null, resolvedMethods.walletAuthEnabled ? "wallet" : null].filter(Boolean);
233
365
  return {
@@ -238,13 +370,18 @@ const ClientProvider = ({
238
370
  methods: resolvedMethods,
239
371
  oauthConfig: {
240
372
  ...config.auth?.oauthConfig,
241
- openOauthInPage: config.auth?.oauthConfig?.openOauthInPage
373
+ ...resolvedClientIds,
374
+ oauthRedirectUri: redirectUrl,
375
+ // on mobile we default to true since many mobile browsers
376
+ // (e.g. Safari) block popups
377
+ openOauthInPage: isMobile ? true : config.auth?.oauthConfig?.openOauthInPage
242
378
  },
243
379
  sessionExpirationSeconds: proxyAuthConfig?.sessionExpirationSeconds,
244
380
  methodOrder,
245
381
  oauthOrder,
246
382
  autoRefreshSession: config.auth?.autoRefreshSession ?? true
247
383
  },
384
+ autoRefreshManagedState: config.autoRefreshManagedState ?? true,
248
385
  walletConfig: {
249
386
  ...config.walletConfig,
250
387
  features: {
@@ -442,14 +579,13 @@ const ClientProvider = ({
442
579
  }
443
580
  /**
444
581
  * @internal
445
- * Schedules a session expiration and warning timeout for the given session key.
582
+ * Schedules a session expiration and warning timer for the given session key.
446
583
  *
447
- * - This function sets up two timeouts: one for warning before the session expires and another to expire the session.
448
- * - The warning timeout is set to trigger before the session expires, allowing for actions like refreshing the session.
449
- * - The expiration timeout clears the session and triggers any necessary callbacks.
584
+ * - Sets up two timers: one for warning before expiry and one for actual expiry.
585
+ * - Uses capped timeouts under the hood so delays > 24.8 days are safe (see utils/timers.ts).
450
586
  *
451
587
  * @param params.sessionKey - The key of the session to schedule expiration for.
452
- * @param params.expiry - The expiration time in seconds for the session.
588
+ * @param params.expiry - The expiration time in seconds since epoch.
453
589
  * @throws {TurnkeyError} If an error occurs while scheduling the session expiration.
454
590
  */
455
591
  async function scheduleSessionExpiration(params) {
@@ -458,21 +594,18 @@ const ClientProvider = ({
458
594
  expiry
459
595
  } = params;
460
596
  try {
461
- // Clear any existing timeout for this session key
462
- if (expiryTimeoutsRef.current[sessionKey]) {
463
- clearTimeout(expiryTimeoutsRef.current[sessionKey]);
464
- delete expiryTimeoutsRef.current[sessionKey];
465
- }
466
- if (expiryTimeoutsRef.current[`${sessionKey}-warning`]) {
467
- clearTimeout(expiryTimeoutsRef.current[`${sessionKey}-warning`]);
468
- delete expiryTimeoutsRef.current[`${sessionKey}-warning`];
469
- }
470
- const timeUntilExpiry = expiry * 1000 - Date.now();
597
+ const warnKey = `${sessionKey}-warning`;
598
+ // Replace any prior timers for this session
599
+ timers.clearKey(expiryTimeoutsRef.current, sessionKey);
600
+ timers.clearKey(expiryTimeoutsRef.current, warnKey);
601
+ const now = Date.now();
602
+ const expiryMs = expiry * 1000;
603
+ const timeUntilExpiry = expiryMs - now;
471
604
  const beforeExpiry = async () => {
472
605
  const activeSession = await getSession();
473
- if (!activeSession && expiryTimeoutsRef.current[sessionKey]) {
474
- clearTimeout(expiryTimeoutsRef.current[`${sessionKey}-warning`]);
475
- expiryTimeoutsRef.current[`${sessionKey}-warning`] = setTimeout(beforeExpiry, 10000);
606
+ if (!activeSession && expiryTimeoutsRef.current[warnKey]) {
607
+ // Keep nudging until session materializes (short 10s timer is fine)
608
+ timers.setTimeoutInMap(expiryTimeoutsRef.current, warnKey, beforeExpiry, 10_000);
476
609
  return;
477
610
  }
478
611
  const session = await getSession({
@@ -500,27 +633,36 @@ const ClientProvider = ({
500
633
  if ((await getActiveSessionKey()) === sessionKey) {
501
634
  setSession(undefined);
502
635
  }
503
- setAllSessions(prevSessions => {
504
- if (!prevSessions) return prevSessions;
505
- const newSessions = {
506
- ...prevSessions
636
+ setAllSessions(prev => {
637
+ if (!prev) return prev;
638
+ const next = {
639
+ ...prev
507
640
  };
508
- delete newSessions[sessionKey];
509
- return newSessions;
641
+ delete next[sessionKey];
642
+ return next;
510
643
  });
511
644
  await clearSession({
512
645
  sessionKey
513
646
  });
514
- delete expiryTimeoutsRef.current[sessionKey];
515
- delete expiryTimeoutsRef.current[`${sessionKey}-warning`];
647
+ // Remove timers for this session
648
+ timers.clearKey(expiryTimeoutsRef.current, sessionKey);
649
+ timers.clearKey(expiryTimeoutsRef.current, warnKey);
516
650
  await logout();
517
651
  };
518
- if (timeUntilExpiry <= utils.SESSION_WARNING_THRESHOLD_MS) {
519
- beforeExpiry();
652
+ // Already expired → expire immediately
653
+ if (timeUntilExpiry <= 0) {
654
+ await expireSession();
655
+ return;
656
+ }
657
+ // Warning timer (if threshold is in the future)
658
+ const warnAt = expiryMs - utils.SESSION_WARNING_THRESHOLD_MS;
659
+ if (warnAt <= now) {
660
+ void beforeExpiry(); // fire-and-forget is fine
520
661
  } else {
521
- expiryTimeoutsRef.current[`${sessionKey}-warning`] = setTimeout(beforeExpiry, timeUntilExpiry - utils.SESSION_WARNING_THRESHOLD_MS);
662
+ timers.setCappedTimeoutInMap(expiryTimeoutsRef.current, warnKey, beforeExpiry, warnAt - now);
522
663
  }
523
- expiryTimeoutsRef.current[sessionKey] = setTimeout(expireSession, timeUntilExpiry);
664
+ // Actual expiry timer (safe for long delays)
665
+ timers.setCappedTimeoutInMap(expiryTimeoutsRef.current, sessionKey, expireSession, timeUntilExpiry);
524
666
  } catch (error) {
525
667
  if (error instanceof sdkTypes.TurnkeyError || error instanceof sdkTypes.TurnkeyNetworkError) {
526
668
  callbacks?.onError?.(error);
@@ -530,25 +672,25 @@ const ClientProvider = ({
530
672
  }
531
673
  }
532
674
  /**
533
- * Clears all scheduled session expiration and warning timeouts for the client.
675
+ * Clears all scheduled session timers (warning + expiry).
534
676
  *
535
- * - This function removes all active session expiration and warning timeouts managed by the provider.
536
- * - It is called automatically when sessions are re-initialized or on logout to prevent memory leaks and ensure no stale timeouts remain.
537
- * - All timeouts stored in `expiryTimeoutsRef` are cleared and the reference is reset.
677
+ * - Removes all active timers managed by this client.
678
+ * - Useful on re-init or logout to avoid stale timers.
538
679
  *
539
- * @throws {TurnkeyError} If an error occurs while clearing the timeouts.
680
+ * @throws {TurnkeyError} If an error occurs while clearing the timers.
540
681
  */
541
- function clearSessionTimeouts() {
682
+ function clearSessionTimeouts(sessionKeys) {
542
683
  try {
543
- Object.values(expiryTimeoutsRef.current).forEach(timeout => {
544
- clearTimeout(timeout);
545
- });
546
- expiryTimeoutsRef.current = {};
684
+ if (sessionKeys) {
685
+ timers.clearKeys(expiryTimeoutsRef.current, sessionKeys);
686
+ } else {
687
+ timers.clearAll(expiryTimeoutsRef.current); // clears & deletes everything
688
+ }
547
689
  } catch (error) {
548
690
  if (error instanceof sdkTypes.TurnkeyError || error instanceof sdkTypes.TurnkeyNetworkError) {
549
691
  callbacks?.onError?.(error);
550
692
  } else {
551
- callbacks?.onError?.(new sdkTypes.TurnkeyError(`Failed to clear session timeouts`, sdkTypes.TurnkeyErrorCodes.CLEAR_SESSION_TIMEOUTS_ERROR, error));
693
+ callbacks?.onError?.(new sdkTypes.TurnkeyError("Failed to clear session timeouts", sdkTypes.TurnkeyErrorCodes.CLEAR_SESSION_TIMEOUTS_ERROR, error));
552
694
  }
553
695
  }
554
696
  }
@@ -564,7 +706,12 @@ const ClientProvider = ({
564
706
  * @returns A void promise.
565
707
  * @throws {TurnkeyError} If the client is not initialized or if there is an error during the process.
566
708
  */
567
- const handlePostAuth = async () => {
709
+ const handlePostAuth = async params => {
710
+ const {
711
+ method,
712
+ action,
713
+ identifier
714
+ } = params;
568
715
  try {
569
716
  const sessionKey = await getActiveSessionKey();
570
717
  const session = await getSession({
@@ -581,6 +728,12 @@ const ClientProvider = ({
581
728
  setAllSessions(allSessions);
582
729
  await refreshWallets();
583
730
  await refreshUser();
731
+ callbacks?.onAuthenticationSuccess?.({
732
+ session,
733
+ method,
734
+ action,
735
+ identifier
736
+ });
584
737
  } catch (error) {
585
738
  if (error instanceof sdkTypes.TurnkeyError || error instanceof sdkTypes.TurnkeyNetworkError) {
586
739
  callbacks?.onError?.(error);
@@ -594,19 +747,29 @@ const ClientProvider = ({
594
747
  * Handles the post-logout flow.
595
748
  *
596
749
  * - This function is called after a successful logout or session clear.
597
- * - It clears all scheduled session expiration and warning timeouts to prevent memory leaks.
598
- * - It resets the session state, removes all session and user data from memory, and clears the wallets list.
750
+ * - It clears all scheduled session expiration and warning timeouts associated to the session key to prevent memory leaks.
751
+ * - It resets the session state, removes user data from memory, the logged out session from all sessions state, and clears the wallets list.
599
752
  * - This ensures that all sensitive information is removed from the provider state after logout.
600
753
  * - Called internally after logout or when all sessions are cleared.
601
754
  *
602
755
  * @returns void
603
756
  * @throws {TurnkeyError} If there is an error during the post-logout process.
604
757
  */
605
- const handlePostLogout = () => {
758
+ const handlePostLogout = sessionKey => {
606
759
  try {
607
- clearSessionTimeouts();
760
+ clearSessionTimeouts(sessionKey ? [sessionKey, `${sessionKey}-warning`] : undefined);
761
+ setAllSessions(prev => {
762
+ if (!prev) return prev;
763
+ if (sessionKey) {
764
+ const next = {
765
+ ...prev
766
+ };
767
+ delete next[sessionKey];
768
+ return next;
769
+ }
770
+ return {};
771
+ });
608
772
  setSession(undefined);
609
- setAllSessions(undefined);
610
773
  setUser(undefined);
611
774
  setWallets([]);
612
775
  } catch (error) {
@@ -625,8 +788,14 @@ const ClientProvider = ({
625
788
  if (!client) {
626
789
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
627
790
  }
628
- await utils.withTurnkeyErrorHandling(() => client.logout(params), callbacks, "Failed to logout");
629
- handlePostLogout();
791
+ await utils.withTurnkeyErrorHandling(async () => {
792
+ // If no sessionKey is provided, we try to get the active one.
793
+ let sessionKey = params?.sessionKey;
794
+ if (!sessionKey) sessionKey = await getActiveSessionKey();
795
+ await client.logout(params);
796
+ // We only handle post logout if the sessionKey is defined since that means we actually logged out of a session.
797
+ if (sessionKey) handlePostLogout(sessionKey);
798
+ }, callbacks, "Failed to logout");
630
799
  return;
631
800
  }, [client, callbacks]);
632
801
  const loginWithPasskey = react.useCallback(async params => {
@@ -639,7 +808,11 @@ const ClientProvider = ({
639
808
  expirationSeconds
640
809
  }), callbacks, "Failed to login with passkey");
641
810
  if (res) {
642
- await handlePostAuth();
811
+ await handlePostAuth({
812
+ method: base.AuthMethod.Passkey,
813
+ action: sdkTypes.AuthAction.LOGIN,
814
+ identifier: res.credentialId
815
+ });
643
816
  }
644
817
  return res;
645
818
  }, [client, callbacks]);
@@ -674,15 +847,19 @@ const ClientProvider = ({
674
847
  expirationSeconds
675
848
  }), callbacks, "Failed to sign up with passkey");
676
849
  if (res) {
677
- await handlePostAuth();
850
+ await handlePostAuth({
851
+ method: base.AuthMethod.Passkey,
852
+ action: sdkTypes.AuthAction.SIGNUP,
853
+ identifier: res.credentialId
854
+ });
678
855
  }
679
856
  return res;
680
857
  }, [client, callbacks]);
681
- const getWalletProviders = react.useCallback(async chain => {
858
+ const fetchWalletProviders = react.useCallback(async chain => {
682
859
  if (!client) {
683
860
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
684
861
  }
685
- const newProviders = await client.getWalletProviders(chain);
862
+ const newProviders = await client.fetchWalletProviders(chain);
686
863
  // we update state with the latest providers
687
864
  // we keep this state so that initializeWalletProviderListeners() re-runs
688
865
  // whenever the list of connected providers changes
@@ -705,7 +882,7 @@ const ClientProvider = ({
705
882
  await client.disconnectWalletAccount(walletProvider);
706
883
  // we only refresh the wallets if:
707
884
  // 1. there is an active session. This is needed because for WalletConnect
708
- // you can unlink a wallet before actually being logged in
885
+ // you can disconnect a wallet before actually being logged in
709
886
  //
710
887
  // 2. it was a WalletConnect provider that we just disconnected. Since
711
888
  // native providers emit a disconnect event which will already refresh
@@ -734,7 +911,11 @@ const ClientProvider = ({
734
911
  expirationSeconds
735
912
  }), callbacks, "Failed to login with wallet");
736
913
  if (res) {
737
- await handlePostAuth();
914
+ await handlePostAuth({
915
+ method: base.AuthMethod.Wallet,
916
+ action: sdkTypes.AuthAction.LOGIN,
917
+ identifier: res.address
918
+ });
738
919
  }
739
920
  return res;
740
921
  }, [client, callbacks]);
@@ -759,7 +940,11 @@ const ClientProvider = ({
759
940
  expirationSeconds
760
941
  }), callbacks, "Failed to sign up with wallet");
761
942
  if (res) {
762
- await handlePostAuth();
943
+ await handlePostAuth({
944
+ method: base.AuthMethod.Wallet,
945
+ action: sdkTypes.AuthAction.SIGNUP,
946
+ identifier: res.address
947
+ });
763
948
  }
764
949
  return res;
765
950
  }, [client, callbacks, masterConfig]);
@@ -784,7 +969,11 @@ const ClientProvider = ({
784
969
  expirationSeconds
785
970
  }), callbacks, "Failed to login or sign up with wallet");
786
971
  if (res) {
787
- await handlePostAuth();
972
+ await handlePostAuth({
973
+ method: base.AuthMethod.Wallet,
974
+ action: res.action,
975
+ identifier: res.address
976
+ });
788
977
  }
789
978
  return res;
790
979
  }, [client, callbacks, masterConfig]);
@@ -806,7 +995,11 @@ const ClientProvider = ({
806
995
  }
807
996
  const res = await utils.withTurnkeyErrorHandling(() => client.loginWithOtp(params), callbacks, "Failed to login with OTP");
808
997
  if (res) {
809
- await handlePostAuth();
998
+ await handlePostAuth({
999
+ method: base.AuthMethod.Otp,
1000
+ action: sdkTypes.AuthAction.LOGIN,
1001
+ identifier: params.verificationToken
1002
+ });
810
1003
  }
811
1004
  return res;
812
1005
  }, [client, callbacks]);
@@ -834,7 +1027,11 @@ const ClientProvider = ({
834
1027
  };
835
1028
  const res = await utils.withTurnkeyErrorHandling(() => client.signUpWithOtp(params), callbacks, "Failed to sign up with OTP");
836
1029
  if (res) {
837
- await handlePostAuth();
1030
+ await handlePostAuth({
1031
+ method: base.AuthMethod.Otp,
1032
+ action: sdkTypes.AuthAction.SIGNUP,
1033
+ identifier: params.verificationToken
1034
+ });
838
1035
  }
839
1036
  return res;
840
1037
  }, [client, callbacks, masterConfig]);
@@ -862,11 +1059,29 @@ const ClientProvider = ({
862
1059
  };
863
1060
  const res = await utils.withTurnkeyErrorHandling(() => client.completeOtp(params), callbacks, "Failed to complete OTP");
864
1061
  if (res) {
865
- await handlePostAuth();
1062
+ await handlePostAuth({
1063
+ method: base.AuthMethod.Otp,
1064
+ action: res.action,
1065
+ identifier: res.verificationToken
1066
+ });
866
1067
  }
867
1068
  return res;
868
1069
  }, [client, callbacks, masterConfig]);
869
- const completeOauth = react.useCallback(async params => {
1070
+ const loginWithOauth = react.useCallback(async params => {
1071
+ if (!client) {
1072
+ throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1073
+ }
1074
+ const res = await utils.withTurnkeyErrorHandling(() => client.loginWithOauth(params), callbacks, "Failed to login with OAuth");
1075
+ if (res) {
1076
+ await handlePostAuth({
1077
+ method: base.AuthMethod.Oauth,
1078
+ action: sdkTypes.AuthAction.LOGIN,
1079
+ identifier: params.oidcToken
1080
+ });
1081
+ }
1082
+ return res;
1083
+ }, [client, callbacks]);
1084
+ const signUpWithOauth = react.useCallback(async params => {
870
1085
  if (!client) {
871
1086
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
872
1087
  }
@@ -874,30 +1089,24 @@ const ClientProvider = ({
874
1089
  throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
875
1090
  }
876
1091
  // If createSubOrgParams is not provided, use the default from masterConfig
877
- const createSubOrgParams = params.createSubOrgParams ?? masterConfig.auth?.createSuborgParams?.oauth;
1092
+ let createSubOrgParams = params.createSubOrgParams ?? masterConfig.auth?.createSuborgParams?.oauth;
878
1093
  params = createSubOrgParams !== undefined ? {
879
1094
  ...params,
880
1095
  createSubOrgParams
881
1096
  } : {
882
1097
  ...params
883
1098
  };
884
- const res = await utils.withTurnkeyErrorHandling(() => client.completeOauth(params), callbacks, "Failed to complete OAuth");
1099
+ const res = await utils.withTurnkeyErrorHandling(() => client.signUpWithOauth(params), callbacks, "Failed to sign up with OAuth");
885
1100
  if (res) {
886
- await handlePostAuth();
1101
+ await handlePostAuth({
1102
+ method: base.AuthMethod.Oauth,
1103
+ action: sdkTypes.AuthAction.SIGNUP,
1104
+ identifier: params.oidcToken
1105
+ });
887
1106
  }
888
1107
  return res;
889
1108
  }, [client, callbacks, masterConfig]);
890
- const loginWithOauth = react.useCallback(async params => {
891
- if (!client) {
892
- throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
893
- }
894
- const res = await utils.withTurnkeyErrorHandling(() => client.loginWithOauth(params), callbacks, "Failed to login with OAuth");
895
- if (res) {
896
- await handlePostAuth();
897
- }
898
- return res;
899
- }, [client, callbacks]);
900
- const signUpWithOauth = react.useCallback(async params => {
1109
+ const completeOauth = react.useCallback(async params => {
901
1110
  if (!client) {
902
1111
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
903
1112
  }
@@ -905,16 +1114,20 @@ const ClientProvider = ({
905
1114
  throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
906
1115
  }
907
1116
  // If createSubOrgParams is not provided, use the default from masterConfig
908
- let createSubOrgParams = params.createSubOrgParams ?? masterConfig.auth?.createSuborgParams?.oauth;
1117
+ const createSubOrgParams = params.createSubOrgParams ?? masterConfig.auth?.createSuborgParams?.oauth;
909
1118
  params = createSubOrgParams !== undefined ? {
910
1119
  ...params,
911
1120
  createSubOrgParams
912
1121
  } : {
913
1122
  ...params
914
1123
  };
915
- const res = await utils.withTurnkeyErrorHandling(() => client.signUpWithOauth(params), callbacks, "Failed to sign up with OAuth");
1124
+ const res = await utils.withTurnkeyErrorHandling(() => client.completeOauth(params), callbacks, "Failed to complete OAuth");
916
1125
  if (res) {
917
- await handlePostAuth();
1126
+ await handlePostAuth({
1127
+ method: base.AuthMethod.Oauth,
1128
+ action: res.action,
1129
+ identifier: params.oidcToken
1130
+ });
918
1131
  }
919
1132
  return res;
920
1133
  }, [client, callbacks, masterConfig]);
@@ -930,6 +1143,12 @@ const ClientProvider = ({
930
1143
  }
931
1144
  return utils.withTurnkeyErrorHandling(() => client.fetchWalletAccounts(params), callbacks, "Failed to fetch wallet accounts");
932
1145
  }, [client, callbacks]);
1146
+ const fetchPrivateKeys = react.useCallback(async params => {
1147
+ if (!client) {
1148
+ throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1149
+ }
1150
+ return utils.withTurnkeyErrorHandling(() => client.fetchPrivateKeys(params), callbacks, "Failed to fetch private keys");
1151
+ }, [client, callbacks]);
933
1152
  const signMessage = react.useCallback(async params => {
934
1153
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
935
1154
  return utils.withTurnkeyErrorHandling(() => client.signMessage(params), callbacks, "Failed to sign message");
@@ -941,7 +1160,7 @@ const ClientProvider = ({
941
1160
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
942
1161
  return new Promise((resolve, reject) => {
943
1162
  pushPage({
944
- key: "Sign Message",
1163
+ key: "Sign message",
945
1164
  content: jsxRuntime.jsx(Message.SignMessageModal, {
946
1165
  message: params.message,
947
1166
  subText: params?.subText,
@@ -979,6 +1198,14 @@ const ClientProvider = ({
979
1198
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
980
1199
  return utils.withTurnkeyErrorHandling(() => client.fetchUser(params), callbacks, "Failed to fetch user");
981
1200
  }, [client, callbacks]);
1201
+ const fetchOrCreateP256ApiKeyUser = react.useCallback(async params => {
1202
+ if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1203
+ return utils.withTurnkeyErrorHandling(() => client.fetchOrCreateP256ApiKeyUser(params), callbacks, "Failed to fetch or create delegated access user");
1204
+ }, [client, callbacks]);
1205
+ const fetchOrCreatePolicies = react.useCallback(async params => {
1206
+ if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1207
+ return utils.withTurnkeyErrorHandling(() => client.fetchOrCreatePolicies(params), callbacks, "Failed to fetch or create delegated access user");
1208
+ }, [client, callbacks]);
982
1209
  const updateUserEmail = react.useCallback(async params => {
983
1210
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
984
1211
  const res = await utils.withTurnkeyErrorHandling(() => client.updateUserEmail(params), callbacks, "Failed to update user email");
@@ -1087,7 +1314,7 @@ const ClientProvider = ({
1087
1314
  stampWith: params?.stampWith
1088
1315
  });
1089
1316
  return res;
1090
- }, [client, callbacks]);
1317
+ }, [client, callbacks, masterConfig, session, user]);
1091
1318
  const importWallet = react.useCallback(async params => {
1092
1319
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1093
1320
  const res = await utils.withTurnkeyErrorHandling(() => client.importWallet(params), callbacks, "Failed to import wallet");
@@ -1095,16 +1322,16 @@ const ClientProvider = ({
1095
1322
  stampWith: params?.stampWith
1096
1323
  });
1097
1324
  return res;
1098
- }, [client, callbacks]);
1325
+ }, [client, callbacks, masterConfig, session, user]);
1099
1326
  const importPrivateKey = react.useCallback(async params => {
1100
1327
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1101
1328
  const res = await utils.withTurnkeyErrorHandling(() => client.importPrivateKey(params), callbacks, "Failed to import private key");
1102
1329
  return res;
1103
- }, [client, callbacks]);
1330
+ }, [client, callbacks, masterConfig, session, user]);
1104
1331
  const deleteSubOrganization = react.useCallback(async params => {
1105
1332
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1106
1333
  return utils.withTurnkeyErrorHandling(() => client.deleteSubOrganization(params), callbacks, "Failed to delete sub-organization");
1107
- }, [client, callbacks]);
1334
+ }, [client, callbacks, masterConfig, session, user]);
1108
1335
  const storeSession = react.useCallback(async params => {
1109
1336
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1110
1337
  await utils.withTurnkeyErrorHandling(() => client.storeSession(params), callbacks, "Failed to store session");
@@ -1122,7 +1349,7 @@ const ClientProvider = ({
1122
1349
  setSession(session);
1123
1350
  setAllSessions(allSessions);
1124
1351
  return;
1125
- }, [client, callbacks]);
1352
+ }, [client, callbacks, masterConfig, session, user]);
1126
1353
  const clearSession = react.useCallback(async params => {
1127
1354
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1128
1355
  await utils.withTurnkeyErrorHandling(() => client.clearSession(params), callbacks, "Failed to clear session");
@@ -1131,13 +1358,13 @@ const ClientProvider = ({
1131
1358
  setSession(session);
1132
1359
  setAllSessions(allSessions);
1133
1360
  return;
1134
- }, [client, callbacks]);
1361
+ }, [client, callbacks, session, user, masterConfig]);
1135
1362
  const clearAllSessions = react.useCallback(async () => {
1136
1363
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1137
1364
  setSession(undefined);
1138
1365
  setAllSessions(undefined);
1139
1366
  return await utils.withTurnkeyErrorHandling(() => client.clearAllSessions(), callbacks, "Failed to clear all sessions");
1140
- }, [client, callbacks]);
1367
+ }, [client, callbacks, session, user, masterConfig]);
1141
1368
  const refreshSession = react.useCallback(async params => {
1142
1369
  if (!client) {
1143
1370
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
@@ -1166,15 +1393,15 @@ const ClientProvider = ({
1166
1393
  setSession(session);
1167
1394
  setAllSessions(allSessions);
1168
1395
  return res;
1169
- }, [client, callbacks]);
1396
+ }, [client, callbacks, scheduleSessionExpiration, session, user, masterConfig]);
1170
1397
  const getSession = react.useCallback(async params => {
1171
1398
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1172
1399
  return utils.withTurnkeyErrorHandling(() => client.getSession(params), callbacks, "Failed to get session");
1173
- }, [client, callbacks]);
1400
+ }, [client, callbacks, masterConfig, session, user]);
1174
1401
  const getAllSessions = react.useCallback(async () => {
1175
1402
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1176
1403
  return utils.withTurnkeyErrorHandling(() => client.getAllSessions(), callbacks, "Failed to get all sessions");
1177
- }, [client, callbacks]);
1404
+ }, [client, callbacks, masterConfig, session, user]);
1178
1405
  const setActiveSession = react.useCallback(async params => {
1179
1406
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1180
1407
  const session = await utils.withTurnkeyErrorHandling(() => client.getSession({
@@ -1185,25 +1412,30 @@ const ClientProvider = ({
1185
1412
  }
1186
1413
  await utils.withTurnkeyErrorHandling(() => client.setActiveSession(params), callbacks, "Failed to set active session");
1187
1414
  setSession(session);
1415
+ await utils.withTurnkeyErrorHandling(async () => {
1416
+ await refreshWallets();
1417
+ await refreshUser();
1418
+ }, callbacks, "Failed to refresh data after setting active session");
1188
1419
  return;
1189
- }, [client, callbacks]);
1420
+ }, [client, callbacks, session, user, masterConfig]);
1190
1421
  const getActiveSessionKey = react.useCallback(async () => {
1191
1422
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1192
1423
  return utils.withTurnkeyErrorHandling(() => client.getActiveSessionKey(), callbacks, "Failed to get active session key");
1193
- }, [client, callbacks]);
1424
+ }, [client, callbacks, masterConfig, session, user]);
1194
1425
  const clearUnusedKeyPairs = react.useCallback(async () => {
1195
1426
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1196
1427
  return utils.withTurnkeyErrorHandling(() => client.clearUnusedKeyPairs(), callbacks, "Failed to clear unused key pairs");
1197
- }, [client, callbacks]);
1428
+ }, [client, callbacks, masterConfig, session, user]);
1198
1429
  const createApiKeyPair = react.useCallback(async params => {
1199
1430
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1200
1431
  return utils.withTurnkeyErrorHandling(() => client.createApiKeyPair(params), callbacks, "Failed to create API key pair");
1201
- }, [client, callbacks]);
1432
+ }, [client, callbacks, session, user, masterConfig]);
1202
1433
  const getProxyAuthConfig = react.useCallback(async () => {
1203
1434
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1204
1435
  return utils.withTurnkeyErrorHandling(() => client.getProxyAuthConfig(), callbacks, "Failed to get proxy auth config");
1205
- }, [client, callbacks]);
1436
+ }, [client, callbacks, masterConfig, session, user]);
1206
1437
  const refreshUser = react.useCallback(async params => {
1438
+ if (!masterConfig?.autoRefreshManagedState) return;
1207
1439
  const {
1208
1440
  stampWith
1209
1441
  } = params || {};
@@ -1214,13 +1446,14 @@ const ClientProvider = ({
1214
1446
  if (user) {
1215
1447
  setUser(user);
1216
1448
  }
1217
- }, [client, callbacks]);
1449
+ }, [client, callbacks, fetchUser, masterConfig, session, user]);
1218
1450
  const refreshWallets = react.useCallback(async params => {
1451
+ if (!masterConfig?.autoRefreshManagedState) return;
1219
1452
  const {
1220
1453
  stampWith
1221
1454
  } = params || {};
1222
1455
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1223
- const walletProviders = await utils.withTurnkeyErrorHandling(() => getWalletProviders(), callbacks, "Failed to refresh wallets");
1456
+ const walletProviders = await utils.withTurnkeyErrorHandling(() => fetchWalletProviders(), callbacks, "Failed to refresh wallets");
1224
1457
  const wallets = await utils.withTurnkeyErrorHandling(() => fetchWallets({
1225
1458
  stampWith,
1226
1459
  walletProviders
@@ -1228,7 +1461,285 @@ const ClientProvider = ({
1228
1461
  if (wallets) {
1229
1462
  setWallets(wallets);
1230
1463
  }
1231
- }, [client, callbacks, getWalletProviders, fetchWallets]);
1464
+ }, [client, callbacks, fetchWalletProviders, fetchWallets, masterConfig, session, user]);
1465
+ const handleDiscordOauth = react.useCallback(async params => {
1466
+ const {
1467
+ clientId = masterConfig?.auth?.oauthConfig?.discordClientId,
1468
+ openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
1469
+ additionalState: additionalParameters
1470
+ } = params || {};
1471
+ try {
1472
+ if (!masterConfig) {
1473
+ throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1474
+ }
1475
+ if (!clientId) {
1476
+ throw new sdkTypes.TurnkeyError("Discord Client ID is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1477
+ }
1478
+ if (!masterConfig.auth?.oauthConfig?.oauthRedirectUri) {
1479
+ throw new sdkTypes.TurnkeyError("OAuth Redirect URI is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1480
+ }
1481
+ const flow = openInPage ? "redirect" : "popup";
1482
+ const redirectURI = masterConfig.auth?.oauthConfig.oauthRedirectUri;
1483
+ // Create key pair and generate nonce
1484
+ const publicKey = await createApiKeyPair();
1485
+ if (!publicKey) {
1486
+ throw new Error("Failed to create public key for OAuth.");
1487
+ }
1488
+ const nonce = utils$1.bytesToHex(sha2.sha256(publicKey));
1489
+ // Generate PKCE challenge pair
1490
+ const {
1491
+ verifier,
1492
+ codeChallenge
1493
+ } = await utils.generateChallengePair();
1494
+ sessionStorage.setItem("discord_verifier", verifier);
1495
+ // Construct Discord Auth URL
1496
+ const discordAuthUrl = new URL(utils.DISCORD_AUTH_URL);
1497
+ discordAuthUrl.searchParams.set("client_id", clientId);
1498
+ discordAuthUrl.searchParams.set("redirect_uri", redirectURI);
1499
+ discordAuthUrl.searchParams.set("response_type", "code");
1500
+ discordAuthUrl.searchParams.set("code_challenge", codeChallenge);
1501
+ discordAuthUrl.searchParams.set("code_challenge_method", "S256");
1502
+ discordAuthUrl.searchParams.set("scope", "identify email");
1503
+ discordAuthUrl.searchParams.set("state", `provider=discord&flow=${flow}&publicKey=${encodeURIComponent(publicKey)}&nonce=${nonce}`);
1504
+ if (additionalParameters) {
1505
+ const extra = Object.entries(additionalParameters).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
1506
+ if (extra) {
1507
+ discordAuthUrl.searchParams.set("state", discordAuthUrl.searchParams.get("state") + `&${extra}`);
1508
+ }
1509
+ }
1510
+ if (openInPage) {
1511
+ window.location.href = discordAuthUrl.toString();
1512
+ return new Promise((_, reject) => {
1513
+ const timeout = setTimeout(() => {
1514
+ reject(new Error("Authentication timed out."));
1515
+ }, 300000);
1516
+ window.addEventListener("beforeunload", () => clearTimeout(timeout));
1517
+ });
1518
+ } else {
1519
+ const width = utils.popupWidth;
1520
+ const height = utils.popupHeight;
1521
+ const left = window.screenX + (window.innerWidth - width) / 2;
1522
+ const top = window.screenY + (window.innerHeight - height) / 2;
1523
+ const authWindow = window.open("about:blank", "_blank", `width=${width},height=${height},top=${top},left=${left},scrollbars=yes,resizable=yes`);
1524
+ if (!authWindow) {
1525
+ throw new Error("Failed to open Discord login window.");
1526
+ }
1527
+ authWindow.location.href = discordAuthUrl.toString();
1528
+ return new Promise((resolve, reject) => {
1529
+ const interval = setInterval(() => {
1530
+ try {
1531
+ if (authWindow.closed) {
1532
+ clearInterval(interval);
1533
+ reject(new Error("Authentication window was closed."));
1534
+ return;
1535
+ }
1536
+ const url = authWindow.location.href || "";
1537
+ if (url.startsWith(window.location.origin)) {
1538
+ const urlParams = new URLSearchParams(new URL(url).search);
1539
+ const authCode = urlParams.get("code");
1540
+ const stateParam = urlParams.get("state");
1541
+ const sessionKey = stateParam?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1542
+ if (authCode && stateParam && stateParam.includes("provider=discord")) {
1543
+ authWindow.close();
1544
+ clearInterval(interval);
1545
+ const verifier = sessionStorage.getItem("discord_verifier");
1546
+ if (!verifier) {
1547
+ reject(new Error("Missing PKCE verifier"));
1548
+ return;
1549
+ }
1550
+ client?.httpClient.proxyOAuth2Authenticate({
1551
+ provider: "OAUTH2_PROVIDER_DISCORD",
1552
+ authCode,
1553
+ redirectUri: redirectURI,
1554
+ codeVerifier: verifier,
1555
+ clientId,
1556
+ nonce: nonce
1557
+ }).then(resp => {
1558
+ sessionStorage.removeItem("discord_verifier");
1559
+ const oidcToken = resp.oidcToken;
1560
+ if (params?.onOauthSuccess) {
1561
+ params.onOauthSuccess({
1562
+ oidcToken,
1563
+ providerName: "discord",
1564
+ ...(sessionKey && {
1565
+ sessionKey
1566
+ })
1567
+ });
1568
+ } else if (callbacks?.onOauthRedirect) {
1569
+ callbacks.onOauthRedirect({
1570
+ idToken: oidcToken,
1571
+ publicKey,
1572
+ ...(sessionKey && {
1573
+ sessionKey
1574
+ })
1575
+ });
1576
+ } else {
1577
+ completeOauth({
1578
+ oidcToken,
1579
+ publicKey,
1580
+ providerName: "discord",
1581
+ ...(sessionKey && {
1582
+ sessionKey
1583
+ })
1584
+ }).then(() => resolve()).catch(reject);
1585
+ return;
1586
+ }
1587
+ resolve();
1588
+ }).catch(reject);
1589
+ }
1590
+ }
1591
+ } catch {
1592
+ // ignore cross-origin
1593
+ }
1594
+ }, 500);
1595
+ if (authWindow.closed) {
1596
+ clearInterval(interval);
1597
+ }
1598
+ });
1599
+ }
1600
+ } catch (error) {
1601
+ throw error;
1602
+ }
1603
+ }, [client, callbacks, completeOauth, createApiKeyPair, masterConfig, session, user]);
1604
+ const handleXOauth = react.useCallback(async params => {
1605
+ const {
1606
+ clientId = masterConfig?.auth?.oauthConfig?.xClientId,
1607
+ openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
1608
+ additionalState: additionalParameters
1609
+ } = params || {};
1610
+ try {
1611
+ if (!masterConfig) {
1612
+ throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1613
+ }
1614
+ if (!clientId) {
1615
+ throw new sdkTypes.TurnkeyError("Twitter Client ID is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1616
+ }
1617
+ if (!masterConfig.auth?.oauthConfig?.oauthRedirectUri) {
1618
+ throw new sdkTypes.TurnkeyError("OAuth Redirect URI is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1619
+ }
1620
+ const flow = openInPage ? "redirect" : "popup";
1621
+ const redirectURI = masterConfig.auth?.oauthConfig.oauthRedirectUri;
1622
+ // Create key pair and generate nonce
1623
+ const publicKey = await createApiKeyPair();
1624
+ if (!publicKey) {
1625
+ throw new Error("Failed to create public key for OAuth.");
1626
+ }
1627
+ const nonce = utils$1.bytesToHex(sha2.sha256(publicKey));
1628
+ // Generate PKCE challenge pair
1629
+ const {
1630
+ verifier,
1631
+ codeChallenge
1632
+ } = await utils.generateChallengePair();
1633
+ sessionStorage.setItem("twitter_verifier", verifier);
1634
+ // Construct Twitter Auth URL
1635
+ const twitterAuthUrl = new URL(utils.X_AUTH_URL);
1636
+ twitterAuthUrl.searchParams.set("client_id", clientId);
1637
+ twitterAuthUrl.searchParams.set("redirect_uri", redirectURI);
1638
+ twitterAuthUrl.searchParams.set("response_type", "code");
1639
+ twitterAuthUrl.searchParams.set("code_challenge", codeChallenge);
1640
+ twitterAuthUrl.searchParams.set("code_challenge_method", "S256");
1641
+ twitterAuthUrl.searchParams.set("scope", "tweet.read users.read");
1642
+ twitterAuthUrl.searchParams.set("state", `provider=twitter&flow=${flow}&publicKey=${encodeURIComponent(publicKey)}&nonce=${nonce}`);
1643
+ if (additionalParameters) {
1644
+ const extra = Object.entries(additionalParameters).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
1645
+ if (extra) {
1646
+ twitterAuthUrl.searchParams.set("state", twitterAuthUrl.searchParams.get("state") + `&${extra}`);
1647
+ }
1648
+ }
1649
+ if (openInPage) {
1650
+ window.location.href = twitterAuthUrl.toString();
1651
+ return new Promise((_, reject) => {
1652
+ const timeout = setTimeout(() => {
1653
+ reject(new Error("Authentication timed out."));
1654
+ }, 300000);
1655
+ window.addEventListener("beforeunload", () => clearTimeout(timeout));
1656
+ });
1657
+ } else {
1658
+ const width = utils.popupWidth;
1659
+ const height = utils.popupHeight;
1660
+ const left = window.screenX + (window.innerWidth - width) / 2;
1661
+ const top = window.screenY + (window.innerHeight - height) / 2;
1662
+ const authWindow = window.open("about:blank", "_blank", `width=${width},height=${height},top=${top},left=${left},scrollbars=yes,resizable=yes`);
1663
+ if (!authWindow) {
1664
+ throw new Error("Failed to open Twitter login window.");
1665
+ }
1666
+ authWindow.location.href = twitterAuthUrl.toString();
1667
+ return new Promise((resolve, reject) => {
1668
+ const interval = setInterval(() => {
1669
+ try {
1670
+ if (authWindow.closed) {
1671
+ clearInterval(interval);
1672
+ reject(new Error("Authentication window was closed."));
1673
+ return;
1674
+ }
1675
+ const url = authWindow.location.href || "";
1676
+ if (url.startsWith(window.location.origin)) {
1677
+ const urlParams = new URLSearchParams(new URL(url).search);
1678
+ const authCode = urlParams.get("code");
1679
+ const stateParam = urlParams.get("state");
1680
+ const sessionKey = stateParam?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1681
+ if (authCode && stateParam && stateParam.includes("provider=twitter")) {
1682
+ authWindow.close();
1683
+ clearInterval(interval);
1684
+ const verifier = sessionStorage.getItem("twitter_verifier");
1685
+ if (!verifier) {
1686
+ reject(new Error("Missing PKCE verifier"));
1687
+ return;
1688
+ }
1689
+ client?.httpClient.proxyOAuth2Authenticate({
1690
+ provider: "OAUTH2_PROVIDER_X",
1691
+ authCode,
1692
+ redirectUri: redirectURI,
1693
+ codeVerifier: verifier,
1694
+ clientId,
1695
+ nonce
1696
+ }).then(resp => {
1697
+ sessionStorage.removeItem("twitter_verifier");
1698
+ const oidcToken = resp.oidcToken;
1699
+ if (params?.onOauthSuccess) {
1700
+ params.onOauthSuccess({
1701
+ oidcToken,
1702
+ providerName: "twitter",
1703
+ ...(sessionKey && {
1704
+ sessionKey
1705
+ })
1706
+ });
1707
+ } else if (callbacks?.onOauthRedirect) {
1708
+ callbacks.onOauthRedirect({
1709
+ idToken: oidcToken,
1710
+ publicKey,
1711
+ ...(sessionKey && {
1712
+ sessionKey
1713
+ })
1714
+ });
1715
+ } else {
1716
+ completeOauth({
1717
+ oidcToken,
1718
+ publicKey,
1719
+ providerName: "twitter",
1720
+ ...(sessionKey && {
1721
+ sessionKey
1722
+ })
1723
+ }).then(() => resolve()).catch(reject);
1724
+ return;
1725
+ }
1726
+ resolve();
1727
+ }).catch(reject);
1728
+ }
1729
+ }
1730
+ } catch {
1731
+ // ignore cross-origin
1732
+ }
1733
+ }, 500);
1734
+ if (authWindow.closed) {
1735
+ clearInterval(interval);
1736
+ }
1737
+ });
1738
+ }
1739
+ } catch (error) {
1740
+ throw error;
1741
+ }
1742
+ }, [client, callbacks, masterConfig, session, user]);
1232
1743
  const handleGoogleOauth = react.useCallback(async params => {
1233
1744
  const {
1234
1745
  clientId = masterConfig?.auth?.oauthConfig?.googleClientId,
@@ -1309,24 +1820,35 @@ const ClientProvider = ({
1309
1820
  if (url.startsWith(window.location.origin)) {
1310
1821
  const hashParams = new URLSearchParams(url.split("#")[1]);
1311
1822
  const idToken = hashParams.get("id_token");
1823
+ const stateParams = hashParams.get("state");
1824
+ const sessionKey = stateParams?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1312
1825
  if (idToken) {
1313
1826
  authWindow.close();
1314
1827
  clearInterval(interval);
1315
1828
  if (params?.onOauthSuccess) {
1316
1829
  params.onOauthSuccess({
1317
1830
  oidcToken: idToken,
1318
- providerName: "google"
1831
+ providerName: "google",
1832
+ ...(sessionKey && {
1833
+ sessionKey
1834
+ })
1319
1835
  });
1320
1836
  } else if (callbacks?.onOauthRedirect) {
1321
1837
  callbacks.onOauthRedirect({
1322
1838
  idToken,
1323
- publicKey
1839
+ publicKey,
1840
+ ...(sessionKey && {
1841
+ sessionKey
1842
+ })
1324
1843
  });
1325
1844
  } else {
1326
1845
  completeOauth({
1327
1846
  oidcToken: idToken,
1328
1847
  publicKey,
1329
- providerName: "google"
1848
+ providerName: "google",
1849
+ ...(sessionKey && {
1850
+ sessionKey
1851
+ })
1330
1852
  }).then(() => resolve()).catch(reject);
1331
1853
  return;
1332
1854
  }
@@ -1345,7 +1867,7 @@ const ClientProvider = ({
1345
1867
  } catch (error) {
1346
1868
  throw error;
1347
1869
  }
1348
- }, [client, callbacks]);
1870
+ }, [client, callbacks, masterConfig, session, user]);
1349
1871
  const handleAppleOauth = react.useCallback(async params => {
1350
1872
  const {
1351
1873
  clientId = masterConfig?.auth?.oauthConfig?.appleClientId,
@@ -1422,24 +1944,35 @@ const ClientProvider = ({
1422
1944
  if (url.startsWith(window.location.origin)) {
1423
1945
  const hashParams = new URLSearchParams(url.split("#")[1]);
1424
1946
  const idToken = hashParams.get("id_token");
1947
+ const stateParams = hashParams.get("state");
1948
+ const sessionKey = stateParams?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1425
1949
  if (idToken) {
1426
1950
  authWindow.close();
1427
1951
  clearInterval(interval);
1428
1952
  if (params?.onOauthSuccess) {
1429
1953
  params.onOauthSuccess({
1430
1954
  oidcToken: idToken,
1431
- providerName: "apple"
1955
+ providerName: "apple",
1956
+ ...(sessionKey && {
1957
+ sessionKey
1958
+ })
1432
1959
  });
1433
1960
  } else if (callbacks?.onOauthRedirect) {
1434
1961
  callbacks.onOauthRedirect({
1435
1962
  idToken,
1436
- publicKey
1963
+ publicKey,
1964
+ ...(sessionKey && {
1965
+ sessionKey
1966
+ })
1437
1967
  });
1438
1968
  } else {
1439
1969
  completeOauth({
1440
1970
  oidcToken: idToken,
1441
1971
  publicKey,
1442
- providerName: "apple"
1972
+ providerName: "apple",
1973
+ ...(sessionKey && {
1974
+ sessionKey
1975
+ })
1443
1976
  }).then(() => resolve()).catch(reject);
1444
1977
  return;
1445
1978
  }
@@ -1458,7 +1991,7 @@ const ClientProvider = ({
1458
1991
  } catch (error) {
1459
1992
  throw error;
1460
1993
  }
1461
- }, [client, callbacks]);
1994
+ }, [client, callbacks, masterConfig, session, user]);
1462
1995
  const handleFacebookOauth = react.useCallback(async params => {
1463
1996
  const {
1464
1997
  clientId = masterConfig?.auth?.oauthConfig?.facebookClientId,
@@ -1546,6 +2079,7 @@ const ClientProvider = ({
1546
2079
  const authCode = urlParams.get("code");
1547
2080
  const stateParam = urlParams.get("state");
1548
2081
  if (authCode && stateParam && stateParam.includes("provider=facebook")) {
2082
+ const sessionKey = stateParam?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1549
2083
  authWindow.close();
1550
2084
  clearInterval(interval);
1551
2085
  // Exchange code for token
@@ -1559,18 +2093,27 @@ const ClientProvider = ({
1559
2093
  if (params?.onOauthSuccess) {
1560
2094
  params.onOauthSuccess({
1561
2095
  oidcToken: tokenData.id_token,
1562
- providerName: "apple"
2096
+ providerName: "facebook",
2097
+ ...(sessionKey && {
2098
+ sessionKey
2099
+ })
1563
2100
  });
1564
2101
  } else if (callbacks?.onOauthRedirect) {
1565
2102
  callbacks.onOauthRedirect({
1566
2103
  idToken: tokenData.id_token,
1567
- publicKey
2104
+ publicKey,
2105
+ ...(sessionKey && {
2106
+ sessionKey
2107
+ })
1568
2108
  });
1569
2109
  } else {
1570
2110
  completeOauth({
1571
2111
  oidcToken: tokenData.id_token,
1572
2112
  publicKey,
1573
- providerName: "facebook"
2113
+ providerName: "facebook",
2114
+ ...(sessionKey && {
2115
+ sessionKey
2116
+ })
1574
2117
  }).then(() => resolve()).catch(reject);
1575
2118
  return;
1576
2119
  }
@@ -1590,13 +2133,15 @@ const ClientProvider = ({
1590
2133
  } catch (error) {
1591
2134
  throw error;
1592
2135
  }
1593
- }, [client, callbacks]);
1594
- const handleLogin = react.useCallback(async () => {
2136
+ }, [client, callbacks, masterConfig, client, session, user]);
2137
+ const handleLogin = react.useCallback(async params => {
1595
2138
  pushPage({
1596
2139
  key: "Log in or sign up",
1597
- content: jsxRuntime.jsx(index.AuthComponent, {})
2140
+ content: jsxRuntime.jsx(index.AuthComponent, {
2141
+ sessionKey: params?.sessionKey
2142
+ })
1598
2143
  });
1599
- }, [pushPage]);
2144
+ }, [pushPage, masterConfig, client, session, user]);
1600
2145
  const handleExportWallet = react.useCallback(async params => {
1601
2146
  const {
1602
2147
  walletId,
@@ -1604,7 +2149,7 @@ const ClientProvider = ({
1604
2149
  stampWith
1605
2150
  } = params;
1606
2151
  pushPage({
1607
- key: "Export Wallet",
2152
+ key: "Export wallet",
1608
2153
  content: jsxRuntime.jsx(Export.ExportComponent, {
1609
2154
  target: walletId,
1610
2155
  exportType: base.ExportType.Wallet,
@@ -1616,18 +2161,22 @@ const ClientProvider = ({
1616
2161
  } : {})
1617
2162
  })
1618
2163
  });
1619
- }, [pushPage]);
2164
+ }, [pushPage, masterConfig, client, session, user]);
1620
2165
  const handleExportPrivateKey = react.useCallback(async params => {
1621
2166
  const {
1622
2167
  privateKeyId,
1623
2168
  targetPublicKey,
2169
+ keyFormat,
1624
2170
  stampWith
1625
2171
  } = params;
1626
2172
  pushPage({
1627
- key: "Export Private Key",
2173
+ key: "Export private key",
1628
2174
  content: jsxRuntime.jsx(Export.ExportComponent, {
1629
2175
  target: privateKeyId,
1630
2176
  exportType: base.ExportType.PrivateKey,
2177
+ ...(keyFormat !== undefined ? {
2178
+ keyFormat
2179
+ } : {}),
1631
2180
  ...(targetPublicKey !== undefined ? {
1632
2181
  targetPublicKey
1633
2182
  } : {}),
@@ -1636,18 +2185,22 @@ const ClientProvider = ({
1636
2185
  } : {})
1637
2186
  })
1638
2187
  });
1639
- }, [pushPage]);
2188
+ }, [pushPage, masterConfig, client, session, user]);
1640
2189
  const handleExportWalletAccount = react.useCallback(async params => {
1641
2190
  const {
1642
2191
  address,
1643
2192
  targetPublicKey,
2193
+ keyFormat,
1644
2194
  stampWith
1645
2195
  } = params;
1646
2196
  pushPage({
1647
- key: "Export Wallet Account",
2197
+ key: "Export wallet account",
1648
2198
  content: jsxRuntime.jsx(Export.ExportComponent, {
1649
2199
  target: address,
1650
2200
  exportType: base.ExportType.WalletAccount,
2201
+ ...(keyFormat !== undefined ? {
2202
+ keyFormat
2203
+ } : {}),
1651
2204
  ...(targetPublicKey !== undefined ? {
1652
2205
  targetPublicKey
1653
2206
  } : {}),
@@ -1656,7 +2209,7 @@ const ClientProvider = ({
1656
2209
  } : {})
1657
2210
  })
1658
2211
  });
1659
- }, [pushPage]);
2212
+ }, [pushPage, masterConfig, client, session, user]);
1660
2213
  const handleImportWallet = react.useCallback(async params => {
1661
2214
  const {
1662
2215
  defaultWalletAccounts,
@@ -1665,8 +2218,9 @@ const ClientProvider = ({
1665
2218
  } = params || {};
1666
2219
  try {
1667
2220
  return utils.withTurnkeyErrorHandling(() => new Promise((resolve, reject) => pushPage({
1668
- key: "Import Wallet",
2221
+ key: "Import wallet",
1669
2222
  content: jsxRuntime.jsx(Import.ImportComponent, {
2223
+ importType: base.ImportType.Wallet,
1670
2224
  onError: error => {
1671
2225
  reject(error);
1672
2226
  },
@@ -1688,7 +2242,40 @@ const ClientProvider = ({
1688
2242
  }
1689
2243
  throw new sdkTypes.TurnkeyError("Failed to import wallet.", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR, error);
1690
2244
  }
1691
- }, [pushPage]);
2245
+ }, [pushPage, masterConfig, client, session, user]);
2246
+ const handleImportPrivateKey = react.useCallback(async params => {
2247
+ const {
2248
+ curve,
2249
+ addressFormats,
2250
+ successPageDuration = 2000,
2251
+ stampWith
2252
+ } = params || {};
2253
+ try {
2254
+ return utils.withTurnkeyErrorHandling(() => new Promise((resolve, reject) => pushPage({
2255
+ key: "Import private key",
2256
+ content: jsxRuntime.jsx(Import.ImportComponent, {
2257
+ importType: base.ImportType.PrivateKey,
2258
+ curve: curve,
2259
+ addressFormats: addressFormats,
2260
+ onError: error => {
2261
+ reject(error);
2262
+ },
2263
+ onSuccess: privateKeyId => resolve(privateKeyId),
2264
+ ...(successPageDuration !== undefined && {
2265
+ successPageDuration
2266
+ }),
2267
+ ...(stampWith !== undefined && {
2268
+ stampWith
2269
+ })
2270
+ })
2271
+ })));
2272
+ } catch (error) {
2273
+ if (error instanceof sdkTypes.TurnkeyError) {
2274
+ throw error;
2275
+ }
2276
+ throw new sdkTypes.TurnkeyError("Failed to import private key.", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR, error);
2277
+ }
2278
+ }, [pushPage, masterConfig, client, session, user]);
1692
2279
  const handleUpdateUserName = react.useCallback(async params => {
1693
2280
  const {
1694
2281
  successPageDuration = 2000,
@@ -1754,7 +2341,7 @@ const ClientProvider = ({
1754
2341
  }
1755
2342
  throw new sdkTypes.TurnkeyError("Failed to update user name.", sdkTypes.TurnkeyErrorCodes.UPDATE_USER_NAME_ERROR, error);
1756
2343
  }
1757
- }, [pushPage]);
2344
+ }, [pushPage, masterConfig, client, session, user]);
1758
2345
  const handleUpdateUserPhoneNumber = react.useCallback(async params => {
1759
2346
  const {
1760
2347
  successPageDuration = 2000,
@@ -1858,7 +2445,7 @@ const ClientProvider = ({
1858
2445
  }
1859
2446
  throw new sdkTypes.TurnkeyError("Failed to initialize OTP for sms verification.", sdkTypes.TurnkeyErrorCodes.INIT_OTP_ERROR, error);
1860
2447
  }
1861
- }, [pushPage]);
2448
+ }, [pushPage, masterConfig, client, session, user]);
1862
2449
  const handleUpdateUserEmail = react.useCallback(async params => {
1863
2450
  const {
1864
2451
  successPageDuration = 2000,
@@ -1955,14 +2542,16 @@ const ClientProvider = ({
1955
2542
  }
1956
2543
  throw new sdkTypes.TurnkeyError("Failed to initialize OTP for email verification.", sdkTypes.TurnkeyErrorCodes.INIT_OTP_ERROR, error);
1957
2544
  }
1958
- }, [pushPage]);
2545
+ }, [pushPage, masterConfig, client, session, user]);
1959
2546
  const handleAddEmail = react.useCallback(async params => {
1960
2547
  const {
1961
2548
  successPageDuration = 2000,
1962
2549
  subTitle,
1963
2550
  title
1964
2551
  } = params || {};
1965
- if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
2552
+ if (!client) {
2553
+ throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
2554
+ }
1966
2555
  if (!session) {
1967
2556
  throw new sdkTypes.TurnkeyError("No active session found.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
1968
2557
  }
@@ -2052,7 +2641,7 @@ const ClientProvider = ({
2052
2641
  }
2053
2642
  throw new sdkTypes.TurnkeyError("Failed to initialize OTP for email verification.", sdkTypes.TurnkeyErrorCodes.INIT_OTP_ERROR, error);
2054
2643
  }
2055
- }, [pushPage]);
2644
+ }, [pushPage, client, session, user]);
2056
2645
  const handleAddPhoneNumber = react.useCallback(async params => {
2057
2646
  const {
2058
2647
  successPageDuration = 2000,
@@ -2160,7 +2749,7 @@ const ClientProvider = ({
2160
2749
  }
2161
2750
  throw new sdkTypes.TurnkeyError("Failed to initialize OTP for sms verification.", sdkTypes.TurnkeyErrorCodes.INIT_OTP_ERROR, error);
2162
2751
  }
2163
- }, [pushPage]);
2752
+ }, [pushPage, masterConfig, client, session, user]);
2164
2753
  const handleRemovePasskey = react.useCallback(async params => {
2165
2754
  const {
2166
2755
  authenticatorId,
@@ -2201,7 +2790,7 @@ const ClientProvider = ({
2201
2790
  preventBack: true
2202
2791
  });
2203
2792
  }), callbacks, "Failed to remove passkey");
2204
- }, [pushPage]);
2793
+ }, [pushPage, masterConfig, client, session, user]);
2205
2794
  const handleAddPasskey = react.useCallback(async params => {
2206
2795
  const {
2207
2796
  name,
@@ -2246,7 +2835,7 @@ const ClientProvider = ({
2246
2835
  }
2247
2836
  throw new sdkTypes.TurnkeyError("Failed to add passkey in handler.", sdkTypes.TurnkeyErrorCodes.ADD_PASSKEY_ERROR, error);
2248
2837
  }
2249
- }, [pushPage]);
2838
+ }, [pushPage, masterConfig, client, session, user]);
2250
2839
  const handleRemoveOauthProvider = react.useCallback(async params => {
2251
2840
  const {
2252
2841
  providerId,
@@ -2290,7 +2879,7 @@ const ClientProvider = ({
2290
2879
  }
2291
2880
  throw new sdkTypes.TurnkeyError("Failed to remove OAuth provider in handler.", sdkTypes.TurnkeyErrorCodes.REMOVE_OAUTH_PROVIDER_ERROR, error);
2292
2881
  }
2293
- }, [pushPage]);
2882
+ }, [pushPage, masterConfig, client, session, user]);
2294
2883
  const handleAddOauthProvider = react.useCallback(async params => {
2295
2884
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
2296
2885
  if (!session) {
@@ -2320,6 +2909,22 @@ const ClientProvider = ({
2320
2909
  });
2321
2910
  };
2322
2911
  switch (providerName) {
2912
+ case sdkTypes.OAuthProviders.DISCORD:
2913
+ {
2914
+ await handleDiscordOauth({
2915
+ openInPage: false,
2916
+ onOauthSuccess
2917
+ });
2918
+ break;
2919
+ }
2920
+ case sdkTypes.OAuthProviders.X:
2921
+ {
2922
+ await handleXOauth({
2923
+ openInPage: false,
2924
+ onOauthSuccess
2925
+ });
2926
+ break;
2927
+ }
2323
2928
  case sdkTypes.OAuthProviders.GOOGLE:
2324
2929
  {
2325
2930
  await handleGoogleOauth({
@@ -2349,8 +2954,8 @@ const ClientProvider = ({
2349
2954
  throw new sdkTypes.TurnkeyError(`Unsupported OAuth provider: ${providerName}`, sdkTypes.TurnkeyErrorCodes.NOT_FOUND);
2350
2955
  }
2351
2956
  }
2352
- }, [pushPage]);
2353
- const handleLinkExternalWallet = react.useCallback(async params => {
2957
+ }, [pushPage, masterConfig, client, session, user]);
2958
+ const handleConnectExternalWallet = react.useCallback(async params => {
2354
2959
  const {
2355
2960
  successPageDuration = 2000
2356
2961
  } = params || {};
@@ -2361,15 +2966,15 @@ const ClientProvider = ({
2361
2966
  if (!masterConfig?.walletConfig?.features?.connecting) {
2362
2967
  throw new sdkTypes.TurnkeyError("Wallet connecting is not enabled.", sdkTypes.TurnkeyErrorCodes.FEATURE_NOT_ENABLED);
2363
2968
  }
2364
- const providers = await getWalletProviders();
2969
+ const providers = await fetchWalletProviders();
2365
2970
  pushPage({
2366
- key: "Link Wallet",
2367
- content: jsxRuntime.jsx(LinkWallet.LinkWalletModal, {
2971
+ key: "Connect wallet",
2972
+ content: jsxRuntime.jsx(ConnectWallet.ConnectWalletModal, {
2368
2973
  providers: providers,
2369
2974
  successPageDuration: successPageDuration
2370
2975
  })
2371
2976
  });
2372
- }, [pushPage]);
2977
+ }, [pushPage, masterConfig, client, session, user]);
2373
2978
  const handleRemoveUserEmail = react.useCallback(async params => {
2374
2979
  const {
2375
2980
  successPageDuration = 2000,
@@ -2408,7 +3013,7 @@ const ClientProvider = ({
2408
3013
  }
2409
3014
  throw new sdkTypes.TurnkeyError("Failed to remove user email.", sdkTypes.TurnkeyErrorCodes.UPDATE_USER_EMAIL_ERROR, error);
2410
3015
  }
2411
- }, [pushPage]);
3016
+ }, [pushPage, masterConfig, client, session, user]);
2412
3017
  const handleRemoveUserPhoneNumber = react.useCallback(async params => {
2413
3018
  const {
2414
3019
  successPageDuration = 2000,
@@ -2447,7 +3052,7 @@ const ClientProvider = ({
2447
3052
  }
2448
3053
  throw new sdkTypes.TurnkeyError("Failed to remove user phone number.", sdkTypes.TurnkeyErrorCodes.UPDATE_USER_PHONE_NUMBER_ERROR, error);
2449
3054
  }
2450
- }, [pushPage]);
3055
+ }, [pushPage, masterConfig, client, session, user]);
2451
3056
  react.useEffect(() => {
2452
3057
  if (proxyAuthConfigRef.current) return;
2453
3058
  // Only fetch the proxy auth config once. Use that to build the master config.
@@ -2546,7 +3151,7 @@ const ClientProvider = ({
2546
3151
  logout,
2547
3152
  loginWithPasskey,
2548
3153
  signUpWithPasskey,
2549
- getWalletProviders,
3154
+ fetchWalletProviders,
2550
3155
  connectWalletAccount,
2551
3156
  disconnectWalletAccount,
2552
3157
  switchWalletAccountChain,
@@ -2563,11 +3168,14 @@ const ClientProvider = ({
2563
3168
  completeOauth,
2564
3169
  fetchWallets,
2565
3170
  fetchWalletAccounts,
3171
+ fetchPrivateKeys,
2566
3172
  refreshWallets,
2567
3173
  signMessage,
2568
3174
  signTransaction,
2569
3175
  signAndSendTransaction,
2570
3176
  fetchUser,
3177
+ fetchOrCreateP256ApiKeyUser,
3178
+ fetchOrCreatePolicies,
2571
3179
  refreshUser,
2572
3180
  updateUserEmail,
2573
3181
  removeUserEmail,
@@ -2599,12 +3207,15 @@ const ClientProvider = ({
2599
3207
  getProxyAuthConfig,
2600
3208
  handleLogin,
2601
3209
  handleGoogleOauth,
3210
+ handleXOauth,
3211
+ handleDiscordOauth,
2602
3212
  handleAppleOauth,
2603
3213
  handleFacebookOauth,
2604
3214
  handleExportWallet,
2605
3215
  handleExportPrivateKey,
2606
3216
  handleExportWalletAccount,
2607
3217
  handleImportWallet,
3218
+ handleImportPrivateKey,
2608
3219
  handleUpdateUserEmail,
2609
3220
  handleUpdateUserPhoneNumber,
2610
3221
  handleUpdateUserName,
@@ -2615,7 +3226,7 @@ const ClientProvider = ({
2615
3226
  handleAddEmail,
2616
3227
  handleAddPhoneNumber,
2617
3228
  handleSignMessage,
2618
- handleLinkExternalWallet,
3229
+ handleConnectExternalWallet,
2619
3230
  handleRemoveUserEmail,
2620
3231
  handleRemoveUserPhoneNumber
2621
3232
  },