@turnkey/react-wallet-kit 1.11.0 → 2.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 (86) hide show
  1. package/dist/components/auth/OTP.d.ts +1 -0
  2. package/dist/components/auth/OTP.d.ts.map +1 -1
  3. package/dist/components/auth/OTP.js +8 -2
  4. package/dist/components/auth/OTP.js.map +1 -1
  5. package/dist/components/auth/OTP.mjs +21 -13
  6. package/dist/components/auth/OTP.mjs.map +1 -1
  7. package/dist/components/auth/index.d.ts.map +1 -1
  8. package/dist/components/auth/index.js +11 -3
  9. package/dist/components/auth/index.js.map +1 -1
  10. package/dist/components/auth/index.mjs +12 -5
  11. package/dist/components/auth/index.mjs.map +1 -1
  12. package/dist/components/user/UpdateEmail.js +6 -3
  13. package/dist/components/user/UpdateEmail.js.map +1 -1
  14. package/dist/components/user/UpdateEmail.mjs +6 -4
  15. package/dist/components/user/UpdateEmail.mjs.map +1 -1
  16. package/dist/components/user/UpdatePhoneNumber.js +6 -3
  17. package/dist/components/user/UpdatePhoneNumber.js.map +1 -1
  18. package/dist/components/user/UpdatePhoneNumber.mjs +6 -4
  19. package/dist/components/user/UpdatePhoneNumber.mjs.map +1 -1
  20. package/dist/index.d.ts +1 -3
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +8 -4
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +1 -1
  25. package/dist/providers/client/Provider.d.ts.map +1 -1
  26. package/dist/providers/client/Provider.js +611 -430
  27. package/dist/providers/client/Provider.js.map +1 -1
  28. package/dist/providers/client/Provider.mjs +2055 -1820
  29. package/dist/providers/client/Provider.mjs.map +1 -1
  30. package/dist/providers/client/Types.d.ts +14 -7
  31. package/dist/providers/client/Types.d.ts.map +1 -1
  32. package/dist/providers/client/Types.js.map +1 -1
  33. package/dist/providers/client/Types.mjs.map +1 -1
  34. package/dist/typedoc-entry/index.d.ts +1 -1
  35. package/dist/typedoc-entry/index.d.ts.map +1 -1
  36. package/dist/types/base.d.ts +38 -28
  37. package/dist/types/base.d.ts.map +1 -1
  38. package/dist/types/base.js.map +1 -1
  39. package/dist/types/base.mjs.map +1 -1
  40. package/dist/types/method-types.d.ts +12 -5
  41. package/dist/types/method-types.d.ts.map +1 -1
  42. package/dist/utils/oauth/completion.d.ts +132 -0
  43. package/dist/utils/oauth/completion.d.ts.map +1 -0
  44. package/dist/utils/oauth/completion.js +136 -0
  45. package/dist/utils/oauth/completion.js.map +1 -0
  46. package/dist/utils/oauth/completion.mjs +176 -0
  47. package/dist/utils/oauth/completion.mjs.map +1 -0
  48. package/dist/utils/oauth/config.d.ts +34 -0
  49. package/dist/utils/oauth/config.d.ts.map +1 -0
  50. package/dist/utils/oauth/config.js +69 -0
  51. package/dist/utils/oauth/config.js.map +1 -0
  52. package/dist/utils/oauth/config.mjs +54 -0
  53. package/dist/utils/oauth/config.mjs.map +1 -0
  54. package/dist/utils/oauth/helpers.d.ts +13 -0
  55. package/dist/utils/oauth/helpers.d.ts.map +1 -0
  56. package/dist/utils/oauth/helpers.js +38 -0
  57. package/dist/utils/oauth/helpers.js.map +1 -0
  58. package/dist/utils/oauth/helpers.mjs +35 -0
  59. package/dist/utils/oauth/helpers.mjs.map +1 -0
  60. package/dist/utils/oauth/index.d.ts +7 -0
  61. package/dist/utils/oauth/index.d.ts.map +1 -0
  62. package/dist/utils/oauth/pkce.d.ts +14 -0
  63. package/dist/utils/oauth/pkce.d.ts.map +1 -0
  64. package/dist/utils/oauth/pkce.js +48 -0
  65. package/dist/utils/oauth/pkce.js.map +1 -0
  66. package/dist/utils/oauth/pkce.mjs +85 -0
  67. package/dist/utils/oauth/pkce.mjs.map +1 -0
  68. package/dist/utils/oauth/storage.d.ts +59 -0
  69. package/dist/utils/oauth/storage.d.ts.map +1 -0
  70. package/dist/utils/oauth/storage.js +116 -0
  71. package/dist/utils/oauth/storage.js.map +1 -0
  72. package/dist/utils/oauth/storage.mjs +104 -0
  73. package/dist/utils/oauth/storage.mjs.map +1 -0
  74. package/dist/utils/oauth/url.d.ts +92 -0
  75. package/dist/utils/oauth/url.d.ts.map +1 -0
  76. package/dist/utils/oauth/url.js +231 -0
  77. package/dist/utils/oauth/url.js.map +1 -0
  78. package/dist/utils/oauth/url.mjs +237 -0
  79. package/dist/utils/oauth/url.mjs.map +1 -0
  80. package/package.json +3 -3
  81. package/dist/utils/oauth.d.ts +0 -323
  82. package/dist/utils/oauth.d.ts.map +0 -1
  83. package/dist/utils/oauth.js +0 -613
  84. package/dist/utils/oauth.js.map +0 -1
  85. package/dist/utils/oauth.mjs +0 -670
  86. package/dist/utils/oauth.mjs.map +0 -1
@@ -4,7 +4,12 @@
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 oauth = require('../../utils/oauth.js');
7
+ var storage = require('../../utils/oauth/storage.js');
8
+ require('../../utils/oauth/config.js');
9
+ var url = require('../../utils/oauth/url.js');
10
+ var completion = require('../../utils/oauth/completion.js');
11
+ var pkce = require('../../utils/oauth/pkce.js');
12
+ var helpers = require('../../utils/oauth/helpers.js');
8
13
  var utils = require('../../utils/utils.js');
9
14
  var timers = require('../../utils/timers.js');
10
15
  var core = require('@turnkey/core');
@@ -33,7 +38,7 @@ var Verify = require('../../components/verify/Verify.js');
33
38
  var OnRamp = require('../../components/onramp/OnRamp.js');
34
39
  var Svg = require('../../components/design/Svg.js');
35
40
  var SendTransaction = require('../../components/send-transaction/SendTransaction.js');
36
- var helpers = require('../../components/send-transaction/helpers.js');
41
+ var helpers$1 = require('../../components/send-transaction/helpers.js');
37
42
 
38
43
  /**
39
44
  * Provides Turnkey client authentication, session management, wallet operations, and user profile management
@@ -89,302 +94,346 @@ const ClientProvider = ({
89
94
  closeModal
90
95
  } = Hook.useModal();
91
96
  const completeRedirectOauth = async () => {
92
- // Check for either hash or search parameters that could indicate an OAuth redirect
93
- if (!window.location.hash && !window.location.search) {
94
- // No OAuth redirect parameters found, nothing to do
95
- return;
96
- }
97
- /**
98
- * Wraps an OAuth completion action with optional modal UI.
99
- * This is the key difference between redirect and popup flows:
100
- * - Popup: No modal needed (the popup window itself is the UI)
101
- * - Redirect: Optional modal to show loading/success states on return
102
- */
103
- const withModalWrapper = async params => {
104
- const {
105
- provider,
106
- isAddProvider,
107
- metadata,
108
- openModal,
109
- action
110
- } = params;
111
- const providerDisplayName = oauth.capitalizeProviderName(provider);
112
- const icon = oauth.getProviderIcon(provider);
113
- if (openModal === "true") {
114
- // Show modal UI for the completion
115
- await new Promise((resolve, reject) => {
116
- pushPage({
117
- key: `${providerDisplayName} OAuth`,
118
- content: jsxRuntime.jsx(Action.ActionPage, {
119
- closeOnComplete: isAddProvider ? false : true,
120
- title: isAddProvider ? `Adding ${providerDisplayName} provider...` : `Authenticating with ${providerDisplayName}...`,
121
- action: async () => {
122
- try {
123
- await action();
124
- if (isAddProvider && metadata) {
125
- // Don't show success for auth. Not needed
126
- pushPage({
127
- key: "OAuth Provider Added",
128
- content: jsxRuntime.jsx(Success.SuccessPage, {
129
- text: `Successfully added ${providerDisplayName} OAuth provider!`,
130
- duration: metadata.successPageDuration ?? 2000,
131
- onComplete: () => {
132
- closeModal();
133
- }
134
- }),
135
- preventBack: true,
136
- showTitle: false
137
- });
138
- }
139
- resolve();
140
- } catch (err) {
141
- if (isAddProvider) {
142
- oauth.clearOAuthAddProviderMetadata();
143
- }
144
- reject(err);
145
- popPage();
146
- }
147
- },
148
- icon: jsxRuntime.jsx(reactFontawesome.FontAwesomeIcon, {
149
- icon: icon,
150
- size: "3x"
151
- })
152
- }),
153
- showTitle: false,
154
- onClose: () => {
155
- if (isAddProvider) {
156
- oauth.clearOAuthAddProviderMetadata();
157
- }
158
- reject(new sdkTypes.TurnkeyError(isAddProvider ? `User canceled the ${providerDisplayName} add provider process.` : `User canceled the ${providerDisplayName} authentication process.`, sdkTypes.TurnkeyErrorCodes.USER_CANCELED));
159
- }
160
- });
161
- });
162
- } else {
163
- // No modal - execute directly
164
- try {
165
- await action();
166
- } catch (err) {
167
- if (isAddProvider) {
168
- oauth.clearOAuthAddProviderMetadata();
169
- }
170
- throw err;
171
- }
172
- }
173
- };
174
- // Handle PKCE-based OAuth redirects (Facebook, Discord, X) with code in search parameters
175
- if (window.location.search && window.location.search.includes("code=") && window.location.search.includes("state=")) {
176
- // Parse the URL using our unified helper
177
- const result = oauth.parseOAuthResponse(window.location.href);
178
- if (!result || !result.authCode || !result.publicKey) {
179
- return;
180
- }
181
- if (result.flow !== "redirect") {
182
- // To complete OAuth we need redirect flow
97
+ // Since we use localStorage (see storage.ts), we always clean up OAuth data
98
+ // when this runs — even if there are no OAuth params in the URL (e.g. the
99
+ // user canceled at the provider and came back manually)
100
+ try {
101
+ // Check for either hash or search parameters that could indicate an OAuth redirect
102
+ if (!window.location.hash && !window.location.search) {
103
+ // No OAuth redirect parameters found, nothing to do
183
104
  return;
184
105
  }
185
- const {
186
- authCode: code,
187
- provider,
188
- publicKey,
189
- oauthIntent,
190
- sessionKey,
191
- nonce,
192
- openModal
193
- } = result;
194
- const isAddProvider = oauthIntent === oauth.OAUTH_INTENT_ADD_PROVIDER;
195
- const metadata = isAddProvider ? oauth.getOAuthAddProviderMetadata() : null;
196
106
  /**
197
- * Helper to complete PKCE redirect flow with optional modal wrapper.
198
- * Uses handlePKCEFlow from oauth utils for the core logic.
199
- * We put this in a separate function to avoid duplicating for each provider.
107
+ * Wraps an OAuth completion action with optional modal UI.
108
+ * This is the key difference between redirect and popup flows:
109
+ * - Popup: No modal needed (the popup window itself is the UI)
110
+ * - Redirect: Optional modal to show loading/success states on return
200
111
  */
201
- const completePKCERedirect = async (providerName, exchangeCodeFn) => {
202
- const action = async () => {
203
- try {
204
- await oauth.handlePKCEFlow({
205
- publicKey,
206
- providerName,
207
- sessionKey: sessionKey ?? undefined,
208
- callbacks,
209
- completeOauth,
210
- onAddProvider:
211
- // Only set onAddProvider if we are adding a provider and have metadata
212
- isAddProvider && metadata ? async oidcToken => {
213
- await addOauthProvider({
214
- providerName: provider,
215
- oidcToken,
216
- organizationId: metadata.organizationId,
217
- userId: metadata.userId,
218
- ...(metadata.stampWith && {
219
- stampWith: metadata.stampWith
220
- })
221
- });
222
- oauth.clearOAuthAddProviderMetadata();
223
- } : undefined,
224
- exchangeCodeForToken: exchangeCodeFn
225
- });
226
- } catch (err) {
227
- if (callbacks?.onError) {
228
- const providerDisplayName = oauth.capitalizeProviderName(providerName);
229
- callbacks.onError(err instanceof sdkTypes.TurnkeyError ? err : new sdkTypes.TurnkeyError(`${providerDisplayName} authentication failed`, sdkTypes.TurnkeyErrorCodes.OAUTH_SIGNUP_ERROR, err));
230
- }
231
- throw err;
232
- }
233
- };
234
- await withModalWrapper({
235
- provider: providerName,
112
+ const withModalWrapper = async params => {
113
+ const {
114
+ provider,
236
115
  isAddProvider,
237
116
  metadata,
238
117
  openModal,
239
118
  action
240
- });
241
- // Clean up URL after successful completion
242
- oauth.cleanupOAuthUrl();
243
- };
244
- // FACEBOOK
245
- if (provider === sdkTypes.OAuthProviders.FACEBOOK) {
246
- const clientId = masterConfig?.auth?.oauthConfig?.facebookClientId;
247
- const redirectURI = masterConfig?.auth?.oauthConfig?.oauthRedirectUri;
248
- const hasVerifier = oauth.hasPKCEVerifier(sdkTypes.OAuthProviders.FACEBOOK);
249
- if (clientId && redirectURI && hasVerifier) {
250
- await completePKCERedirect(sdkTypes.OAuthProviders.FACEBOOK, async codeVerifier => {
251
- const tokenResponse = await oauth.exchangeFacebookCodeForToken(clientId, redirectURI, code, codeVerifier);
252
- const oidcToken = tokenResponse?.id_token;
253
- if (!oidcToken) {
254
- throw new sdkTypes.TurnkeyError("Missing OIDC token", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
255
- }
256
- return oidcToken;
257
- });
258
- }
259
- return;
260
- }
261
- // DISCORD
262
- if (provider === sdkTypes.OAuthProviders.DISCORD) {
263
- const clientId = masterConfig?.auth?.oauthConfig?.discordClientId;
264
- const redirectURI = masterConfig?.auth?.oauthConfig?.oauthRedirectUri;
265
- const hasVerifier = oauth.hasPKCEVerifier(sdkTypes.OAuthProviders.DISCORD);
266
- if (clientId && redirectURI && hasVerifier && nonce) {
267
- await completePKCERedirect(sdkTypes.OAuthProviders.DISCORD, async codeVerifier => {
268
- const resp = await client?.httpClient.proxyOAuth2Authenticate({
269
- provider: "OAUTH2_PROVIDER_DISCORD",
270
- authCode: code,
271
- redirectUri: redirectURI,
272
- codeVerifier,
273
- clientId,
274
- nonce
119
+ } = params;
120
+ const providerDisplayName = helpers.capitalizeProviderName(provider);
121
+ const icon = helpers.getProviderIcon(provider);
122
+ if (openModal === "true") {
123
+ // Show modal UI for the completion
124
+ await new Promise((resolve, reject) => {
125
+ pushPage({
126
+ key: `${providerDisplayName} OAuth`,
127
+ content: jsxRuntime.jsx(Action.ActionPage, {
128
+ closeOnComplete: isAddProvider ? false : true,
129
+ title: isAddProvider ? `Adding ${providerDisplayName} provider...` : `Authenticating with ${providerDisplayName}...`,
130
+ action: async () => {
131
+ try {
132
+ await action();
133
+ if (isAddProvider && metadata) {
134
+ // Don't show success for auth. Not needed
135
+ pushPage({
136
+ key: "OAuth Provider Added",
137
+ content: jsxRuntime.jsx(Success.SuccessPage, {
138
+ text: `Successfully added ${providerDisplayName} OAuth provider!`,
139
+ duration: metadata.successPageDuration ?? 2000,
140
+ onComplete: () => {
141
+ closeModal();
142
+ }
143
+ }),
144
+ preventBack: true,
145
+ showTitle: false
146
+ });
147
+ }
148
+ resolve();
149
+ } catch (err) {
150
+ reject(err);
151
+ popPage();
152
+ }
153
+ },
154
+ icon: jsxRuntime.jsx(reactFontawesome.FontAwesomeIcon, {
155
+ icon: icon,
156
+ size: "3x"
157
+ })
158
+ }),
159
+ showTitle: false,
160
+ onClose: () => {
161
+ reject(new sdkTypes.TurnkeyError(isAddProvider ? `User canceled the ${providerDisplayName} add provider process.` : `User canceled the ${providerDisplayName} authentication process.`, sdkTypes.TurnkeyErrorCodes.USER_CANCELED));
162
+ }
275
163
  });
276
- const oidcToken = resp?.oidcToken;
277
- if (!oidcToken) {
278
- throw new sdkTypes.TurnkeyError("Missing OIDC token", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
279
- }
280
- return oidcToken;
281
164
  });
165
+ } else {
166
+ // No modal - execute directly
167
+ await action();
282
168
  }
283
- return;
284
- }
285
- // X (Twitter)
286
- if (provider === sdkTypes.OAuthProviders.X) {
287
- const clientId = masterConfig?.auth?.oauthConfig?.xClientId;
288
- const redirectURI = masterConfig?.auth?.oauthConfig?.oauthRedirectUri;
289
- const hasVerifier = oauth.hasPKCEVerifier(sdkTypes.OAuthProviders.X);
290
- if (clientId && redirectURI && hasVerifier && nonce) {
291
- await completePKCERedirect(sdkTypes.OAuthProviders.X, async codeVerifier => {
292
- const resp = await client?.httpClient.proxyOAuth2Authenticate({
293
- provider: "OAUTH2_PROVIDER_X",
294
- authCode: code,
295
- redirectUri: redirectURI,
296
- codeVerifier,
297
- clientId,
298
- nonce
299
- });
300
- const oidcToken = resp?.oidcToken;
301
- if (!oidcToken) {
302
- throw new sdkTypes.TurnkeyError("Missing OIDC token", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
169
+ };
170
+ // Handle PKCE-based OAuth redirects (Facebook, Discord, X) with code in search parameters
171
+ if (window.location.search && window.location.search.includes("code=") && window.location.search.includes("state=")) {
172
+ // Parse the URL using our unified helper
173
+ const result = url.parseOAuthResponse(window.location.href);
174
+ if (!result || !result.authCode || !result.publicKey) {
175
+ return;
176
+ }
177
+ if (result.flow !== "redirect") {
178
+ // To complete OAuth we need redirect flow
179
+ return;
180
+ }
181
+ const {
182
+ authCode: code,
183
+ provider,
184
+ publicKey,
185
+ oauthIntent,
186
+ sessionKey,
187
+ nonce,
188
+ openModal
189
+ } = result;
190
+ const isAddProvider = oauthIntent === storage.OAUTH_INTENT_ADD_PROVIDER;
191
+ const metadata = isAddProvider ? storage.getOAuthAddProviderMetadata() : null;
192
+ /**
193
+ * Helper to complete PKCE redirect flow with optional modal wrapper.
194
+ * Uses handlePKCEFlow from oauth utils for the core logic.
195
+ * We put this in a separate function to avoid duplicating for each provider.
196
+ */
197
+ const completePKCERedirect = async (providerName, exchangeCodeFn, secondaryClientIds) => {
198
+ const action = async () => {
199
+ try {
200
+ await completion.completePKCEFlow({
201
+ publicKey,
202
+ providerName,
203
+ sessionKey: sessionKey ?? undefined,
204
+ callbacks,
205
+ completeOauth: completionParams => {
206
+ const existingCreateSubOrgParams = masterConfig?.auth?.createSuborgParams?.oauth;
207
+ const secondaryProviders = core.buildSecondaryOauthProviders(completionParams.oidcToken, completionParams.providerName ?? providerName, secondaryClientIds);
208
+ return completeOauth({
209
+ ...completionParams,
210
+ ...(secondaryProviders.length > 0 && {
211
+ createSubOrgParams: {
212
+ ...existingCreateSubOrgParams,
213
+ oauthProviders: [...(existingCreateSubOrgParams?.oauthProviders ?? []), ...secondaryProviders]
214
+ }
215
+ })
216
+ });
217
+ },
218
+ onAddProvider:
219
+ // Only set onAddProvider if we are adding a provider and have metadata
220
+ isAddProvider && metadata ? async oidcToken => {
221
+ const oidcClaims = core.buildSecondaryOidcClaims(oidcToken, secondaryClientIds);
222
+ await addOauthProvider({
223
+ providerName: provider,
224
+ oidcToken,
225
+ ...(oidcClaims.length > 0 && {
226
+ oidcClaims
227
+ }),
228
+ organizationId: metadata.organizationId,
229
+ userId: metadata.userId,
230
+ ...(metadata.stampWith && {
231
+ stampWith: metadata.stampWith
232
+ })
233
+ });
234
+ } : undefined,
235
+ exchangeCodeForToken: exchangeCodeFn
236
+ });
237
+ } catch (err) {
238
+ if (callbacks?.onError) {
239
+ const providerDisplayName = helpers.capitalizeProviderName(providerName);
240
+ callbacks.onError(err instanceof sdkTypes.TurnkeyError ? err : new sdkTypes.TurnkeyError(`${providerDisplayName} authentication failed`, sdkTypes.TurnkeyErrorCodes.OAUTH_SIGNUP_ERROR, err));
241
+ }
242
+ throw err;
303
243
  }
304
- return oidcToken;
244
+ };
245
+ await withModalWrapper({
246
+ provider: providerName,
247
+ isAddProvider,
248
+ metadata,
249
+ openModal,
250
+ action
305
251
  });
252
+ // Clean up URL after successful completion
253
+ url.cleanupOAuthUrl();
254
+ };
255
+ // FACEBOOK
256
+ if (provider === sdkTypes.OAuthProviders.FACEBOOK) {
257
+ const clientId = masterConfig?.auth?.oauthConfig?.facebook?.primaryClientId;
258
+ const secondaryClientIds = masterConfig?.auth?.oauthConfig?.facebook?.secondaryClientIds ?? [];
259
+ const redirectURI = masterConfig?.auth?.oauthConfig?.oauthRedirectUri;
260
+ const hasVerifier = storage.hasPKCEVerifier(sdkTypes.OAuthProviders.FACEBOOK);
261
+ if (clientId && redirectURI && hasVerifier) {
262
+ await completePKCERedirect(sdkTypes.OAuthProviders.FACEBOOK, async codeVerifier => {
263
+ const tokenResponse = await pkce.exchangeFacebookCodeForToken(clientId, redirectURI, code, codeVerifier);
264
+ const oidcToken = tokenResponse?.id_token;
265
+ if (!oidcToken) {
266
+ throw new sdkTypes.TurnkeyError("Missing OIDC token", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
267
+ }
268
+ return oidcToken;
269
+ }, secondaryClientIds);
270
+ }
271
+ return;
272
+ }
273
+ // DISCORD
274
+ if (provider === sdkTypes.OAuthProviders.DISCORD) {
275
+ const clientId = masterConfig?.auth?.oauthConfig?.discord?.primaryClientId;
276
+ const secondaryClientIds = masterConfig?.auth?.oauthConfig?.discord?.secondaryClientIds ?? [];
277
+ const redirectURI = masterConfig?.auth?.oauthConfig?.oauthRedirectUri;
278
+ const hasVerifier = storage.hasPKCEVerifier(sdkTypes.OAuthProviders.DISCORD);
279
+ if (clientId && redirectURI && hasVerifier && nonce) {
280
+ await completePKCERedirect(sdkTypes.OAuthProviders.DISCORD, async codeVerifier => {
281
+ const resp = await client?.httpClient.proxyOAuth2Authenticate({
282
+ provider: "OAUTH2_PROVIDER_DISCORD",
283
+ authCode: code,
284
+ redirectUri: redirectURI,
285
+ codeVerifier,
286
+ clientId,
287
+ nonce
288
+ });
289
+ const oidcToken = resp?.oidcToken;
290
+ if (!oidcToken) {
291
+ throw new sdkTypes.TurnkeyError("Missing OIDC token", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
292
+ }
293
+ return oidcToken;
294
+ }, secondaryClientIds);
295
+ }
296
+ return;
297
+ }
298
+ // X (Twitter)
299
+ if (provider === sdkTypes.OAuthProviders.X) {
300
+ const clientId = masterConfig?.auth?.oauthConfig?.x?.primaryClientId;
301
+ const secondaryClientIds = masterConfig?.auth?.oauthConfig?.x?.secondaryClientIds ?? [];
302
+ const redirectURI = masterConfig?.auth?.oauthConfig?.oauthRedirectUri;
303
+ const hasVerifier = storage.hasPKCEVerifier(sdkTypes.OAuthProviders.X);
304
+ if (clientId && redirectURI && hasVerifier && nonce) {
305
+ await completePKCERedirect(sdkTypes.OAuthProviders.X, async codeVerifier => {
306
+ const resp = await client?.httpClient.proxyOAuth2Authenticate({
307
+ provider: "OAUTH2_PROVIDER_X",
308
+ authCode: code,
309
+ redirectUri: redirectURI,
310
+ codeVerifier,
311
+ clientId,
312
+ nonce
313
+ });
314
+ const oidcToken = resp?.oidcToken;
315
+ if (!oidcToken) {
316
+ throw new sdkTypes.TurnkeyError("Missing OIDC token", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
317
+ }
318
+ return oidcToken;
319
+ }, secondaryClientIds);
320
+ }
321
+ return;
306
322
  }
307
- return;
308
- }
309
- }
310
- // Handle Google/Apple redirects (uses hash with idToken - non-PKCE)
311
- if (window.location.hash) {
312
- // Parse the URL using our unified helper
313
- const result = oauth.parseOAuthResponse(window.location.href);
314
- if (!result || !result.idToken || result.flow !== "redirect" || !result.publicKey) {
315
- // idToken and publicKey are required to complete OAuth. These are both in the hash for non-PKCE providers
316
- return;
317
323
  }
318
- const {
319
- idToken,
320
- provider,
321
- publicKey,
322
- openModal,
323
- sessionKey,
324
- oauthIntent
325
- } = result;
326
- const isAddProvider = oauthIntent === oauth.OAUTH_INTENT_ADD_PROVIDER;
327
- const metadata = isAddProvider ? oauth.getOAuthAddProviderMetadata() : null;
328
- const resolvedProvider = provider || sdkTypes.OAuthProviders.GOOGLE;
329
- // Use completeOAuthFlow from utils for the core completion logic
330
- const action = async () => {
331
- await oauth.completeOAuthFlow({
332
- provider: resolvedProvider,
324
+ // Handle Google/Apple redirects (uses hash with idToken - non-PKCE)
325
+ if (window.location.hash) {
326
+ // Parse the URL using our unified helper
327
+ const result = url.parseOAuthResponse(window.location.href);
328
+ if (!result || !result.idToken || result.flow !== "redirect" || !result.publicKey) {
329
+ // idToken and publicKey are required to complete OAuth. These are both in the hash for non-PKCE providers
330
+ return;
331
+ }
332
+ const {
333
+ idToken,
334
+ provider,
333
335
  publicKey,
334
- oidcToken: idToken,
335
- sessionKey: sessionKey ?? undefined,
336
- callbacks,
337
- completeOauth,
338
- onAddProvider: isAddProvider && metadata ? async oidcToken => {
339
- await addOauthProvider({
340
- providerName: resolvedProvider,
341
- oidcToken,
342
- organizationId: metadata.organizationId,
343
- userId: metadata.userId,
344
- ...(metadata.stampWith && {
345
- stampWith: metadata.stampWith
346
- })
347
- });
348
- oauth.clearOAuthAddProviderMetadata();
349
- } : undefined
336
+ openModal,
337
+ sessionKey,
338
+ oauthIntent
339
+ } = result;
340
+ const isAddProvider = oauthIntent === storage.OAUTH_INTENT_ADD_PROVIDER;
341
+ const metadata = isAddProvider ? storage.getOAuthAddProviderMetadata() : null;
342
+ const resolvedProvider = provider || sdkTypes.OAuthProviders.GOOGLE;
343
+ // Grab Google/Apple secondary client IDs from config for use in completion
344
+ const secondaryClientIds = (resolvedProvider === sdkTypes.OAuthProviders.GOOGLE ? masterConfig?.auth?.oauthConfig?.google?.secondaryClientIds : resolvedProvider === sdkTypes.OAuthProviders.APPLE ? masterConfig?.auth?.oauthConfig?.apple?.secondaryClientIds : undefined) ?? [];
345
+ // Use completeOAuthFlow from utils for the core completion logic
346
+ const action = async () => {
347
+ await completion.completeOAuthFlow({
348
+ provider: resolvedProvider,
349
+ publicKey,
350
+ oidcToken: idToken,
351
+ sessionKey: sessionKey ?? undefined,
352
+ callbacks,
353
+ completeOauth: completionParams => {
354
+ const existingCreateSubOrgParams = masterConfig?.auth?.createSuborgParams?.oauth;
355
+ const secondaryProviders = core.buildSecondaryOauthProviders(completionParams.oidcToken, completionParams.providerName ?? resolvedProvider, secondaryClientIds);
356
+ return completeOauth({
357
+ ...completionParams,
358
+ ...(secondaryProviders.length > 0 && {
359
+ createSubOrgParams: {
360
+ ...existingCreateSubOrgParams,
361
+ oauthProviders: [...(existingCreateSubOrgParams?.oauthProviders ?? []), ...secondaryProviders]
362
+ }
363
+ })
364
+ });
365
+ },
366
+ onAddProvider: isAddProvider && metadata ? async oidcToken => {
367
+ const oidcClaims = core.buildSecondaryOidcClaims(oidcToken, secondaryClientIds);
368
+ await addOauthProvider({
369
+ providerName: resolvedProvider,
370
+ oidcToken,
371
+ ...(oidcClaims.length > 0 && {
372
+ oidcClaims
373
+ }),
374
+ organizationId: metadata.organizationId,
375
+ userId: metadata.userId,
376
+ ...(metadata.stampWith && {
377
+ stampWith: metadata.stampWith
378
+ })
379
+ });
380
+ } : undefined
381
+ });
382
+ };
383
+ await withModalWrapper({
384
+ provider: resolvedProvider,
385
+ isAddProvider,
386
+ metadata,
387
+ openModal: openModal ?? undefined,
388
+ action
350
389
  });
351
- };
352
- await withModalWrapper({
353
- provider: resolvedProvider,
354
- isAddProvider,
355
- metadata,
356
- openModal: openModal ?? undefined,
357
- action
358
- });
359
- // Clean up the URL after processing
360
- oauth.cleanupOAuthUrlPreserveSearch();
390
+ // Clean up the URL after processing
391
+ url.cleanupOAuthUrlPreserveSearch();
392
+ }
393
+ } finally {
394
+ storage.clearAllOAuthData();
361
395
  }
362
396
  };
363
397
  const buildConfig = proxyAuthConfig => {
364
398
  // Juggle the local overrides with the values set in the dashboard (proxyAuthConfig).
365
399
  const resolvedMethods = {
366
- emailOtpAuthEnabled: config.auth?.methods?.emailOtpAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("email"),
367
- smsOtpAuthEnabled: config.auth?.methods?.smsOtpAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("sms"),
368
- passkeyAuthEnabled: config.auth?.methods?.passkeyAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("passkey"),
369
- walletAuthEnabled: config.auth?.methods?.walletAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("wallet"),
370
- googleOauthEnabled: config.auth?.methods?.googleOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("google"),
371
- xOauthEnabled: config.auth?.methods?.xOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("x"),
372
- discordOauthEnabled: config.auth?.methods?.discordOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("discord"),
373
- appleOauthEnabled: config.auth?.methods?.appleOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("apple"),
374
- facebookOauthEnabled: config.auth?.methods?.facebookOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("facebook")
400
+ emailOtpAuthEnabled: config.ui?.authModal?.methods?.emailOtpAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("email"),
401
+ smsOtpAuthEnabled: config.ui?.authModal?.methods?.smsOtpAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("sms"),
402
+ passkeyAuthEnabled: config.ui?.authModal?.methods?.passkeyAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("passkey"),
403
+ walletAuthEnabled: config.ui?.authModal?.methods?.walletAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("wallet"),
404
+ googleOauthEnabled: config.ui?.authModal?.methods?.googleOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("google"),
405
+ xOauthEnabled: config.ui?.authModal?.methods?.xOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("x"),
406
+ discordOauthEnabled: config.ui?.authModal?.methods?.discordOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("discord"),
407
+ appleOauthEnabled: config.ui?.authModal?.methods?.appleOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("apple"),
408
+ facebookOauthEnabled: config.ui?.authModal?.methods?.facebookOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("facebook")
375
409
  };
376
- const resolvedClientIds = {
377
- googleClientId: config.auth?.oauthConfig?.googleClientId ?? proxyAuthConfig?.oauthClientIds?.google,
378
- appleClientId: config.auth?.oauthConfig?.appleClientId ?? proxyAuthConfig?.oauthClientIds?.apple,
379
- facebookClientId: config.auth?.oauthConfig?.facebookClientId ?? proxyAuthConfig?.oauthClientIds?.facebook,
380
- xClientId: config.auth?.oauthConfig?.xClientId ?? proxyAuthConfig?.oauthClientIds?.x,
381
- discordClientId: config.auth?.oauthConfig?.discordClientId ?? proxyAuthConfig?.oauthClientIds?.discord
410
+ const resolvedOauthProviders = {
411
+ google: {
412
+ primaryClientId: config.auth?.oauthConfig?.google?.primaryClientId ?? proxyAuthConfig?.oauthClientIds?.google,
413
+ secondaryClientIds: config.auth?.oauthConfig?.google?.secondaryClientIds ?? []
414
+ },
415
+ apple: {
416
+ primaryClientId: config.auth?.oauthConfig?.apple?.primaryClientId ?? proxyAuthConfig?.oauthClientIds?.apple,
417
+ secondaryClientIds: config.auth?.oauthConfig?.apple?.secondaryClientIds ?? []
418
+ },
419
+ facebook: {
420
+ primaryClientId: config.auth?.oauthConfig?.facebook?.primaryClientId ?? proxyAuthConfig?.oauthClientIds?.facebook,
421
+ secondaryClientIds: config.auth?.oauthConfig?.facebook?.secondaryClientIds ?? []
422
+ },
423
+ x: {
424
+ primaryClientId: config.auth?.oauthConfig?.x?.primaryClientId ?? proxyAuthConfig?.oauthClientIds?.x,
425
+ secondaryClientIds: config.auth?.oauthConfig?.x?.secondaryClientIds ?? []
426
+ },
427
+ discord: {
428
+ primaryClientId: config.auth?.oauthConfig?.discord?.primaryClientId ?? proxyAuthConfig?.oauthClientIds?.discord,
429
+ secondaryClientIds: config.auth?.oauthConfig?.discord?.secondaryClientIds ?? []
430
+ }
382
431
  };
383
432
  const redirectUrl = config.auth?.oauthConfig?.oauthRedirectUri ?? proxyAuthConfig?.oauthRedirectUrl;
384
433
  // Set a default ordering for the oAuth methods
385
- const oauthOrder = config.auth?.oauthOrder ?? ["google", "apple", "x", "discord", "facebook"].filter(provider => resolvedMethods[`${provider}OauthEnabled`]);
434
+ const oauthOrder = config.ui?.authModal?.oauthOrder ?? ["google", "apple", "x", "discord", "facebook"].filter(provider => resolvedMethods[`${provider}OauthEnabled`]);
386
435
  // Set a default ordering for the overall auth methods
387
- 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);
436
+ const methodOrder = config.ui?.authModal?.methodOrder ?? [oauthOrder.length > 0 ? "socials" : null, resolvedMethods.emailOtpAuthEnabled ? "email" : null, resolvedMethods.smsOtpAuthEnabled ? "sms" : null, resolvedMethods.passkeyAuthEnabled ? "passkey" : null, resolvedMethods.walletAuthEnabled ? "wallet" : null].filter(Boolean);
388
437
  // Warn if they are trying to set auth proxy only settings directly
389
438
  if (proxyAuthConfig) {
390
439
  if (config.auth?.sessionExpirationSeconds) {
@@ -403,24 +452,33 @@ const ClientProvider = ({
403
452
  otpAlphanumeric: proxyAuthConfig?.otpAlphanumeric ?? config.auth?.otpAlphanumeric ?? true,
404
453
  otpLength: proxyAuthConfig?.otpLength ?? config.auth?.otpLength ?? "6"
405
454
  };
455
+ const scopePasskeyToUser = config.auth?.scopePasskeyToUser ?? true;
406
456
  return {
407
457
  ...config,
458
+ scopePasskeyToUser,
408
459
  // Overrides:
409
460
  auth: {
410
461
  ...config.auth,
411
462
  ...authProxyPrioSettings,
412
- methods: resolvedMethods,
413
463
  oauthConfig: {
414
464
  ...config.auth?.oauthConfig,
415
- ...resolvedClientIds,
465
+ ...resolvedOauthProviders,
416
466
  oauthRedirectUri: redirectUrl,
417
467
  // on mobile we default to true since many mobile browsers
418
468
  // (e.g. Safari) block popups
419
469
  openOauthInPage: isMobile ? true : config.auth?.oauthConfig?.openOauthInPage
420
470
  },
421
- methodOrder,
422
- oauthOrder,
423
- autoRefreshSession: config.auth?.autoRefreshSession ?? true
471
+ autoRefreshSession: config.auth?.autoRefreshSession ?? true,
472
+ scopePasskeyToUser
473
+ },
474
+ ui: {
475
+ ...config.ui,
476
+ authModal: {
477
+ ...config.ui?.authModal,
478
+ methods: resolvedMethods,
479
+ methodOrder,
480
+ oauthOrder
481
+ }
424
482
  },
425
483
  autoRefreshManagedState: config.autoRefreshManagedState ?? true,
426
484
  walletConfig: {
@@ -485,7 +543,8 @@ const ClientProvider = ({
485
543
  walletConnect: masterConfig.walletConfig.walletConnect
486
544
  })
487
545
  },
488
- defaultStamperType: masterConfig.defaultStamperType
546
+ defaultStamperType: masterConfig.defaultStamperType,
547
+ scopePasskeyToUser: masterConfig.scopePasskeyToUser ?? true
489
548
  });
490
549
  await turnkeyClient.init();
491
550
  setClient(turnkeyClient);
@@ -538,7 +597,7 @@ const ClientProvider = ({
538
597
  // specifically, if WalletConnect finishes initializing before this promise resolves,
539
598
  // `maybeRefreshWallets()` could overwrite the WalletConnect wallet state with an outdated
540
599
  // list of wallets that doesn’t yet include the WalletConnect wallets
541
- const [, wallets] = await Promise.all([maybeRefreshUser(), (() => {
600
+ const [user, wallets] = await Promise.all([maybeRefreshUser(), (() => {
542
601
  if (!masterConfig?.autoRefreshManagedState) return [];
543
602
  return fetchWallets();
544
603
  })()]);
@@ -546,6 +605,7 @@ const ClientProvider = ({
546
605
  if (wallets) {
547
606
  setWallets(prev => utils.mergeWalletsWithoutDuplicates(prev, wallets));
548
607
  }
608
+ if (client) await core.applyPasskeyScope(client, client.config.passkeyConfig, user, masterConfig?.auth?.scopePasskeyToUser);
549
609
  return;
550
610
  }
551
611
  } catch (error) {
@@ -733,7 +793,8 @@ const ClientProvider = ({
733
793
  const allSessions = await client.getAllSessions();
734
794
  setSession(session);
735
795
  setAllSessions(allSessions);
736
- await Promise.all([maybeRefreshWallets(), maybeRefreshUser()]);
796
+ const [, user] = await Promise.all([maybeRefreshWallets(), maybeRefreshUser()]);
797
+ if (client) await core.applyPasskeyScope(client, client.config.passkeyConfig, user, masterConfig?.auth?.scopePasskeyToUser);
737
798
  if (masterConfig?.auth?.verifyWalletOnSignup === true && appProofs && appProofs.length > 0 && action === sdkTypes.AuthAction.SIGNUP) {
738
799
  // On signup, if we have appProofs, verify them
739
800
  await handleVerifyAppProofs({
@@ -767,9 +828,10 @@ const ClientProvider = ({
767
828
  * @returns void
768
829
  * @throws {TurnkeyError} If there is an error during the post-logout process.
769
830
  */
770
- const handlePostLogout = sessionKey => {
831
+ const handlePostLogout = react.useCallback(async sessionKey => {
771
832
  try {
772
- clearSessionTimeouts(sessionKey ? [sessionKey, `${sessionKey}-warning`] : undefined);
833
+ const timeoutKeys = sessionKey ? [sessionKey, `${sessionKey}-warning`] : undefined;
834
+ clearSessionTimeouts(timeoutKeys);
773
835
  setAllSessions(prev => {
774
836
  if (!prev) return prev;
775
837
  if (sessionKey) {
@@ -784,16 +846,29 @@ const ClientProvider = ({
784
846
  setSession(undefined);
785
847
  setUser(undefined);
786
848
  setWallets([]);
849
+ if (client) await core.resetPasskeyScope(client, client.config.passkeyConfig);
787
850
  } catch (error) {
788
- callbacks?.onError?.(new sdkTypes.TurnkeyError(`Failed to initialize sessions`, sdkTypes.TurnkeyErrorCodes.HANDLE_POST_LOGOUT_ERROR, error));
851
+ callbacks?.onError?.(new sdkTypes.TurnkeyError(`Failed to handle post logout`, sdkTypes.TurnkeyErrorCodes.HANDLE_POST_LOGOUT_ERROR, error));
789
852
  }
790
- };
853
+ }, [callbacks, clearSessionTimeouts, client, masterConfig?.passkeyConfig]);
791
854
  const createHttpClient = react.useCallback(params => {
792
855
  if (!client) {
793
856
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
794
857
  }
795
858
  return client.createHttpClient(params);
796
859
  }, [client]);
860
+ const overrideApiKeyStamper = react.useCallback(params => {
861
+ if (!client) {
862
+ throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
863
+ }
864
+ return client.overrideApiKeyStamper(params);
865
+ }, [client]);
866
+ const overridePasskeyStamper = react.useCallback(params => {
867
+ if (!client) {
868
+ throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
869
+ }
870
+ return client.overridePasskeyStamper(params);
871
+ }, [client]);
797
872
  const getActiveSessionKey = react.useCallback(async () => {
798
873
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
799
874
  return utils.withTurnkeyErrorHandling(() => client.getActiveSessionKey(), undefined, callbacks, "Failed to get active session key");
@@ -808,10 +883,10 @@ const ClientProvider = ({
808
883
  if (!sessionKey) sessionKey = await getActiveSessionKey();
809
884
  await client.logout(params);
810
885
  // We only handle post logout if the sessionKey is defined since that means we actually logged out of a session.
811
- if (sessionKey) handlePostLogout(sessionKey);
886
+ if (sessionKey) await handlePostLogout(sessionKey);
812
887
  }, undefined, callbacks, "Failed to logout");
813
888
  return;
814
- }, [client, callbacks, getActiveSessionKey]);
889
+ }, [client, callbacks, getActiveSessionKey, handlePostLogout]);
815
890
  const getSession = react.useCallback(async params => {
816
891
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
817
892
  return utils.withTurnkeyErrorHandling(() => client.getSession(params), undefined, callbacks, "Failed to get session");
@@ -871,6 +946,7 @@ const ClientProvider = ({
871
946
  if (user) {
872
947
  setUser(user);
873
948
  }
949
+ return user;
874
950
  }, [client, callbacks, fetchUser, logout]);
875
951
  /**
876
952
  * @internal
@@ -882,7 +958,7 @@ const ClientProvider = ({
882
958
  * @returns A promise that resolves when the user is refreshed, or does nothing if auto-refresh is disabled
883
959
  */
884
960
  const maybeRefreshUser = react.useCallback(async params => {
885
- if (!masterConfig?.autoRefreshManagedState) return;
961
+ if (!masterConfig?.autoRefreshManagedState) return undefined;
886
962
  return refreshUser(params);
887
963
  }, [masterConfig, refreshUser]);
888
964
  const refreshWallets = react.useCallback(async params => {
@@ -923,11 +999,15 @@ const ClientProvider = ({
923
999
  }, [masterConfig, refreshWallets]);
924
1000
  const clearSession = react.useCallback(async params => {
925
1001
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1002
+ const activeSessionKey = await getActiveSessionKey();
926
1003
  await utils.withTurnkeyErrorHandling(async () => client.clearSession(params), undefined, callbacks, "Failed to clear session");
927
- const sessionKey = params?.sessionKey ?? (await getActiveSessionKey());
1004
+ const sessionKey = params?.sessionKey ?? activeSessionKey;
928
1005
  if (!sessionKey) return;
929
- if (!params?.sessionKey) {
1006
+ if (!params?.sessionKey || params.sessionKey === activeSessionKey) {
930
1007
  setSession(undefined);
1008
+ setUser(undefined);
1009
+ setWallets([]);
1010
+ if (client) await core.resetPasskeyScope(client, client.config.passkeyConfig);
931
1011
  }
932
1012
  clearSessionTimeouts([sessionKey]);
933
1013
  // clear only the cleared session from allSessions
@@ -939,7 +1019,7 @@ const ClientProvider = ({
939
1019
  }
940
1020
  setAllSessions(newAllSessions);
941
1021
  return;
942
- }, [client, callbacks, getActiveSessionKey, allSessions]);
1022
+ }, [client, callbacks, getActiveSessionKey, allSessions, masterConfig?.passkeyConfig]);
943
1023
  /**
944
1024
  * @internal
945
1025
  * Schedules a session expiration and warning timer for the given session key.
@@ -1609,17 +1689,20 @@ const ClientProvider = ({
1609
1689
  const addPasskey = react.useCallback(async params => {
1610
1690
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1611
1691
  const res = await utils.withTurnkeyErrorHandling(() => client.addPasskey(params), () => logout(), callbacks, "Failed to add passkey");
1612
- if (res) await maybeRefreshUser({
1613
- stampWith: params?.stampWith,
1614
- ...(params?.organizationId && {
1615
- organizationId: params.organizationId
1616
- }),
1617
- ...(params?.userId && {
1618
- userId: params.userId
1619
- })
1620
- });
1692
+ if (res) {
1693
+ const updatedUser = await maybeRefreshUser({
1694
+ stampWith: params?.stampWith,
1695
+ ...(params?.organizationId && {
1696
+ organizationId: params.organizationId
1697
+ }),
1698
+ ...(params?.userId && {
1699
+ userId: params.userId
1700
+ })
1701
+ });
1702
+ await core.applyPasskeyScope(client, client.config.passkeyConfig, updatedUser, masterConfig?.auth?.scopePasskeyToUser);
1703
+ }
1621
1704
  return res;
1622
- }, [client, callbacks, logout, maybeRefreshUser]);
1705
+ }, [client, callbacks, logout, maybeRefreshUser, masterConfig?.passkeyConfig, masterConfig?.auth?.scopePasskeyToUser]);
1623
1706
  const removePasskeys = react.useCallback(async params => {
1624
1707
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1625
1708
  const res = await utils.withTurnkeyErrorHandling(() => client.removePasskeys(params), () => logout(), callbacks, "Failed to remove passkeys");
@@ -1727,15 +1810,20 @@ const ClientProvider = ({
1727
1810
  const allSessions = await getAllSessions();
1728
1811
  setSession(session);
1729
1812
  setAllSessions(allSessions);
1730
- await Promise.all([maybeRefreshWallets(), maybeRefreshUser()]);
1731
- }, [client, callbacks, logout, getActiveSessionKey, getSession, scheduleSessionExpiration, getAllSessions, maybeRefreshWallets, maybeRefreshUser]);
1813
+ const nextUser = await maybeRefreshUser();
1814
+ await maybeRefreshWallets();
1815
+ if (client) await core.applyPasskeyScope(client, client.config.passkeyConfig, nextUser, masterConfig?.auth?.scopePasskeyToUser);
1816
+ }, [client, callbacks, logout, getActiveSessionKey, getSession, scheduleSessionExpiration, getAllSessions, masterConfig?.passkeyConfig, masterConfig?.auth?.scopePasskeyToUser, maybeRefreshWallets, maybeRefreshUser]);
1732
1817
  const clearAllSessions = react.useCallback(async () => {
1733
1818
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1734
1819
  setSession(undefined);
1735
1820
  setAllSessions(undefined);
1821
+ setUser(undefined);
1822
+ setWallets([]);
1736
1823
  clearSessionTimeouts();
1737
- return await utils.withTurnkeyErrorHandling(() => client.clearAllSessions(), () => logout(), callbacks, "Failed to clear all sessions");
1738
- }, [client, callbacks, logout, clearSessionTimeouts]);
1824
+ await utils.withTurnkeyErrorHandling(() => client.clearAllSessions(), () => logout(), callbacks, "Failed to clear all sessions");
1825
+ await core.resetPasskeyScope(client, client.config.passkeyConfig);
1826
+ }, [client, callbacks, logout, clearSessionTimeouts, masterConfig?.passkeyConfig]);
1739
1827
  const setActiveSession = react.useCallback(async params => {
1740
1828
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1741
1829
  const session = await utils.withTurnkeyErrorHandling(() => client.getSession({
@@ -1748,10 +1836,12 @@ const ClientProvider = ({
1748
1836
  await utils.withTurnkeyErrorHandling(() => client.setActiveSession(params), () => logout(), callbacks, "Failed to set active session");
1749
1837
  setSession(session);
1750
1838
  await utils.withTurnkeyErrorHandling(async () => {
1751
- await Promise.all([maybeRefreshWallets(), maybeRefreshUser()]);
1839
+ const nextUser = await maybeRefreshUser();
1840
+ await maybeRefreshWallets();
1841
+ if (client) await core.applyPasskeyScope(client, client.config.passkeyConfig, nextUser, masterConfig?.auth?.scopePasskeyToUser);
1752
1842
  }, () => logout(), callbacks, "Failed to refresh data after setting active session");
1753
1843
  return;
1754
- }, [client, callbacks, logout, getSession, maybeRefreshWallets, maybeRefreshUser]);
1844
+ }, [client, callbacks, logout, getSession, masterConfig?.passkeyConfig, masterConfig?.auth?.scopePasskeyToUser, maybeRefreshWallets, maybeRefreshUser]);
1755
1845
  const clearUnusedKeyPairs = react.useCallback(async () => {
1756
1846
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1757
1847
  return utils.withTurnkeyErrorHandling(() => client.clearUnusedKeyPairs(), undefined, callbacks, "Failed to clear unused key pairs");
@@ -1760,6 +1850,14 @@ const ClientProvider = ({
1760
1850
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1761
1851
  return utils.withTurnkeyErrorHandling(() => client.createApiKeyPair(params), undefined, callbacks, "Failed to create API key pair");
1762
1852
  }, [client, callbacks]);
1853
+ const deleteApiKeyPair = react.useCallback(async params => {
1854
+ if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1855
+ return utils.withTurnkeyErrorHandling(() => client.deleteApiKeyPair(params), undefined, callbacks, "Failed to delete API key pair");
1856
+ }, [client, callbacks]);
1857
+ const signWithApiKey = react.useCallback(async params => {
1858
+ if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1859
+ return utils.withTurnkeyErrorHandling(() => client.signWithApiKey(params), undefined, callbacks, "Failed to sign with API key");
1860
+ }, [client, callbacks]);
1763
1861
  const getProxyAuthConfig = react.useCallback(async () => {
1764
1862
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1765
1863
  return utils.withTurnkeyErrorHandling(() => client.getProxyAuthConfig(), undefined, callbacks, "Failed to get proxy auth config");
@@ -1774,10 +1872,12 @@ const ClientProvider = ({
1774
1872
  }, [client, callbacks, logout]);
1775
1873
  const handleDiscordOauth = react.useCallback(async params => {
1776
1874
  const {
1777
- clientId = masterConfig?.auth?.oauthConfig?.discordClientId,
1875
+ primaryClientId = masterConfig?.auth?.oauthConfig?.discord?.primaryClientId,
1876
+ secondaryClientIds = masterConfig?.auth?.oauthConfig?.discord?.secondaryClientIds ?? [],
1778
1877
  openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
1779
1878
  additionalState: additionalParameters
1780
1879
  } = params || {};
1880
+ const clientId = primaryClientId;
1781
1881
  const provider = sdkTypes.OAuthProviders.DISCORD;
1782
1882
  if (!masterConfig) {
1783
1883
  throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
@@ -1800,10 +1900,10 @@ const ClientProvider = ({
1800
1900
  const {
1801
1901
  verifier,
1802
1902
  codeChallenge
1803
- } = await oauth.generateChallengePair();
1804
- oauth.storePKCEVerifier(provider, verifier);
1903
+ } = await pkce.generateChallengePair();
1904
+ storage.storePKCEVerifier(provider, verifier);
1805
1905
  // Build OAuth URL
1806
- const authUrl = oauth.buildOAuthUrl({
1906
+ const authUrl = url.buildOAuthUrl({
1807
1907
  provider,
1808
1908
  clientId,
1809
1909
  redirectUri,
@@ -1815,12 +1915,12 @@ const ClientProvider = ({
1815
1915
  });
1816
1916
  if (openInPage) {
1817
1917
  // Remainder of logic will occur in completeRedirectOauth
1818
- return oauth.redirectToOAuthProvider(authUrl);
1918
+ return url.redirectToOAuthProvider(authUrl);
1819
1919
  }
1820
1920
  // Popup flow
1821
- const authWindow = oauth.openOAuthPopup();
1921
+ const authWindow = url.openOAuthPopup();
1822
1922
  if (!authWindow) {
1823
- throw new Error(`Failed to open ${oauth.capitalizeProviderName(provider)} login window.`);
1923
+ throw new Error(`Failed to open ${helpers.capitalizeProviderName(provider)} login window.`);
1824
1924
  }
1825
1925
  authWindow.location.href = authUrl;
1826
1926
  return new Promise((resolve, reject) => {
@@ -1831,18 +1931,30 @@ const ClientProvider = ({
1831
1931
  reject(new Error("Authentication window was closed."));
1832
1932
  return;
1833
1933
  }
1834
- const url = authWindow.location.href || "";
1835
- if (url.startsWith(window.location.origin)) {
1836
- const result = oauth.parseOAuthResponse(url, provider);
1934
+ const url$1 = authWindow.location.href || "";
1935
+ if (url$1.startsWith(window.location.origin)) {
1936
+ const result = url.parseOAuthResponse(url$1, provider);
1837
1937
  if (result) {
1838
1938
  authWindow.close();
1839
1939
  clearInterval(interval);
1840
- oauth.completeOAuthPopup({
1940
+ completion.completeOAuthPopup({
1841
1941
  provider,
1842
1942
  publicKey,
1843
1943
  result,
1844
1944
  callbacks,
1845
- completeOauth,
1945
+ completeOauth: completionParams => {
1946
+ const existingCreateSubOrgParams = masterConfig?.auth?.createSuborgParams?.oauth;
1947
+ const secondaryProviders = core.buildSecondaryOauthProviders(completionParams.oidcToken, completionParams.providerName ?? provider, secondaryClientIds);
1948
+ return completeOauth({
1949
+ ...completionParams,
1950
+ ...(secondaryProviders.length > 0 && {
1951
+ createSubOrgParams: {
1952
+ ...existingCreateSubOrgParams,
1953
+ oauthProviders: [...(existingCreateSubOrgParams?.oauthProviders ?? []), ...secondaryProviders]
1954
+ }
1955
+ })
1956
+ });
1957
+ },
1846
1958
  onOauthSuccess: params?.onOauthSuccess,
1847
1959
  exchangeCodeForToken: async codeVerifier => {
1848
1960
  const resp = await client?.httpClient.proxyOAuth2Authenticate({
@@ -1869,10 +1981,12 @@ const ClientProvider = ({
1869
1981
  }, [client, callbacks, completeOauth, createApiKeyPair, masterConfig]);
1870
1982
  const handleXOauth = react.useCallback(async params => {
1871
1983
  const {
1872
- clientId = masterConfig?.auth?.oauthConfig?.xClientId,
1984
+ primaryClientId = masterConfig?.auth?.oauthConfig?.x?.primaryClientId,
1985
+ secondaryClientIds = masterConfig?.auth?.oauthConfig?.x?.secondaryClientIds ?? [],
1873
1986
  openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
1874
1987
  additionalState: additionalParameters
1875
1988
  } = params || {};
1989
+ const clientId = primaryClientId;
1876
1990
  const provider = sdkTypes.OAuthProviders.X;
1877
1991
  if (!masterConfig) {
1878
1992
  throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
@@ -1895,10 +2009,10 @@ const ClientProvider = ({
1895
2009
  const {
1896
2010
  verifier,
1897
2011
  codeChallenge
1898
- } = await oauth.generateChallengePair();
1899
- oauth.storePKCEVerifier(provider, verifier);
2012
+ } = await pkce.generateChallengePair();
2013
+ storage.storePKCEVerifier(provider, verifier);
1900
2014
  // Build OAuth URL
1901
- const authUrl = oauth.buildOAuthUrl({
2015
+ const authUrl = url.buildOAuthUrl({
1902
2016
  provider,
1903
2017
  clientId,
1904
2018
  redirectUri,
@@ -1910,12 +2024,12 @@ const ClientProvider = ({
1910
2024
  });
1911
2025
  if (openInPage) {
1912
2026
  // Remainder of logic will occur in completeRedirectOauth
1913
- return oauth.redirectToOAuthProvider(authUrl);
2027
+ return url.redirectToOAuthProvider(authUrl);
1914
2028
  }
1915
2029
  // Popup flow
1916
- const authWindow = oauth.openOAuthPopup();
2030
+ const authWindow = url.openOAuthPopup();
1917
2031
  if (!authWindow) {
1918
- throw new Error(`Failed to open ${oauth.capitalizeProviderName(provider)} login window.`);
2032
+ throw new Error(`Failed to open ${helpers.capitalizeProviderName(provider)} login window.`);
1919
2033
  }
1920
2034
  authWindow.location.href = authUrl;
1921
2035
  return new Promise((resolve, reject) => {
@@ -1926,18 +2040,30 @@ const ClientProvider = ({
1926
2040
  reject(new Error("Authentication window was closed."));
1927
2041
  return;
1928
2042
  }
1929
- const url = authWindow.location.href || "";
1930
- if (url.startsWith(window.location.origin)) {
1931
- const result = oauth.parseOAuthResponse(url, provider);
2043
+ const url$1 = authWindow.location.href || "";
2044
+ if (url$1.startsWith(window.location.origin)) {
2045
+ const result = url.parseOAuthResponse(url$1, provider);
1932
2046
  if (result) {
1933
2047
  authWindow.close();
1934
2048
  clearInterval(interval);
1935
- oauth.completeOAuthPopup({
2049
+ completion.completeOAuthPopup({
1936
2050
  provider,
1937
2051
  publicKey,
1938
2052
  result,
1939
2053
  callbacks,
1940
- completeOauth,
2054
+ completeOauth: completionParams => {
2055
+ const existingCreateSubOrgParams = masterConfig?.auth?.createSuborgParams?.oauth;
2056
+ const secondaryProviders = core.buildSecondaryOauthProviders(completionParams.oidcToken, completionParams.providerName ?? provider, secondaryClientIds);
2057
+ return completeOauth({
2058
+ ...completionParams,
2059
+ ...(secondaryProviders.length > 0 && {
2060
+ createSubOrgParams: {
2061
+ ...existingCreateSubOrgParams,
2062
+ oauthProviders: [...(existingCreateSubOrgParams?.oauthProviders ?? []), ...secondaryProviders]
2063
+ }
2064
+ })
2065
+ });
2066
+ },
1941
2067
  onOauthSuccess: params?.onOauthSuccess,
1942
2068
  exchangeCodeForToken: async codeVerifier => {
1943
2069
  const resp = await client?.httpClient.proxyOAuth2Authenticate({
@@ -1964,10 +2090,12 @@ const ClientProvider = ({
1964
2090
  }, [client, callbacks, completeOauth, createApiKeyPair, masterConfig]);
1965
2091
  const handleGoogleOauth = react.useCallback(async params => {
1966
2092
  const {
1967
- clientId = masterConfig?.auth?.oauthConfig?.googleClientId,
2093
+ primaryClientId = masterConfig?.auth?.oauthConfig?.google?.primaryClientId,
2094
+ secondaryClientIds = masterConfig?.auth?.oauthConfig?.google?.secondaryClientIds ?? [],
1968
2095
  openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
1969
2096
  additionalState: additionalParameters
1970
2097
  } = params || {};
2098
+ const clientId = primaryClientId;
1971
2099
  const provider = sdkTypes.OAuthProviders.GOOGLE;
1972
2100
  if (!masterConfig) {
1973
2101
  throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
@@ -1988,7 +2116,7 @@ const ClientProvider = ({
1988
2116
  }
1989
2117
  const nonce = utils$1.bytesToHex(sha2.sha256(publicKey));
1990
2118
  // Build OAuth URL
1991
- const authUrl = oauth.buildOAuthUrl({
2119
+ const authUrl = url.buildOAuthUrl({
1992
2120
  provider,
1993
2121
  clientId,
1994
2122
  redirectUri,
@@ -1999,12 +2127,12 @@ const ClientProvider = ({
1999
2127
  });
2000
2128
  if (openInPage) {
2001
2129
  // Remainder of logic will occur in completeRedirectOauth
2002
- return oauth.redirectToOAuthProvider(authUrl);
2130
+ return url.redirectToOAuthProvider(authUrl);
2003
2131
  }
2004
2132
  // Popup flow
2005
- const authWindow = oauth.openOAuthPopup();
2133
+ const authWindow = url.openOAuthPopup();
2006
2134
  if (!authWindow) {
2007
- throw new Error(`Failed to open ${oauth.capitalizeProviderName(provider)} login window.`);
2135
+ throw new Error(`Failed to open ${helpers.capitalizeProviderName(provider)} login window.`);
2008
2136
  }
2009
2137
  authWindow.location.href = authUrl;
2010
2138
  return new Promise((resolve, reject) => {
@@ -2015,18 +2143,30 @@ const ClientProvider = ({
2015
2143
  reject(new Error("Authentication window was closed."));
2016
2144
  return;
2017
2145
  }
2018
- const url = authWindow.location.href || "";
2019
- if (url.startsWith(window.location.origin)) {
2020
- const result = oauth.parseOAuthResponse(url, provider);
2146
+ const url$1 = authWindow.location.href || "";
2147
+ if (url$1.startsWith(window.location.origin)) {
2148
+ const result = url.parseOAuthResponse(url$1, provider);
2021
2149
  if (result) {
2022
2150
  authWindow.close();
2023
2151
  clearInterval(interval);
2024
- oauth.completeOAuthPopup({
2152
+ completion.completeOAuthPopup({
2025
2153
  provider,
2026
2154
  publicKey,
2027
2155
  result,
2028
2156
  callbacks,
2029
- completeOauth,
2157
+ completeOauth: completionParams => {
2158
+ const existingCreateSubOrgParams = masterConfig?.auth?.createSuborgParams?.oauth;
2159
+ const secondaryProviders = core.buildSecondaryOauthProviders(completionParams.oidcToken, completionParams.providerName ?? provider, secondaryClientIds);
2160
+ return completeOauth({
2161
+ ...completionParams,
2162
+ ...(secondaryProviders.length > 0 && {
2163
+ createSubOrgParams: {
2164
+ ...existingCreateSubOrgParams,
2165
+ oauthProviders: [...(existingCreateSubOrgParams?.oauthProviders ?? []), ...secondaryProviders]
2166
+ }
2167
+ })
2168
+ });
2169
+ },
2030
2170
  onOauthSuccess: params?.onOauthSuccess
2031
2171
  }).then(() => resolve()).catch(reject);
2032
2172
  }
@@ -2042,10 +2182,12 @@ const ClientProvider = ({
2042
2182
  }, [callbacks, completeOauth, createApiKeyPair, masterConfig]);
2043
2183
  const handleAppleOauth = react.useCallback(async params => {
2044
2184
  const {
2045
- clientId = masterConfig?.auth?.oauthConfig?.appleClientId,
2185
+ primaryClientId = masterConfig?.auth?.oauthConfig?.apple?.primaryClientId,
2186
+ secondaryClientIds = masterConfig?.auth?.oauthConfig?.apple?.secondaryClientIds ?? [],
2046
2187
  openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
2047
2188
  additionalState: additionalParameters
2048
2189
  } = params || {};
2190
+ const clientId = primaryClientId;
2049
2191
  const provider = sdkTypes.OAuthProviders.APPLE;
2050
2192
  if (!masterConfig) {
2051
2193
  throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
@@ -2066,7 +2208,7 @@ const ClientProvider = ({
2066
2208
  }
2067
2209
  const nonce = utils$1.bytesToHex(sha2.sha256(publicKey));
2068
2210
  // Build OAuth URL
2069
- const authUrl = oauth.buildOAuthUrl({
2211
+ const authUrl = url.buildOAuthUrl({
2070
2212
  provider,
2071
2213
  clientId,
2072
2214
  redirectUri,
@@ -2077,12 +2219,12 @@ const ClientProvider = ({
2077
2219
  });
2078
2220
  if (openInPage) {
2079
2221
  // Remainder of logic will occur in completeRedirectOauth
2080
- return oauth.redirectToOAuthProvider(authUrl);
2222
+ return url.redirectToOAuthProvider(authUrl);
2081
2223
  }
2082
2224
  // Popup flow
2083
- const authWindow = oauth.openOAuthPopup();
2225
+ const authWindow = url.openOAuthPopup();
2084
2226
  if (!authWindow) {
2085
- throw new Error(`Failed to open ${oauth.capitalizeProviderName(provider)} login window.`);
2227
+ throw new Error(`Failed to open ${helpers.capitalizeProviderName(provider)} login window.`);
2086
2228
  }
2087
2229
  authWindow.location.href = authUrl;
2088
2230
  return new Promise((resolve, reject) => {
@@ -2093,18 +2235,30 @@ const ClientProvider = ({
2093
2235
  reject(new Error("Authentication window was closed."));
2094
2236
  return;
2095
2237
  }
2096
- const url = authWindow.location.href || "";
2097
- if (url.startsWith(window.location.origin)) {
2098
- const result = oauth.parseOAuthResponse(url, provider);
2238
+ const url$1 = authWindow.location.href || "";
2239
+ if (url$1.startsWith(window.location.origin)) {
2240
+ const result = url.parseOAuthResponse(url$1, provider);
2099
2241
  if (result) {
2100
2242
  authWindow.close();
2101
2243
  clearInterval(interval);
2102
- oauth.completeOAuthPopup({
2244
+ completion.completeOAuthPopup({
2103
2245
  provider,
2104
2246
  publicKey,
2105
2247
  result,
2106
2248
  callbacks,
2107
- completeOauth,
2249
+ completeOauth: completionParams => {
2250
+ const existingCreateSubOrgParams = masterConfig?.auth?.createSuborgParams?.oauth;
2251
+ const secondaryProviders = core.buildSecondaryOauthProviders(completionParams.oidcToken, completionParams.providerName ?? provider, secondaryClientIds);
2252
+ return completeOauth({
2253
+ ...completionParams,
2254
+ ...(secondaryProviders.length > 0 && {
2255
+ createSubOrgParams: {
2256
+ ...existingCreateSubOrgParams,
2257
+ oauthProviders: [...(existingCreateSubOrgParams?.oauthProviders ?? []), ...secondaryProviders]
2258
+ }
2259
+ })
2260
+ });
2261
+ },
2108
2262
  onOauthSuccess: params?.onOauthSuccess
2109
2263
  }).then(() => resolve()).catch(reject);
2110
2264
  }
@@ -2120,10 +2274,12 @@ const ClientProvider = ({
2120
2274
  }, [callbacks, completeOauth, createApiKeyPair, masterConfig]);
2121
2275
  const handleFacebookOauth = react.useCallback(async params => {
2122
2276
  const {
2123
- clientId = masterConfig?.auth?.oauthConfig?.facebookClientId,
2277
+ primaryClientId = masterConfig?.auth?.oauthConfig?.facebook?.primaryClientId,
2278
+ secondaryClientIds = masterConfig?.auth?.oauthConfig?.facebook?.secondaryClientIds ?? [],
2124
2279
  openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
2125
2280
  additionalState: additionalParameters
2126
2281
  } = params || {};
2282
+ const clientId = primaryClientId;
2127
2283
  const provider = sdkTypes.OAuthProviders.FACEBOOK;
2128
2284
  if (!masterConfig) {
2129
2285
  throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
@@ -2146,10 +2302,10 @@ const ClientProvider = ({
2146
2302
  const {
2147
2303
  verifier,
2148
2304
  codeChallenge
2149
- } = await oauth.generateChallengePair();
2150
- oauth.storePKCEVerifier(provider, verifier);
2305
+ } = await pkce.generateChallengePair();
2306
+ storage.storePKCEVerifier(provider, verifier);
2151
2307
  // Build OAuth URL
2152
- const authUrl = oauth.buildOAuthUrl({
2308
+ const authUrl = url.buildOAuthUrl({
2153
2309
  provider,
2154
2310
  clientId,
2155
2311
  redirectUri,
@@ -2161,12 +2317,12 @@ const ClientProvider = ({
2161
2317
  });
2162
2318
  if (openInPage) {
2163
2319
  // Remainder of logic will occur in completeRedirectOauth
2164
- return oauth.redirectToOAuthProvider(authUrl);
2320
+ return url.redirectToOAuthProvider(authUrl);
2165
2321
  }
2166
2322
  // Popup flow
2167
- const authWindow = oauth.openOAuthPopup();
2323
+ const authWindow = url.openOAuthPopup();
2168
2324
  if (!authWindow) {
2169
- throw new Error(`Failed to open ${oauth.capitalizeProviderName(provider)} login window.`);
2325
+ throw new Error(`Failed to open ${helpers.capitalizeProviderName(provider)} login window.`);
2170
2326
  }
2171
2327
  authWindow.location.href = authUrl;
2172
2328
  return new Promise((resolve, reject) => {
@@ -2177,21 +2333,33 @@ const ClientProvider = ({
2177
2333
  reject(new Error("Authentication window was closed."));
2178
2334
  return;
2179
2335
  }
2180
- const url = authWindow.location.href || "";
2181
- if (url.startsWith(window.location.origin)) {
2182
- const result = oauth.parseOAuthResponse(url, provider);
2336
+ const url$1 = authWindow.location.href || "";
2337
+ if (url$1.startsWith(window.location.origin)) {
2338
+ const result = url.parseOAuthResponse(url$1, provider);
2183
2339
  if (result) {
2184
2340
  authWindow.close();
2185
2341
  clearInterval(interval);
2186
- oauth.completeOAuthPopup({
2342
+ completion.completeOAuthPopup({
2187
2343
  provider,
2188
2344
  publicKey,
2189
2345
  result,
2190
2346
  callbacks,
2191
- completeOauth,
2347
+ completeOauth: completionParams => {
2348
+ const existingCreateSubOrgParams = masterConfig?.auth?.createSuborgParams?.oauth;
2349
+ const secondaryProviders = core.buildSecondaryOauthProviders(completionParams.oidcToken, completionParams.providerName ?? provider, secondaryClientIds);
2350
+ return completeOauth({
2351
+ ...completionParams,
2352
+ ...(secondaryProviders.length > 0 && {
2353
+ createSubOrgParams: {
2354
+ ...existingCreateSubOrgParams,
2355
+ oauthProviders: [...(existingCreateSubOrgParams?.oauthProviders ?? []), ...secondaryProviders]
2356
+ }
2357
+ })
2358
+ });
2359
+ },
2192
2360
  onOauthSuccess: params?.onOauthSuccess,
2193
2361
  exchangeCodeForToken: async codeVerifier => {
2194
- const tokenData = await oauth.exchangeFacebookCodeForToken(clientId, redirectUri, result.authCode, codeVerifier);
2362
+ const tokenData = await pkce.exchangeFacebookCodeForToken(clientId, redirectUri, result.authCode, codeVerifier);
2195
2363
  return tokenData.id_token;
2196
2364
  }
2197
2365
  }).then(() => resolve()).catch(reject);
@@ -2492,9 +2660,6 @@ const ClientProvider = ({
2492
2660
  if (!masterConfig) {
2493
2661
  throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.CONFIG_NOT_INITIALIZED);
2494
2662
  }
2495
- if (!masterConfig.auth?.methods?.smsOtpAuthEnabled) {
2496
- throw new sdkTypes.TurnkeyError("SMS OTP authentication is not enabled in the configuration.", sdkTypes.TurnkeyErrorCodes.AUTH_METHOD_NOT_ENABLED);
2497
- }
2498
2663
  const s = await getSession();
2499
2664
  const organizationId = params?.organizationId || s?.organizationId;
2500
2665
  if (!organizationId) {
@@ -2544,7 +2709,10 @@ const ClientProvider = ({
2544
2709
  });
2545
2710
  }), () => logout());
2546
2711
  } else {
2547
- const otpId = await initOtp({
2712
+ const {
2713
+ otpId,
2714
+ otpEncryptionTargetBundle
2715
+ } = await initOtp({
2548
2716
  otpType: core.OtpType.Sms,
2549
2717
  contact: params.phoneNumber
2550
2718
  });
@@ -2555,6 +2723,7 @@ const ClientProvider = ({
2555
2723
  otpType: core.OtpType.Sms,
2556
2724
  contact: params.phoneNumber,
2557
2725
  otpId: otpId,
2726
+ otpEncryptionTargetBundle: otpEncryptionTargetBundle,
2558
2727
  otpLength: masterConfig.auth?.otpLength !== undefined ? Number(masterConfig.auth.otpLength) : undefined,
2559
2728
  alphanumeric: masterConfig.auth?.otpAlphanumeric,
2560
2729
  onContinue: async otpCode => {
@@ -2564,8 +2733,7 @@ const ClientProvider = ({
2564
2733
  } = await verifyOtp({
2565
2734
  otpId,
2566
2735
  otpCode,
2567
- contact: params.phoneNumber,
2568
- otpType: core.OtpType.Sms
2736
+ otpEncryptionTargetBundle
2569
2737
  });
2570
2738
  const res = await updateUserPhoneNumber({
2571
2739
  phoneNumber: params.phoneNumber,
@@ -2662,7 +2830,10 @@ const ClientProvider = ({
2662
2830
  });
2663
2831
  }), () => logout());
2664
2832
  } else {
2665
- const otpId = await initOtp({
2833
+ const {
2834
+ otpId,
2835
+ otpEncryptionTargetBundle
2836
+ } = await initOtp({
2666
2837
  otpType: core.OtpType.Email,
2667
2838
  contact: params.email
2668
2839
  });
@@ -2673,6 +2844,7 @@ const ClientProvider = ({
2673
2844
  otpType: core.OtpType.Email,
2674
2845
  contact: params.email,
2675
2846
  otpId: otpId,
2847
+ otpEncryptionTargetBundle: otpEncryptionTargetBundle,
2676
2848
  otpLength: masterConfig?.auth?.otpLength !== undefined ? Number(masterConfig.auth.otpLength) : undefined,
2677
2849
  alphanumeric: masterConfig?.auth?.otpAlphanumeric,
2678
2850
  onContinue: async otpCode => {
@@ -2682,8 +2854,7 @@ const ClientProvider = ({
2682
2854
  } = await verifyOtp({
2683
2855
  otpId,
2684
2856
  otpCode,
2685
- contact: params.email,
2686
- otpType: core.OtpType.Email
2857
+ otpEncryptionTargetBundle
2687
2858
  });
2688
2859
  const res = await updateUserEmail({
2689
2860
  email: params.email,
@@ -2777,7 +2948,10 @@ const ClientProvider = ({
2777
2948
  });
2778
2949
  }), () => logout());
2779
2950
  } else {
2780
- const otpId = await initOtp({
2951
+ const {
2952
+ otpId,
2953
+ otpEncryptionTargetBundle
2954
+ } = await initOtp({
2781
2955
  otpType: core.OtpType.Email,
2782
2956
  contact: params.email
2783
2957
  });
@@ -2788,6 +2962,7 @@ const ClientProvider = ({
2788
2962
  otpType: core.OtpType.Email,
2789
2963
  contact: params.email,
2790
2964
  otpId: otpId,
2965
+ otpEncryptionTargetBundle: otpEncryptionTargetBundle,
2791
2966
  otpLength: masterConfig?.auth?.otpLength !== undefined ? Number(masterConfig.auth.otpLength) : undefined,
2792
2967
  alphanumeric: masterConfig?.auth?.otpAlphanumeric,
2793
2968
  onContinue: async otpCode => {
@@ -2797,8 +2972,7 @@ const ClientProvider = ({
2797
2972
  } = await verifyOtp({
2798
2973
  otpId,
2799
2974
  otpCode,
2800
- contact: params.email,
2801
- otpType: core.OtpType.Email
2975
+ otpEncryptionTargetBundle
2802
2976
  });
2803
2977
  const res = await updateUserEmail({
2804
2978
  email: params.email,
@@ -2842,9 +3016,6 @@ const ClientProvider = ({
2842
3016
  if (!masterConfig) {
2843
3017
  throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.CONFIG_NOT_INITIALIZED);
2844
3018
  }
2845
- if (!masterConfig.auth?.methods?.smsOtpAuthEnabled) {
2846
- throw new sdkTypes.TurnkeyError("SMS OTP authentication is not enabled in the configuration.", sdkTypes.TurnkeyErrorCodes.AUTH_METHOD_NOT_ENABLED);
2847
- }
2848
3019
  const s = await getSession();
2849
3020
  const organizationId = params?.organizationId || s?.organizationId;
2850
3021
  if (!organizationId) {
@@ -2896,7 +3067,10 @@ const ClientProvider = ({
2896
3067
  });
2897
3068
  }), () => logout(), callbacks, "Failed to add phone number");
2898
3069
  } else {
2899
- const otpId = await initOtp({
3070
+ const {
3071
+ otpId,
3072
+ otpEncryptionTargetBundle
3073
+ } = await initOtp({
2900
3074
  otpType: core.OtpType.Sms,
2901
3075
  contact: params.phoneNumber
2902
3076
  });
@@ -2907,6 +3081,7 @@ const ClientProvider = ({
2907
3081
  otpType: core.OtpType.Sms,
2908
3082
  contact: params.phoneNumber,
2909
3083
  otpId: otpId,
3084
+ otpEncryptionTargetBundle: otpEncryptionTargetBundle,
2910
3085
  otpLength: masterConfig.auth?.otpLength !== undefined ? Number(masterConfig.auth.otpLength) : undefined,
2911
3086
  alphanumeric: masterConfig?.auth?.otpAlphanumeric,
2912
3087
  onContinue: async otpCode => {
@@ -2916,8 +3091,7 @@ const ClientProvider = ({
2916
3091
  } = await verifyOtp({
2917
3092
  otpId,
2918
3093
  otpCode,
2919
- contact: params.phoneNumber,
2920
- otpType: core.OtpType.Sms
3094
+ otpEncryptionTargetBundle
2921
3095
  });
2922
3096
  const res = await updateUserPhoneNumber({
2923
3097
  phoneNumber: params.phoneNumber,
@@ -3110,6 +3284,29 @@ const ClientProvider = ({
3110
3284
  successPageDuration = 2000,
3111
3285
  openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false
3112
3286
  } = params;
3287
+ // Resolve the provider's per-provider config, then apply per-call overrides.
3288
+ let providerConfig;
3289
+ switch (providerName) {
3290
+ case sdkTypes.OAuthProviders.DISCORD:
3291
+ providerConfig = masterConfig?.auth?.oauthConfig?.discord;
3292
+ break;
3293
+ case sdkTypes.OAuthProviders.X:
3294
+ providerConfig = masterConfig?.auth?.oauthConfig?.x;
3295
+ break;
3296
+ case sdkTypes.OAuthProviders.GOOGLE:
3297
+ providerConfig = masterConfig?.auth?.oauthConfig?.google;
3298
+ break;
3299
+ case sdkTypes.OAuthProviders.APPLE:
3300
+ providerConfig = masterConfig?.auth?.oauthConfig?.apple;
3301
+ break;
3302
+ case sdkTypes.OAuthProviders.FACEBOOK:
3303
+ providerConfig = masterConfig?.auth?.oauthConfig?.facebook;
3304
+ break;
3305
+ default:
3306
+ providerConfig = undefined;
3307
+ }
3308
+ const primaryClientId = params.primaryClientId ?? providerConfig?.primaryClientId;
3309
+ const secondaryClientIds = params.secondaryClientIds ?? providerConfig?.secondaryClientIds ?? [];
3113
3310
  const s = await getSession();
3114
3311
  const organizationId = params?.organizationId || s?.organizationId;
3115
3312
  if (!organizationId) {
@@ -3122,9 +3319,13 @@ const ClientProvider = ({
3122
3319
  return new Promise(async (resolve, reject) => {
3123
3320
  try {
3124
3321
  const onOauthSuccess = async params => {
3322
+ const oidcClaims = core.buildSecondaryOidcClaims(params.oidcToken, secondaryClientIds);
3125
3323
  await addOauthProvider({
3126
3324
  providerName: params.providerName,
3127
3325
  oidcToken: params.oidcToken,
3326
+ ...(oidcClaims.length > 0 && {
3327
+ oidcClaims
3328
+ }),
3128
3329
  stampWith,
3129
3330
  organizationId,
3130
3331
  userId
@@ -3144,7 +3345,7 @@ const ClientProvider = ({
3144
3345
  });
3145
3346
  };
3146
3347
  if (openInPage) {
3147
- oauth.storeOAuthAddProviderMetadata({
3348
+ storage.storeOAuthAddProviderMetadata({
3148
3349
  organizationId,
3149
3350
  userId,
3150
3351
  ...(stampWith && {
@@ -3154,71 +3355,47 @@ const ClientProvider = ({
3154
3355
  });
3155
3356
  }
3156
3357
  const additionalState = openInPage ? {
3157
- oauthIntent: oauth.OAUTH_INTENT_ADD_PROVIDER,
3358
+ oauthIntent: storage.OAUTH_INTENT_ADD_PROVIDER,
3158
3359
  ...(successPageDuration > 0 && {
3159
3360
  openModal: "true"
3160
3361
  })
3161
3362
  } : undefined;
3363
+ // Forward overrides so the inner handler uses the same client IDs.
3364
+ const sharedOauthParams = {
3365
+ openInPage,
3366
+ ...(primaryClientId && {
3367
+ primaryClientId
3368
+ }),
3369
+ ...(secondaryClientIds.length > 0 && {
3370
+ secondaryClientIds
3371
+ }),
3372
+ ...(additionalState && {
3373
+ additionalState
3374
+ }),
3375
+ ...(!openInPage && {
3376
+ onOauthSuccess
3377
+ })
3378
+ };
3162
3379
  switch (providerName) {
3163
3380
  case sdkTypes.OAuthProviders.DISCORD:
3164
3381
  {
3165
- return await handleDiscordOauth({
3166
- openInPage,
3167
- ...(additionalState && {
3168
- additionalState
3169
- }),
3170
- ...(!openInPage && {
3171
- onOauthSuccess
3172
- })
3173
- });
3382
+ return await handleDiscordOauth(sharedOauthParams);
3174
3383
  }
3175
3384
  case sdkTypes.OAuthProviders.X:
3176
3385
  {
3177
- return await handleXOauth({
3178
- openInPage,
3179
- ...(additionalState && {
3180
- additionalState
3181
- }),
3182
- ...(!openInPage && {
3183
- onOauthSuccess
3184
- })
3185
- });
3386
+ return await handleXOauth(sharedOauthParams);
3186
3387
  }
3187
3388
  case sdkTypes.OAuthProviders.GOOGLE:
3188
3389
  {
3189
- return await handleGoogleOauth({
3190
- openInPage,
3191
- ...(additionalState && {
3192
- additionalState
3193
- }),
3194
- ...(!openInPage && {
3195
- onOauthSuccess
3196
- })
3197
- });
3390
+ return await handleGoogleOauth(sharedOauthParams);
3198
3391
  }
3199
3392
  case sdkTypes.OAuthProviders.APPLE:
3200
3393
  {
3201
- return await handleAppleOauth({
3202
- openInPage,
3203
- ...(additionalState && {
3204
- additionalState
3205
- }),
3206
- ...(!openInPage && {
3207
- onOauthSuccess
3208
- })
3209
- });
3394
+ return await handleAppleOauth(sharedOauthParams);
3210
3395
  }
3211
3396
  case sdkTypes.OAuthProviders.FACEBOOK:
3212
3397
  {
3213
- return await handleFacebookOauth({
3214
- openInPage,
3215
- ...(additionalState && {
3216
- additionalState
3217
- }),
3218
- ...(!openInPage && {
3219
- onOauthSuccess
3220
- })
3221
- });
3398
+ return await handleFacebookOauth(sharedOauthParams);
3222
3399
  }
3223
3400
  default:
3224
3401
  {
@@ -3382,7 +3559,7 @@ const ClientProvider = ({
3382
3559
  const tx = {
3383
3560
  from,
3384
3561
  to,
3385
- caip2,
3562
+ caip2: caip2,
3386
3563
  sponsor: sponsor ?? false,
3387
3564
  ...(value ? {
3388
3565
  value
@@ -3428,7 +3605,7 @@ const ClientProvider = ({
3428
3605
  preventBack: true,
3429
3606
  onClose: () => reject(new sdkTypes.TurnkeyError("User canceled the transaction.", sdkTypes.TurnkeyErrorCodes.USER_CANCELED)),
3430
3607
  content: jsxRuntime.jsx(SendTransaction.SendTransactionPage, {
3431
- icon: icon ?? helpers.getChainLogo(caip2),
3608
+ icon: icon ?? helpers$1.getChainLogo(caip2),
3432
3609
  action: action,
3433
3610
  caip2: caip2,
3434
3611
  successPageDuration: successPageDuration,
@@ -3478,7 +3655,7 @@ const ClientProvider = ({
3478
3655
  preventBack: true,
3479
3656
  onClose: () => reject(new sdkTypes.TurnkeyError("User canceled the transaction.", sdkTypes.TurnkeyErrorCodes.USER_CANCELED)),
3480
3657
  content: jsxRuntime.jsx(SendTransaction.SendTransactionPage, {
3481
- icon: icon ?? helpers.getChainLogo(caip2),
3658
+ icon: icon ?? helpers$1.getChainLogo(caip2),
3482
3659
  action: action,
3483
3660
  caip2: caip2,
3484
3661
  successPageDuration: successPageDuration,
@@ -3815,6 +3992,8 @@ const ClientProvider = ({
3815
3992
  config: masterConfig,
3816
3993
  httpClient: client?.httpClient,
3817
3994
  createHttpClient,
3995
+ overrideApiKeyStamper,
3996
+ overridePasskeyStamper,
3818
3997
  createPasskey,
3819
3998
  logout,
3820
3999
  loginWithPasskey,
@@ -3877,6 +4056,8 @@ const ClientProvider = ({
3877
4056
  clearUnusedKeyPairs,
3878
4057
  getActiveSessionKey,
3879
4058
  createApiKeyPair,
4059
+ deleteApiKeyPair,
4060
+ signWithApiKey,
3880
4061
  getProxyAuthConfig,
3881
4062
  fetchBootProofForAppProof,
3882
4063
  verifyAppProofs,