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

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 (87) hide show
  1. package/dist/_virtual/_rollupPluginBabelHelpers.mjs +49 -1
  2. package/dist/_virtual/_rollupPluginBabelHelpers.mjs.map +1 -1
  3. package/dist/components/auth/OTP.d.ts +1 -0
  4. package/dist/components/auth/OTP.d.ts.map +1 -1
  5. package/dist/components/auth/OTP.js +5 -1
  6. package/dist/components/auth/OTP.js.map +1 -1
  7. package/dist/components/auth/OTP.mjs +6 -3
  8. package/dist/components/auth/OTP.mjs.map +1 -1
  9. package/dist/components/auth/Wallet.d.ts +4 -4
  10. package/dist/components/auth/Wallet.d.ts.map +1 -1
  11. package/dist/components/auth/Wallet.js +69 -48
  12. package/dist/components/auth/Wallet.js.map +1 -1
  13. package/dist/components/auth/Wallet.mjs +75 -55
  14. package/dist/components/auth/Wallet.mjs.map +1 -1
  15. package/dist/components/auth/index.d.ts +5 -1
  16. package/dist/components/auth/index.d.ts.map +1 -1
  17. package/dist/components/auth/index.js +98 -12
  18. package/dist/components/auth/index.js.map +1 -1
  19. package/dist/components/auth/index.mjs +185 -82
  20. package/dist/components/auth/index.mjs.map +1 -1
  21. package/dist/components/export/Export.d.ts +2 -1
  22. package/dist/components/export/Export.d.ts.map +1 -1
  23. package/dist/components/export/Export.js +7 -6
  24. package/dist/components/export/Export.js.map +1 -1
  25. package/dist/components/export/Export.mjs +7 -6
  26. package/dist/components/export/Export.mjs.map +1 -1
  27. package/dist/components/export/ExportWarning.d.ts +2 -1
  28. package/dist/components/export/ExportWarning.d.ts.map +1 -1
  29. package/dist/components/export/ExportWarning.js +3 -2
  30. package/dist/components/export/ExportWarning.js.map +1 -1
  31. package/dist/components/export/ExportWarning.mjs +3 -2
  32. package/dist/components/export/ExportWarning.mjs.map +1 -1
  33. package/dist/components/import/Import.d.ts +7 -3
  34. package/dist/components/import/Import.d.ts.map +1 -1
  35. package/dist/components/import/Import.js +74 -37
  36. package/dist/components/import/Import.js.map +1 -1
  37. package/dist/components/import/Import.mjs +116 -54
  38. package/dist/components/import/Import.mjs.map +1 -1
  39. package/dist/components/user/ConnectWallet.d.ts +8 -0
  40. package/dist/components/user/ConnectWallet.d.ts.map +1 -0
  41. package/dist/components/user/{LinkWallet.js → ConnectWallet.js} +18 -18
  42. package/dist/components/user/ConnectWallet.js.map +1 -0
  43. package/dist/components/user/{LinkWallet.mjs → ConnectWallet.mjs} +25 -25
  44. package/dist/components/user/ConnectWallet.mjs.map +1 -0
  45. package/dist/index.js +13 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/index.mjs +2 -1
  48. package/dist/index.mjs.map +1 -1
  49. package/dist/providers/client/Provider.d.ts +3 -3
  50. package/dist/providers/client/Provider.d.ts.map +1 -1
  51. package/dist/providers/client/Provider.js +684 -105
  52. package/dist/providers/client/Provider.js.map +1 -1
  53. package/dist/providers/client/Provider.mjs +2368 -1597
  54. package/dist/providers/client/Provider.mjs.map +1 -1
  55. package/dist/providers/client/Types.d.ts +116 -11
  56. package/dist/providers/client/Types.d.ts.map +1 -1
  57. package/dist/providers/client/Types.js.map +1 -1
  58. package/dist/providers/client/Types.mjs.map +1 -1
  59. package/dist/providers/modal/Provider.js +1 -1
  60. package/dist/providers/modal/Provider.mjs +1 -1
  61. package/dist/styles.css +1 -1
  62. package/dist/types/base.d.ts +28 -2
  63. package/dist/types/base.d.ts.map +1 -1
  64. package/dist/types/base.js +23 -0
  65. package/dist/types/base.js.map +1 -1
  66. package/dist/types/base.mjs +19 -1
  67. package/dist/types/base.mjs.map +1 -1
  68. package/dist/utils/timers.d.ts +73 -0
  69. package/dist/utils/timers.d.ts.map +1 -0
  70. package/dist/utils/timers.js +133 -0
  71. package/dist/utils/timers.js.map +1 -0
  72. package/dist/utils/timers.mjs +142 -0
  73. package/dist/utils/timers.mjs.map +1 -0
  74. package/dist/{utils.d.ts → utils/utils.d.ts} +8 -3
  75. package/dist/utils/utils.d.ts.map +1 -0
  76. package/dist/{utils.js → utils/utils.js} +12 -3
  77. package/dist/utils/utils.js.map +1 -0
  78. package/dist/{utils.mjs → utils/utils.mjs} +12 -5
  79. package/dist/utils/utils.mjs.map +1 -0
  80. package/package.json +3 -3
  81. package/dist/components/user/LinkWallet.d.ts +0 -8
  82. package/dist/components/user/LinkWallet.d.ts.map +0 -1
  83. package/dist/components/user/LinkWallet.js.map +0 -1
  84. package/dist/components/user/LinkWallet.mjs.map +0 -1
  85. package/dist/utils.d.ts.map +0 -1
  86. package/dist/utils.js.map +0 -1
  87. 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:
@@ -92,6 +93,7 @@ const ClientProvider = ({
92
93
  const flow = stateParams.get("flow");
93
94
  const publicKey = stateParams.get("publicKey");
94
95
  const openModal = stateParams.get("openModal");
96
+ const sessionKey = stateParams.get("sessionKey");
95
97
  if (provider === "facebook" && flow === "redirect" && publicKey) {
96
98
  // We have all the required parameters for a Facebook PKCE flow
97
99
  const clientId = masterConfig?.auth?.oauthConfig?.facebookClientId;
@@ -116,8 +118,13 @@ const ClientProvider = ({
116
118
  await completeOauth({
117
119
  oidcToken,
118
120
  publicKey,
119
- providerName: "facebook"
121
+ providerName: "facebook",
122
+ ...(sessionKey && {
123
+ sessionKey
124
+ })
120
125
  });
126
+ // Clean up the URL after processing
127
+ window.history.replaceState(null, document.title, window.location.pathname);
121
128
  resolve();
122
129
  } catch (err) {
123
130
  reject(err);
@@ -140,6 +147,114 @@ const ClientProvider = ({
140
147
  });
141
148
  }
142
149
  }
150
+ if (provider === "discord" && flow === "redirect" && publicKey) {
151
+ const clientId = masterConfig?.auth?.oauthConfig?.discordClientId;
152
+ const redirectURI = masterConfig?.auth?.oauthConfig?.oauthRedirectUri;
153
+ const verifier = sessionStorage.getItem("discord_verifier");
154
+ const nonce = stateParams.get("nonce");
155
+ const sessionKey = stateParams.get("sessionKey");
156
+ if (clientId && redirectURI && verifier && nonce) {
157
+ await new Promise((resolve, reject) => {
158
+ pushPage({
159
+ key: `Discord OAuth`,
160
+ content: jsxRuntime.jsx(Action.ActionPage, {
161
+ title: `Authenticating with Discord...`,
162
+ action: async () => {
163
+ try {
164
+ const resp = await client?.httpClient.proxyOAuth2Authenticate({
165
+ provider: "OAUTH2_PROVIDER_DISCORD",
166
+ authCode: code,
167
+ redirectUri: redirectURI,
168
+ codeVerifier: verifier,
169
+ nonce: nonce
170
+ });
171
+ sessionStorage.removeItem("discord_verifier");
172
+ const oidcToken = resp?.oidcToken;
173
+ if (!oidcToken) {
174
+ throw new sdkTypes.TurnkeyError("Missing OIDC token", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
175
+ }
176
+ await completeOauth({
177
+ oidcToken,
178
+ publicKey,
179
+ providerName: "discord",
180
+ ...(sessionKey && {
181
+ sessionKey
182
+ })
183
+ });
184
+ // Clean up the URL after processing
185
+ window.history.replaceState(null, document.title, window.location.pathname);
186
+ resolve(null);
187
+ } catch (err) {
188
+ reject(err);
189
+ if (callbacks?.onError) {
190
+ callbacks.onError(err instanceof sdkTypes.TurnkeyError ? err : new sdkTypes.TurnkeyError("Discord authentication failed", sdkTypes.TurnkeyErrorCodes.OAUTH_SIGNUP_ERROR, err));
191
+ }
192
+ }
193
+ },
194
+ icon: jsxRuntime.jsx(reactFontawesome.FontAwesomeIcon, {
195
+ size: "3x",
196
+ icon: freeBrandsSvgIcons.faDiscord
197
+ })
198
+ }),
199
+ showTitle: false
200
+ });
201
+ });
202
+ }
203
+ }
204
+ if (provider === "twitter" && flow === "redirect" && publicKey) {
205
+ const clientId = masterConfig?.auth?.oauthConfig?.xClientId;
206
+ const redirectURI = masterConfig?.auth?.oauthConfig?.oauthRedirectUri;
207
+ const verifier = sessionStorage.getItem("twitter_verifier");
208
+ const nonce = stateParams.get("nonce");
209
+ const sessionKey = stateParams.get("sessionKey");
210
+ if (clientId && redirectURI && verifier && nonce) {
211
+ await new Promise((resolve, reject) => {
212
+ pushPage({
213
+ key: `Twitter OAuth`,
214
+ content: jsxRuntime.jsx(Action.ActionPage, {
215
+ title: `Authenticating with Twitter...`,
216
+ action: async () => {
217
+ try {
218
+ const resp = await client?.httpClient.proxyOAuth2Authenticate({
219
+ provider: "OAUTH2_PROVIDER_X",
220
+ authCode: code,
221
+ redirectUri: redirectURI,
222
+ codeVerifier: verifier,
223
+ nonce: nonce
224
+ });
225
+ sessionStorage.removeItem("twitter_verifier");
226
+ const oidcToken = resp?.oidcToken;
227
+ if (!oidcToken) {
228
+ throw new sdkTypes.TurnkeyError("Missing OIDC token", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
229
+ }
230
+ await completeOauth({
231
+ oidcToken,
232
+ publicKey,
233
+ providerName: "twitter",
234
+ ...(sessionKey && {
235
+ sessionKey
236
+ })
237
+ });
238
+ // Clean up the URL after processing
239
+ window.history.replaceState(null, document.title, window.location.pathname);
240
+ resolve(null);
241
+ } catch (err) {
242
+ reject(err);
243
+ if (callbacks?.onError) {
244
+ callbacks.onError(err instanceof sdkTypes.TurnkeyError ? err : new sdkTypes.TurnkeyError("Twitter authentication failed", sdkTypes.TurnkeyErrorCodes.OAUTH_SIGNUP_ERROR, err));
245
+ }
246
+ }
247
+ },
248
+ icon: jsxRuntime.jsx(reactFontawesome.FontAwesomeIcon, {
249
+ size: "3x",
250
+ icon: freeBrandsSvgIcons.faXTwitter
251
+ })
252
+ }),
253
+ showTitle: false
254
+ });
255
+ });
256
+ }
257
+ }
143
258
  }
144
259
  }
145
260
  // Handle Google/Apple redirects (uses hash with id_token)
@@ -151,7 +266,8 @@ const ClientProvider = ({
151
266
  provider,
152
267
  flow,
153
268
  publicKey,
154
- openModal
269
+ openModal,
270
+ sessionKey
155
271
  } = utils.parseOAuthRedirect(hash);
156
272
  if (idToken && flow === "redirect" && publicKey) {
157
273
  if (openModal === "true") {
@@ -183,7 +299,10 @@ const ClientProvider = ({
183
299
  publicKey,
184
300
  ...(provider ? {
185
301
  providerName: provider
186
- } : {})
302
+ } : {}),
303
+ ...(sessionKey && {
304
+ sessionKey
305
+ })
187
306
  });
188
307
  resolve(null);
189
308
  } catch (err) {
@@ -223,11 +342,13 @@ const ClientProvider = ({
223
342
  passkeyAuthEnabled: config.auth?.methods?.passkeyAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("passkey"),
224
343
  walletAuthEnabled: config.auth?.methods?.walletAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("wallet"),
225
344
  googleOauthEnabled: config.auth?.methods?.googleOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("google"),
345
+ xOauthEnabled: config.auth?.methods?.xOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("x"),
346
+ discordOauthEnabled: config.auth?.methods?.discordOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("discord"),
226
347
  appleOauthEnabled: config.auth?.methods?.appleOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("apple"),
227
348
  facebookOauthEnabled: config.auth?.methods?.facebookOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("facebook")
228
349
  };
229
350
  // Set a default ordering for the oAuth methods
230
- const oauthOrder = config.auth?.oauthOrder ?? ["google", "apple", "facebook"].filter(provider => resolvedMethods[`${provider}OauthEnabled`]);
351
+ const oauthOrder = config.auth?.oauthOrder ?? ["google", "apple", "x", "discord", "facebook"].filter(provider => resolvedMethods[`${provider}OauthEnabled`]);
231
352
  // Set a default ordering for the overall auth methods
232
353
  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
354
  return {
@@ -442,14 +563,13 @@ const ClientProvider = ({
442
563
  }
443
564
  /**
444
565
  * @internal
445
- * Schedules a session expiration and warning timeout for the given session key.
566
+ * Schedules a session expiration and warning timer for the given session key.
446
567
  *
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.
568
+ * - Sets up two timers: one for warning before expiry and one for actual expiry.
569
+ * - Uses capped timeouts under the hood so delays > 24.8 days are safe (see utils/timers.ts).
450
570
  *
451
571
  * @param params.sessionKey - The key of the session to schedule expiration for.
452
- * @param params.expiry - The expiration time in seconds for the session.
572
+ * @param params.expiry - The expiration time in seconds since epoch.
453
573
  * @throws {TurnkeyError} If an error occurs while scheduling the session expiration.
454
574
  */
455
575
  async function scheduleSessionExpiration(params) {
@@ -458,21 +578,18 @@ const ClientProvider = ({
458
578
  expiry
459
579
  } = params;
460
580
  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();
581
+ const warnKey = `${sessionKey}-warning`;
582
+ // Replace any prior timers for this session
583
+ timers.clearKey(expiryTimeoutsRef.current, sessionKey);
584
+ timers.clearKey(expiryTimeoutsRef.current, warnKey);
585
+ const now = Date.now();
586
+ const expiryMs = expiry * 1000;
587
+ const timeUntilExpiry = expiryMs - now;
471
588
  const beforeExpiry = async () => {
472
589
  const activeSession = await getSession();
473
- if (!activeSession && expiryTimeoutsRef.current[sessionKey]) {
474
- clearTimeout(expiryTimeoutsRef.current[`${sessionKey}-warning`]);
475
- expiryTimeoutsRef.current[`${sessionKey}-warning`] = setTimeout(beforeExpiry, 10000);
590
+ if (!activeSession && expiryTimeoutsRef.current[warnKey]) {
591
+ // Keep nudging until session materializes (short 10s timer is fine)
592
+ timers.setTimeoutInMap(expiryTimeoutsRef.current, warnKey, beforeExpiry, 10_000);
476
593
  return;
477
594
  }
478
595
  const session = await getSession({
@@ -500,27 +617,36 @@ const ClientProvider = ({
500
617
  if ((await getActiveSessionKey()) === sessionKey) {
501
618
  setSession(undefined);
502
619
  }
503
- setAllSessions(prevSessions => {
504
- if (!prevSessions) return prevSessions;
505
- const newSessions = {
506
- ...prevSessions
620
+ setAllSessions(prev => {
621
+ if (!prev) return prev;
622
+ const next = {
623
+ ...prev
507
624
  };
508
- delete newSessions[sessionKey];
509
- return newSessions;
625
+ delete next[sessionKey];
626
+ return next;
510
627
  });
511
628
  await clearSession({
512
629
  sessionKey
513
630
  });
514
- delete expiryTimeoutsRef.current[sessionKey];
515
- delete expiryTimeoutsRef.current[`${sessionKey}-warning`];
631
+ // Remove timers for this session
632
+ timers.clearKey(expiryTimeoutsRef.current, sessionKey);
633
+ timers.clearKey(expiryTimeoutsRef.current, warnKey);
516
634
  await logout();
517
635
  };
518
- if (timeUntilExpiry <= utils.SESSION_WARNING_THRESHOLD_MS) {
519
- beforeExpiry();
636
+ // Already expired → expire immediately
637
+ if (timeUntilExpiry <= 0) {
638
+ await expireSession();
639
+ return;
640
+ }
641
+ // Warning timer (if threshold is in the future)
642
+ const warnAt = expiryMs - utils.SESSION_WARNING_THRESHOLD_MS;
643
+ if (warnAt <= now) {
644
+ void beforeExpiry(); // fire-and-forget is fine
520
645
  } else {
521
- expiryTimeoutsRef.current[`${sessionKey}-warning`] = setTimeout(beforeExpiry, timeUntilExpiry - utils.SESSION_WARNING_THRESHOLD_MS);
646
+ timers.setCappedTimeoutInMap(expiryTimeoutsRef.current, warnKey, beforeExpiry, warnAt - now);
522
647
  }
523
- expiryTimeoutsRef.current[sessionKey] = setTimeout(expireSession, timeUntilExpiry);
648
+ // Actual expiry timer (safe for long delays)
649
+ timers.setCappedTimeoutInMap(expiryTimeoutsRef.current, sessionKey, expireSession, timeUntilExpiry);
524
650
  } catch (error) {
525
651
  if (error instanceof sdkTypes.TurnkeyError || error instanceof sdkTypes.TurnkeyNetworkError) {
526
652
  callbacks?.onError?.(error);
@@ -530,25 +656,25 @@ const ClientProvider = ({
530
656
  }
531
657
  }
532
658
  /**
533
- * Clears all scheduled session expiration and warning timeouts for the client.
659
+ * Clears all scheduled session timers (warning + expiry).
534
660
  *
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.
661
+ * - Removes all active timers managed by this client.
662
+ * - Useful on re-init or logout to avoid stale timers.
538
663
  *
539
- * @throws {TurnkeyError} If an error occurs while clearing the timeouts.
664
+ * @throws {TurnkeyError} If an error occurs while clearing the timers.
540
665
  */
541
- function clearSessionTimeouts() {
666
+ function clearSessionTimeouts(sessionKeys) {
542
667
  try {
543
- Object.values(expiryTimeoutsRef.current).forEach(timeout => {
544
- clearTimeout(timeout);
545
- });
546
- expiryTimeoutsRef.current = {};
668
+ if (sessionKeys) {
669
+ timers.clearKeys(expiryTimeoutsRef.current, sessionKeys);
670
+ } else {
671
+ timers.clearAll(expiryTimeoutsRef.current); // clears & deletes everything
672
+ }
547
673
  } catch (error) {
548
674
  if (error instanceof sdkTypes.TurnkeyError || error instanceof sdkTypes.TurnkeyNetworkError) {
549
675
  callbacks?.onError?.(error);
550
676
  } else {
551
- callbacks?.onError?.(new sdkTypes.TurnkeyError(`Failed to clear session timeouts`, sdkTypes.TurnkeyErrorCodes.CLEAR_SESSION_TIMEOUTS_ERROR, error));
677
+ callbacks?.onError?.(new sdkTypes.TurnkeyError("Failed to clear session timeouts", sdkTypes.TurnkeyErrorCodes.CLEAR_SESSION_TIMEOUTS_ERROR, error));
552
678
  }
553
679
  }
554
680
  }
@@ -564,7 +690,12 @@ const ClientProvider = ({
564
690
  * @returns A void promise.
565
691
  * @throws {TurnkeyError} If the client is not initialized or if there is an error during the process.
566
692
  */
567
- const handlePostAuth = async () => {
693
+ const handlePostAuth = async params => {
694
+ const {
695
+ method,
696
+ action,
697
+ identifier
698
+ } = params;
568
699
  try {
569
700
  const sessionKey = await getActiveSessionKey();
570
701
  const session = await getSession({
@@ -581,6 +712,12 @@ const ClientProvider = ({
581
712
  setAllSessions(allSessions);
582
713
  await refreshWallets();
583
714
  await refreshUser();
715
+ callbacks?.onAuthenticationSuccess?.({
716
+ session,
717
+ method,
718
+ action,
719
+ identifier
720
+ });
584
721
  } catch (error) {
585
722
  if (error instanceof sdkTypes.TurnkeyError || error instanceof sdkTypes.TurnkeyNetworkError) {
586
723
  callbacks?.onError?.(error);
@@ -594,19 +731,29 @@ const ClientProvider = ({
594
731
  * Handles the post-logout flow.
595
732
  *
596
733
  * - 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.
734
+ * - It clears all scheduled session expiration and warning timeouts associated to the session key to prevent memory leaks.
735
+ * - It resets the session state, removes user data from memory, the logged out session from all sessions state, and clears the wallets list.
599
736
  * - This ensures that all sensitive information is removed from the provider state after logout.
600
737
  * - Called internally after logout or when all sessions are cleared.
601
738
  *
602
739
  * @returns void
603
740
  * @throws {TurnkeyError} If there is an error during the post-logout process.
604
741
  */
605
- const handlePostLogout = () => {
742
+ const handlePostLogout = sessionKey => {
606
743
  try {
607
- clearSessionTimeouts();
744
+ clearSessionTimeouts(sessionKey ? [sessionKey, `${sessionKey}-warning`] : undefined);
745
+ setAllSessions(prev => {
746
+ if (!prev) return prev;
747
+ if (sessionKey) {
748
+ const next = {
749
+ ...prev
750
+ };
751
+ delete next[sessionKey];
752
+ return next;
753
+ }
754
+ return {};
755
+ });
608
756
  setSession(undefined);
609
- setAllSessions(undefined);
610
757
  setUser(undefined);
611
758
  setWallets([]);
612
759
  } catch (error) {
@@ -625,8 +772,14 @@ const ClientProvider = ({
625
772
  if (!client) {
626
773
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
627
774
  }
628
- await utils.withTurnkeyErrorHandling(() => client.logout(params), callbacks, "Failed to logout");
629
- handlePostLogout();
775
+ await utils.withTurnkeyErrorHandling(async () => {
776
+ // If no sessionKey is provided, we try to get the active one.
777
+ let sessionKey = params?.sessionKey;
778
+ if (!sessionKey) sessionKey = await getActiveSessionKey();
779
+ await client.logout(params);
780
+ // We only handle post logout if the sessionKey is defined since that means we actually logged out of a session.
781
+ if (sessionKey) handlePostLogout(sessionKey);
782
+ }, callbacks, "Failed to logout");
630
783
  return;
631
784
  }, [client, callbacks]);
632
785
  const loginWithPasskey = react.useCallback(async params => {
@@ -639,7 +792,11 @@ const ClientProvider = ({
639
792
  expirationSeconds
640
793
  }), callbacks, "Failed to login with passkey");
641
794
  if (res) {
642
- await handlePostAuth();
795
+ await handlePostAuth({
796
+ method: base.AuthMethod.Passkey,
797
+ action: sdkTypes.AuthAction.LOGIN,
798
+ identifier: res.credentialId
799
+ });
643
800
  }
644
801
  return res;
645
802
  }, [client, callbacks]);
@@ -674,7 +831,11 @@ const ClientProvider = ({
674
831
  expirationSeconds
675
832
  }), callbacks, "Failed to sign up with passkey");
676
833
  if (res) {
677
- await handlePostAuth();
834
+ await handlePostAuth({
835
+ method: base.AuthMethod.Passkey,
836
+ action: sdkTypes.AuthAction.SIGNUP,
837
+ identifier: res.credentialId
838
+ });
678
839
  }
679
840
  return res;
680
841
  }, [client, callbacks]);
@@ -705,7 +866,7 @@ const ClientProvider = ({
705
866
  await client.disconnectWalletAccount(walletProvider);
706
867
  // we only refresh the wallets if:
707
868
  // 1. there is an active session. This is needed because for WalletConnect
708
- // you can unlink a wallet before actually being logged in
869
+ // you can disconnect a wallet before actually being logged in
709
870
  //
710
871
  // 2. it was a WalletConnect provider that we just disconnected. Since
711
872
  // native providers emit a disconnect event which will already refresh
@@ -734,7 +895,11 @@ const ClientProvider = ({
734
895
  expirationSeconds
735
896
  }), callbacks, "Failed to login with wallet");
736
897
  if (res) {
737
- await handlePostAuth();
898
+ await handlePostAuth({
899
+ method: base.AuthMethod.Wallet,
900
+ action: sdkTypes.AuthAction.LOGIN,
901
+ identifier: res.address
902
+ });
738
903
  }
739
904
  return res;
740
905
  }, [client, callbacks]);
@@ -759,7 +924,11 @@ const ClientProvider = ({
759
924
  expirationSeconds
760
925
  }), callbacks, "Failed to sign up with wallet");
761
926
  if (res) {
762
- await handlePostAuth();
927
+ await handlePostAuth({
928
+ method: base.AuthMethod.Wallet,
929
+ action: sdkTypes.AuthAction.SIGNUP,
930
+ identifier: res.address
931
+ });
763
932
  }
764
933
  return res;
765
934
  }, [client, callbacks, masterConfig]);
@@ -784,7 +953,11 @@ const ClientProvider = ({
784
953
  expirationSeconds
785
954
  }), callbacks, "Failed to login or sign up with wallet");
786
955
  if (res) {
787
- await handlePostAuth();
956
+ await handlePostAuth({
957
+ method: base.AuthMethod.Wallet,
958
+ action: res.action,
959
+ identifier: res.address
960
+ });
788
961
  }
789
962
  return res;
790
963
  }, [client, callbacks, masterConfig]);
@@ -806,7 +979,11 @@ const ClientProvider = ({
806
979
  }
807
980
  const res = await utils.withTurnkeyErrorHandling(() => client.loginWithOtp(params), callbacks, "Failed to login with OTP");
808
981
  if (res) {
809
- await handlePostAuth();
982
+ await handlePostAuth({
983
+ method: base.AuthMethod.Otp,
984
+ action: sdkTypes.AuthAction.LOGIN,
985
+ identifier: params.verificationToken
986
+ });
810
987
  }
811
988
  return res;
812
989
  }, [client, callbacks]);
@@ -834,7 +1011,11 @@ const ClientProvider = ({
834
1011
  };
835
1012
  const res = await utils.withTurnkeyErrorHandling(() => client.signUpWithOtp(params), callbacks, "Failed to sign up with OTP");
836
1013
  if (res) {
837
- await handlePostAuth();
1014
+ await handlePostAuth({
1015
+ method: base.AuthMethod.Otp,
1016
+ action: sdkTypes.AuthAction.SIGNUP,
1017
+ identifier: params.verificationToken
1018
+ });
838
1019
  }
839
1020
  return res;
840
1021
  }, [client, callbacks, masterConfig]);
@@ -862,11 +1043,29 @@ const ClientProvider = ({
862
1043
  };
863
1044
  const res = await utils.withTurnkeyErrorHandling(() => client.completeOtp(params), callbacks, "Failed to complete OTP");
864
1045
  if (res) {
865
- await handlePostAuth();
1046
+ await handlePostAuth({
1047
+ method: base.AuthMethod.Otp,
1048
+ action: res.action,
1049
+ identifier: res.verificationToken
1050
+ });
866
1051
  }
867
1052
  return res;
868
1053
  }, [client, callbacks, masterConfig]);
869
- const completeOauth = react.useCallback(async params => {
1054
+ const loginWithOauth = react.useCallback(async params => {
1055
+ if (!client) {
1056
+ throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1057
+ }
1058
+ const res = await utils.withTurnkeyErrorHandling(() => client.loginWithOauth(params), callbacks, "Failed to login with OAuth");
1059
+ if (res) {
1060
+ await handlePostAuth({
1061
+ method: base.AuthMethod.Oauth,
1062
+ action: sdkTypes.AuthAction.LOGIN,
1063
+ identifier: params.oidcToken
1064
+ });
1065
+ }
1066
+ return res;
1067
+ }, [client, callbacks]);
1068
+ const signUpWithOauth = react.useCallback(async params => {
870
1069
  if (!client) {
871
1070
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
872
1071
  }
@@ -874,30 +1073,24 @@ const ClientProvider = ({
874
1073
  throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
875
1074
  }
876
1075
  // If createSubOrgParams is not provided, use the default from masterConfig
877
- const createSubOrgParams = params.createSubOrgParams ?? masterConfig.auth?.createSuborgParams?.oauth;
1076
+ let createSubOrgParams = params.createSubOrgParams ?? masterConfig.auth?.createSuborgParams?.oauth;
878
1077
  params = createSubOrgParams !== undefined ? {
879
1078
  ...params,
880
1079
  createSubOrgParams
881
1080
  } : {
882
1081
  ...params
883
1082
  };
884
- const res = await utils.withTurnkeyErrorHandling(() => client.completeOauth(params), callbacks, "Failed to complete OAuth");
1083
+ const res = await utils.withTurnkeyErrorHandling(() => client.signUpWithOauth(params), callbacks, "Failed to sign up with OAuth");
885
1084
  if (res) {
886
- await handlePostAuth();
1085
+ await handlePostAuth({
1086
+ method: base.AuthMethod.Oauth,
1087
+ action: sdkTypes.AuthAction.SIGNUP,
1088
+ identifier: params.oidcToken
1089
+ });
887
1090
  }
888
1091
  return res;
889
1092
  }, [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 => {
1093
+ const completeOauth = react.useCallback(async params => {
901
1094
  if (!client) {
902
1095
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
903
1096
  }
@@ -905,16 +1098,20 @@ const ClientProvider = ({
905
1098
  throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
906
1099
  }
907
1100
  // If createSubOrgParams is not provided, use the default from masterConfig
908
- let createSubOrgParams = params.createSubOrgParams ?? masterConfig.auth?.createSuborgParams?.oauth;
1101
+ const createSubOrgParams = params.createSubOrgParams ?? masterConfig.auth?.createSuborgParams?.oauth;
909
1102
  params = createSubOrgParams !== undefined ? {
910
1103
  ...params,
911
1104
  createSubOrgParams
912
1105
  } : {
913
1106
  ...params
914
1107
  };
915
- const res = await utils.withTurnkeyErrorHandling(() => client.signUpWithOauth(params), callbacks, "Failed to sign up with OAuth");
1108
+ const res = await utils.withTurnkeyErrorHandling(() => client.completeOauth(params), callbacks, "Failed to complete OAuth");
916
1109
  if (res) {
917
- await handlePostAuth();
1110
+ await handlePostAuth({
1111
+ method: base.AuthMethod.Oauth,
1112
+ action: res.action,
1113
+ identifier: params.oidcToken
1114
+ });
918
1115
  }
919
1116
  return res;
920
1117
  }, [client, callbacks, masterConfig]);
@@ -930,6 +1127,12 @@ const ClientProvider = ({
930
1127
  }
931
1128
  return utils.withTurnkeyErrorHandling(() => client.fetchWalletAccounts(params), callbacks, "Failed to fetch wallet accounts");
932
1129
  }, [client, callbacks]);
1130
+ const fetchPrivateKeys = react.useCallback(async params => {
1131
+ if (!client) {
1132
+ throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1133
+ }
1134
+ return utils.withTurnkeyErrorHandling(() => client.fetchPrivateKeys(params), callbacks, "Failed to fetch private keys");
1135
+ }, [client, callbacks]);
933
1136
  const signMessage = react.useCallback(async params => {
934
1137
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
935
1138
  return utils.withTurnkeyErrorHandling(() => client.signMessage(params), callbacks, "Failed to sign message");
@@ -941,7 +1144,7 @@ const ClientProvider = ({
941
1144
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
942
1145
  return new Promise((resolve, reject) => {
943
1146
  pushPage({
944
- key: "Sign Message",
1147
+ key: "Sign message",
945
1148
  content: jsxRuntime.jsx(Message.SignMessageModal, {
946
1149
  message: params.message,
947
1150
  subText: params?.subText,
@@ -1185,6 +1388,10 @@ const ClientProvider = ({
1185
1388
  }
1186
1389
  await utils.withTurnkeyErrorHandling(() => client.setActiveSession(params), callbacks, "Failed to set active session");
1187
1390
  setSession(session);
1391
+ await utils.withTurnkeyErrorHandling(async () => {
1392
+ await refreshWallets();
1393
+ await refreshUser();
1394
+ }, callbacks, "Failed to refresh data after setting active session");
1188
1395
  return;
1189
1396
  }, [client, callbacks]);
1190
1397
  const getActiveSessionKey = react.useCallback(async () => {
@@ -1229,6 +1436,282 @@ const ClientProvider = ({
1229
1436
  setWallets(wallets);
1230
1437
  }
1231
1438
  }, [client, callbacks, getWalletProviders, fetchWallets]);
1439
+ const handleDiscordOauth = react.useCallback(async params => {
1440
+ const {
1441
+ clientId = masterConfig?.auth?.oauthConfig?.discordClientId,
1442
+ openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
1443
+ additionalState: additionalParameters
1444
+ } = params || {};
1445
+ try {
1446
+ if (!masterConfig) {
1447
+ throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1448
+ }
1449
+ if (!clientId) {
1450
+ throw new sdkTypes.TurnkeyError("Discord Client ID is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1451
+ }
1452
+ if (!masterConfig.auth?.oauthConfig?.oauthRedirectUri) {
1453
+ throw new sdkTypes.TurnkeyError("OAuth Redirect URI is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1454
+ }
1455
+ const flow = openInPage ? "redirect" : "popup";
1456
+ const redirectURI = masterConfig.auth?.oauthConfig.oauthRedirectUri;
1457
+ // Create key pair and generate nonce
1458
+ const publicKey = await createApiKeyPair();
1459
+ if (!publicKey) {
1460
+ throw new Error("Failed to create public key for OAuth.");
1461
+ }
1462
+ const nonce = utils$1.bytesToHex(sha2.sha256(publicKey));
1463
+ // Generate PKCE challenge pair
1464
+ const {
1465
+ verifier,
1466
+ codeChallenge
1467
+ } = await utils.generateChallengePair();
1468
+ sessionStorage.setItem("discord_verifier", verifier);
1469
+ // Construct Discord Auth URL
1470
+ const discordAuthUrl = new URL(utils.DISCORD_AUTH_URL);
1471
+ discordAuthUrl.searchParams.set("client_id", clientId);
1472
+ discordAuthUrl.searchParams.set("redirect_uri", redirectURI);
1473
+ discordAuthUrl.searchParams.set("response_type", "code");
1474
+ discordAuthUrl.searchParams.set("code_challenge", codeChallenge);
1475
+ discordAuthUrl.searchParams.set("code_challenge_method", "S256");
1476
+ discordAuthUrl.searchParams.set("scope", "identify email");
1477
+ discordAuthUrl.searchParams.set("state", `provider=discord&flow=${flow}&publicKey=${encodeURIComponent(publicKey)}&nonce=${nonce}`);
1478
+ if (additionalParameters) {
1479
+ const extra = Object.entries(additionalParameters).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
1480
+ if (extra) {
1481
+ discordAuthUrl.searchParams.set("state", discordAuthUrl.searchParams.get("state") + `&${extra}`);
1482
+ }
1483
+ }
1484
+ if (openInPage) {
1485
+ window.location.href = discordAuthUrl.toString();
1486
+ return new Promise((_, reject) => {
1487
+ const timeout = setTimeout(() => {
1488
+ reject(new Error("Authentication timed out."));
1489
+ }, 300000);
1490
+ window.addEventListener("beforeunload", () => clearTimeout(timeout));
1491
+ });
1492
+ } else {
1493
+ const width = utils.popupWidth;
1494
+ const height = utils.popupHeight;
1495
+ const left = window.screenX + (window.innerWidth - width) / 2;
1496
+ const top = window.screenY + (window.innerHeight - height) / 2;
1497
+ const authWindow = window.open("about:blank", "_blank", `width=${width},height=${height},top=${top},left=${left},scrollbars=yes,resizable=yes`);
1498
+ if (!authWindow) {
1499
+ throw new Error("Failed to open Discord login window.");
1500
+ }
1501
+ authWindow.location.href = discordAuthUrl.toString();
1502
+ return new Promise((resolve, reject) => {
1503
+ const interval = setInterval(() => {
1504
+ try {
1505
+ if (authWindow.closed) {
1506
+ clearInterval(interval);
1507
+ reject(new Error("Authentication window was closed."));
1508
+ return;
1509
+ }
1510
+ const url = authWindow.location.href || "";
1511
+ if (url.startsWith(window.location.origin)) {
1512
+ const urlParams = new URLSearchParams(new URL(url).search);
1513
+ const authCode = urlParams.get("code");
1514
+ const stateParam = urlParams.get("state");
1515
+ const sessionKey = stateParam?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1516
+ if (authCode && stateParam && stateParam.includes("provider=discord")) {
1517
+ authWindow.close();
1518
+ clearInterval(interval);
1519
+ const verifier = sessionStorage.getItem("discord_verifier");
1520
+ if (!verifier) {
1521
+ reject(new Error("Missing PKCE verifier"));
1522
+ return;
1523
+ }
1524
+ client?.httpClient.proxyOAuth2Authenticate({
1525
+ provider: "OAUTH2_PROVIDER_DISCORD",
1526
+ authCode,
1527
+ redirectUri: redirectURI,
1528
+ codeVerifier: verifier,
1529
+ nonce: nonce
1530
+ }).then(resp => {
1531
+ sessionStorage.removeItem("discord_verifier");
1532
+ const oidcToken = resp.oidcToken;
1533
+ if (params?.onOauthSuccess) {
1534
+ params.onOauthSuccess({
1535
+ oidcToken,
1536
+ providerName: "discord",
1537
+ ...(sessionKey && {
1538
+ sessionKey
1539
+ })
1540
+ });
1541
+ } else if (callbacks?.onOauthRedirect) {
1542
+ callbacks.onOauthRedirect({
1543
+ idToken: oidcToken,
1544
+ publicKey,
1545
+ ...(sessionKey && {
1546
+ sessionKey
1547
+ })
1548
+ });
1549
+ } else {
1550
+ completeOauth({
1551
+ oidcToken,
1552
+ publicKey,
1553
+ providerName: "discord",
1554
+ ...(sessionKey && {
1555
+ sessionKey
1556
+ })
1557
+ }).then(() => resolve()).catch(reject);
1558
+ return;
1559
+ }
1560
+ resolve();
1561
+ }).catch(reject);
1562
+ }
1563
+ }
1564
+ } catch {
1565
+ // ignore cross-origin
1566
+ }
1567
+ }, 500);
1568
+ if (authWindow.closed) {
1569
+ clearInterval(interval);
1570
+ }
1571
+ });
1572
+ }
1573
+ } catch (error) {
1574
+ throw error;
1575
+ }
1576
+ }, [client, callbacks]);
1577
+ const handleXOauth = react.useCallback(async params => {
1578
+ const {
1579
+ clientId = masterConfig?.auth?.oauthConfig?.xClientId,
1580
+ openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
1581
+ additionalState: additionalParameters
1582
+ } = params || {};
1583
+ try {
1584
+ if (!masterConfig) {
1585
+ throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1586
+ }
1587
+ if (!clientId) {
1588
+ throw new sdkTypes.TurnkeyError("Twitter Client ID is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1589
+ }
1590
+ if (!masterConfig.auth?.oauthConfig?.oauthRedirectUri) {
1591
+ throw new sdkTypes.TurnkeyError("OAuth Redirect URI is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1592
+ }
1593
+ const flow = openInPage ? "redirect" : "popup";
1594
+ const redirectURI = masterConfig.auth?.oauthConfig.oauthRedirectUri;
1595
+ // Create key pair and generate nonce
1596
+ const publicKey = await createApiKeyPair();
1597
+ if (!publicKey) {
1598
+ throw new Error("Failed to create public key for OAuth.");
1599
+ }
1600
+ const nonce = utils$1.bytesToHex(sha2.sha256(publicKey));
1601
+ // Generate PKCE challenge pair
1602
+ const {
1603
+ verifier,
1604
+ codeChallenge
1605
+ } = await utils.generateChallengePair();
1606
+ sessionStorage.setItem("twitter_verifier", verifier);
1607
+ // Construct Twitter Auth URL
1608
+ const twitterAuthUrl = new URL(utils.X_AUTH_URL);
1609
+ twitterAuthUrl.searchParams.set("client_id", clientId);
1610
+ twitterAuthUrl.searchParams.set("redirect_uri", redirectURI);
1611
+ twitterAuthUrl.searchParams.set("response_type", "code");
1612
+ twitterAuthUrl.searchParams.set("code_challenge", codeChallenge);
1613
+ twitterAuthUrl.searchParams.set("code_challenge_method", "S256");
1614
+ twitterAuthUrl.searchParams.set("scope", "tweet.read users.read");
1615
+ twitterAuthUrl.searchParams.set("state", `provider=twitter&flow=${flow}&publicKey=${encodeURIComponent(publicKey)}&nonce=${nonce}`);
1616
+ if (additionalParameters) {
1617
+ const extra = Object.entries(additionalParameters).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
1618
+ if (extra) {
1619
+ twitterAuthUrl.searchParams.set("state", twitterAuthUrl.searchParams.get("state") + `&${extra}`);
1620
+ }
1621
+ }
1622
+ if (openInPage) {
1623
+ window.location.href = twitterAuthUrl.toString();
1624
+ return new Promise((_, reject) => {
1625
+ const timeout = setTimeout(() => {
1626
+ reject(new Error("Authentication timed out."));
1627
+ }, 300000);
1628
+ window.addEventListener("beforeunload", () => clearTimeout(timeout));
1629
+ });
1630
+ } else {
1631
+ const width = utils.popupWidth;
1632
+ const height = utils.popupHeight;
1633
+ const left = window.screenX + (window.innerWidth - width) / 2;
1634
+ const top = window.screenY + (window.innerHeight - height) / 2;
1635
+ const authWindow = window.open("about:blank", "_blank", `width=${width},height=${height},top=${top},left=${left},scrollbars=yes,resizable=yes`);
1636
+ if (!authWindow) {
1637
+ throw new Error("Failed to open Twitter login window.");
1638
+ }
1639
+ authWindow.location.href = twitterAuthUrl.toString();
1640
+ return new Promise((resolve, reject) => {
1641
+ const interval = setInterval(() => {
1642
+ try {
1643
+ if (authWindow.closed) {
1644
+ clearInterval(interval);
1645
+ reject(new Error("Authentication window was closed."));
1646
+ return;
1647
+ }
1648
+ const url = authWindow.location.href || "";
1649
+ if (url.startsWith(window.location.origin)) {
1650
+ const urlParams = new URLSearchParams(new URL(url).search);
1651
+ const authCode = urlParams.get("code");
1652
+ const stateParam = urlParams.get("state");
1653
+ const sessionKey = stateParam?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1654
+ if (authCode && stateParam && stateParam.includes("provider=twitter")) {
1655
+ authWindow.close();
1656
+ clearInterval(interval);
1657
+ const verifier = sessionStorage.getItem("twitter_verifier");
1658
+ if (!verifier) {
1659
+ reject(new Error("Missing PKCE verifier"));
1660
+ return;
1661
+ }
1662
+ client?.httpClient.proxyOAuth2Authenticate({
1663
+ provider: "OAUTH2_PROVIDER_X",
1664
+ authCode,
1665
+ redirectUri: redirectURI,
1666
+ codeVerifier: verifier,
1667
+ nonce
1668
+ }).then(resp => {
1669
+ sessionStorage.removeItem("twitter_verifier");
1670
+ const oidcToken = resp.oidcToken;
1671
+ if (params?.onOauthSuccess) {
1672
+ params.onOauthSuccess({
1673
+ oidcToken,
1674
+ providerName: "twitter",
1675
+ ...(sessionKey && {
1676
+ sessionKey
1677
+ })
1678
+ });
1679
+ } else if (callbacks?.onOauthRedirect) {
1680
+ callbacks.onOauthRedirect({
1681
+ idToken: oidcToken,
1682
+ publicKey,
1683
+ ...(sessionKey && {
1684
+ sessionKey
1685
+ })
1686
+ });
1687
+ } else {
1688
+ completeOauth({
1689
+ oidcToken,
1690
+ publicKey,
1691
+ providerName: "twitter",
1692
+ ...(sessionKey && {
1693
+ sessionKey
1694
+ })
1695
+ }).then(() => resolve()).catch(reject);
1696
+ return;
1697
+ }
1698
+ resolve();
1699
+ }).catch(reject);
1700
+ }
1701
+ }
1702
+ } catch {
1703
+ // ignore cross-origin
1704
+ }
1705
+ }, 500);
1706
+ if (authWindow.closed) {
1707
+ clearInterval(interval);
1708
+ }
1709
+ });
1710
+ }
1711
+ } catch (error) {
1712
+ throw error;
1713
+ }
1714
+ }, [client, callbacks]);
1232
1715
  const handleGoogleOauth = react.useCallback(async params => {
1233
1716
  const {
1234
1717
  clientId = masterConfig?.auth?.oauthConfig?.googleClientId,
@@ -1309,24 +1792,35 @@ const ClientProvider = ({
1309
1792
  if (url.startsWith(window.location.origin)) {
1310
1793
  const hashParams = new URLSearchParams(url.split("#")[1]);
1311
1794
  const idToken = hashParams.get("id_token");
1795
+ const stateParams = hashParams.get("state");
1796
+ const sessionKey = stateParams?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1312
1797
  if (idToken) {
1313
1798
  authWindow.close();
1314
1799
  clearInterval(interval);
1315
1800
  if (params?.onOauthSuccess) {
1316
1801
  params.onOauthSuccess({
1317
1802
  oidcToken: idToken,
1318
- providerName: "google"
1803
+ providerName: "google",
1804
+ ...(sessionKey && {
1805
+ sessionKey
1806
+ })
1319
1807
  });
1320
1808
  } else if (callbacks?.onOauthRedirect) {
1321
1809
  callbacks.onOauthRedirect({
1322
1810
  idToken,
1323
- publicKey
1811
+ publicKey,
1812
+ ...(sessionKey && {
1813
+ sessionKey
1814
+ })
1324
1815
  });
1325
1816
  } else {
1326
1817
  completeOauth({
1327
1818
  oidcToken: idToken,
1328
1819
  publicKey,
1329
- providerName: "google"
1820
+ providerName: "google",
1821
+ ...(sessionKey && {
1822
+ sessionKey
1823
+ })
1330
1824
  }).then(() => resolve()).catch(reject);
1331
1825
  return;
1332
1826
  }
@@ -1422,24 +1916,35 @@ const ClientProvider = ({
1422
1916
  if (url.startsWith(window.location.origin)) {
1423
1917
  const hashParams = new URLSearchParams(url.split("#")[1]);
1424
1918
  const idToken = hashParams.get("id_token");
1919
+ const stateParams = hashParams.get("state");
1920
+ const sessionKey = stateParams?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1425
1921
  if (idToken) {
1426
1922
  authWindow.close();
1427
1923
  clearInterval(interval);
1428
1924
  if (params?.onOauthSuccess) {
1429
1925
  params.onOauthSuccess({
1430
1926
  oidcToken: idToken,
1431
- providerName: "apple"
1927
+ providerName: "apple",
1928
+ ...(sessionKey && {
1929
+ sessionKey
1930
+ })
1432
1931
  });
1433
1932
  } else if (callbacks?.onOauthRedirect) {
1434
1933
  callbacks.onOauthRedirect({
1435
1934
  idToken,
1436
- publicKey
1935
+ publicKey,
1936
+ ...(sessionKey && {
1937
+ sessionKey
1938
+ })
1437
1939
  });
1438
1940
  } else {
1439
1941
  completeOauth({
1440
1942
  oidcToken: idToken,
1441
1943
  publicKey,
1442
- providerName: "apple"
1944
+ providerName: "apple",
1945
+ ...(sessionKey && {
1946
+ sessionKey
1947
+ })
1443
1948
  }).then(() => resolve()).catch(reject);
1444
1949
  return;
1445
1950
  }
@@ -1546,6 +2051,7 @@ const ClientProvider = ({
1546
2051
  const authCode = urlParams.get("code");
1547
2052
  const stateParam = urlParams.get("state");
1548
2053
  if (authCode && stateParam && stateParam.includes("provider=facebook")) {
2054
+ const sessionKey = stateParam?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1549
2055
  authWindow.close();
1550
2056
  clearInterval(interval);
1551
2057
  // Exchange code for token
@@ -1559,18 +2065,27 @@ const ClientProvider = ({
1559
2065
  if (params?.onOauthSuccess) {
1560
2066
  params.onOauthSuccess({
1561
2067
  oidcToken: tokenData.id_token,
1562
- providerName: "apple"
2068
+ providerName: "facebook",
2069
+ ...(sessionKey && {
2070
+ sessionKey
2071
+ })
1563
2072
  });
1564
2073
  } else if (callbacks?.onOauthRedirect) {
1565
2074
  callbacks.onOauthRedirect({
1566
2075
  idToken: tokenData.id_token,
1567
- publicKey
2076
+ publicKey,
2077
+ ...(sessionKey && {
2078
+ sessionKey
2079
+ })
1568
2080
  });
1569
2081
  } else {
1570
2082
  completeOauth({
1571
2083
  oidcToken: tokenData.id_token,
1572
2084
  publicKey,
1573
- providerName: "facebook"
2085
+ providerName: "facebook",
2086
+ ...(sessionKey && {
2087
+ sessionKey
2088
+ })
1574
2089
  }).then(() => resolve()).catch(reject);
1575
2090
  return;
1576
2091
  }
@@ -1591,10 +2106,12 @@ const ClientProvider = ({
1591
2106
  throw error;
1592
2107
  }
1593
2108
  }, [client, callbacks]);
1594
- const handleLogin = react.useCallback(async () => {
2109
+ const handleLogin = react.useCallback(async params => {
1595
2110
  pushPage({
1596
2111
  key: "Log in or sign up",
1597
- content: jsxRuntime.jsx(index.AuthComponent, {})
2112
+ content: jsxRuntime.jsx(index.AuthComponent, {
2113
+ sessionKey: params?.sessionKey
2114
+ })
1598
2115
  });
1599
2116
  }, [pushPage]);
1600
2117
  const handleExportWallet = react.useCallback(async params => {
@@ -1604,7 +2121,7 @@ const ClientProvider = ({
1604
2121
  stampWith
1605
2122
  } = params;
1606
2123
  pushPage({
1607
- key: "Export Wallet",
2124
+ key: "Export wallet",
1608
2125
  content: jsxRuntime.jsx(Export.ExportComponent, {
1609
2126
  target: walletId,
1610
2127
  exportType: base.ExportType.Wallet,
@@ -1621,13 +2138,17 @@ const ClientProvider = ({
1621
2138
  const {
1622
2139
  privateKeyId,
1623
2140
  targetPublicKey,
2141
+ keyFormat,
1624
2142
  stampWith
1625
2143
  } = params;
1626
2144
  pushPage({
1627
- key: "Export Private Key",
2145
+ key: "Export private key",
1628
2146
  content: jsxRuntime.jsx(Export.ExportComponent, {
1629
2147
  target: privateKeyId,
1630
2148
  exportType: base.ExportType.PrivateKey,
2149
+ ...(keyFormat !== undefined ? {
2150
+ keyFormat
2151
+ } : {}),
1631
2152
  ...(targetPublicKey !== undefined ? {
1632
2153
  targetPublicKey
1633
2154
  } : {}),
@@ -1641,13 +2162,17 @@ const ClientProvider = ({
1641
2162
  const {
1642
2163
  address,
1643
2164
  targetPublicKey,
2165
+ keyFormat,
1644
2166
  stampWith
1645
2167
  } = params;
1646
2168
  pushPage({
1647
- key: "Export Wallet Account",
2169
+ key: "Export wallet account",
1648
2170
  content: jsxRuntime.jsx(Export.ExportComponent, {
1649
2171
  target: address,
1650
2172
  exportType: base.ExportType.WalletAccount,
2173
+ ...(keyFormat !== undefined ? {
2174
+ keyFormat
2175
+ } : {}),
1651
2176
  ...(targetPublicKey !== undefined ? {
1652
2177
  targetPublicKey
1653
2178
  } : {}),
@@ -1665,8 +2190,9 @@ const ClientProvider = ({
1665
2190
  } = params || {};
1666
2191
  try {
1667
2192
  return utils.withTurnkeyErrorHandling(() => new Promise((resolve, reject) => pushPage({
1668
- key: "Import Wallet",
2193
+ key: "Import wallet",
1669
2194
  content: jsxRuntime.jsx(Import.ImportComponent, {
2195
+ importType: base.ImportType.Wallet,
1670
2196
  onError: error => {
1671
2197
  reject(error);
1672
2198
  },
@@ -1689,6 +2215,39 @@ const ClientProvider = ({
1689
2215
  throw new sdkTypes.TurnkeyError("Failed to import wallet.", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR, error);
1690
2216
  }
1691
2217
  }, [pushPage]);
2218
+ const handleImportPrivateKey = react.useCallback(async params => {
2219
+ const {
2220
+ curve,
2221
+ addressFormats,
2222
+ successPageDuration = 2000,
2223
+ stampWith
2224
+ } = params || {};
2225
+ try {
2226
+ return utils.withTurnkeyErrorHandling(() => new Promise((resolve, reject) => pushPage({
2227
+ key: "Import private key",
2228
+ content: jsxRuntime.jsx(Import.ImportComponent, {
2229
+ importType: base.ImportType.PrivateKey,
2230
+ curve: curve,
2231
+ addressFormats: addressFormats,
2232
+ onError: error => {
2233
+ reject(error);
2234
+ },
2235
+ onSuccess: privateKeyId => resolve(privateKeyId),
2236
+ ...(successPageDuration !== undefined && {
2237
+ successPageDuration
2238
+ }),
2239
+ ...(stampWith !== undefined && {
2240
+ stampWith
2241
+ })
2242
+ })
2243
+ })));
2244
+ } catch (error) {
2245
+ if (error instanceof sdkTypes.TurnkeyError) {
2246
+ throw error;
2247
+ }
2248
+ throw new sdkTypes.TurnkeyError("Failed to import private key.", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR, error);
2249
+ }
2250
+ }, [pushPage]);
1692
2251
  const handleUpdateUserName = react.useCallback(async params => {
1693
2252
  const {
1694
2253
  successPageDuration = 2000,
@@ -2320,6 +2879,22 @@ const ClientProvider = ({
2320
2879
  });
2321
2880
  };
2322
2881
  switch (providerName) {
2882
+ case sdkTypes.OAuthProviders.DISCORD:
2883
+ {
2884
+ await handleDiscordOauth({
2885
+ openInPage: false,
2886
+ onOauthSuccess
2887
+ });
2888
+ break;
2889
+ }
2890
+ case sdkTypes.OAuthProviders.X:
2891
+ {
2892
+ await handleXOauth({
2893
+ openInPage: false,
2894
+ onOauthSuccess
2895
+ });
2896
+ break;
2897
+ }
2323
2898
  case sdkTypes.OAuthProviders.GOOGLE:
2324
2899
  {
2325
2900
  await handleGoogleOauth({
@@ -2350,7 +2925,7 @@ const ClientProvider = ({
2350
2925
  }
2351
2926
  }
2352
2927
  }, [pushPage]);
2353
- const handleLinkExternalWallet = react.useCallback(async params => {
2928
+ const handleConnectExternalWallet = react.useCallback(async params => {
2354
2929
  const {
2355
2930
  successPageDuration = 2000
2356
2931
  } = params || {};
@@ -2363,8 +2938,8 @@ const ClientProvider = ({
2363
2938
  }
2364
2939
  const providers = await getWalletProviders();
2365
2940
  pushPage({
2366
- key: "Link Wallet",
2367
- content: jsxRuntime.jsx(LinkWallet.LinkWalletModal, {
2941
+ key: "Connect wallet",
2942
+ content: jsxRuntime.jsx(ConnectWallet.ConnectWalletModal, {
2368
2943
  providers: providers,
2369
2944
  successPageDuration: successPageDuration
2370
2945
  })
@@ -2563,6 +3138,7 @@ const ClientProvider = ({
2563
3138
  completeOauth,
2564
3139
  fetchWallets,
2565
3140
  fetchWalletAccounts,
3141
+ fetchPrivateKeys,
2566
3142
  refreshWallets,
2567
3143
  signMessage,
2568
3144
  signTransaction,
@@ -2599,12 +3175,15 @@ const ClientProvider = ({
2599
3175
  getProxyAuthConfig,
2600
3176
  handleLogin,
2601
3177
  handleGoogleOauth,
3178
+ handleXOauth,
3179
+ handleDiscordOauth,
2602
3180
  handleAppleOauth,
2603
3181
  handleFacebookOauth,
2604
3182
  handleExportWallet,
2605
3183
  handleExportPrivateKey,
2606
3184
  handleExportWalletAccount,
2607
3185
  handleImportWallet,
3186
+ handleImportPrivateKey,
2608
3187
  handleUpdateUserEmail,
2609
3188
  handleUpdateUserPhoneNumber,
2610
3189
  handleUpdateUserName,
@@ -2615,7 +3194,7 @@ const ClientProvider = ({
2615
3194
  handleAddEmail,
2616
3195
  handleAddPhoneNumber,
2617
3196
  handleSignMessage,
2618
- handleLinkExternalWallet,
3197
+ handleConnectExternalWallet,
2619
3198
  handleRemoveUserEmail,
2620
3199
  handleRemoveUserPhoneNumber
2621
3200
  },