@turnkey/core 1.0.0-beta.4 → 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 (65) hide show
  1. package/dist/__clients__/core.d.ts +88 -35
  2. package/dist/__clients__/core.d.ts.map +1 -1
  3. package/dist/__clients__/core.js +271 -140
  4. package/dist/__clients__/core.js.map +1 -1
  5. package/dist/__clients__/core.mjs +274 -143
  6. package/dist/__clients__/core.mjs.map +1 -1
  7. package/dist/__generated__/sdk-client-base.d.ts +13 -0
  8. package/dist/__generated__/sdk-client-base.d.ts.map +1 -1
  9. package/dist/__generated__/sdk-client-base.js +198 -8
  10. package/dist/__generated__/sdk-client-base.js.map +1 -1
  11. package/dist/__generated__/sdk-client-base.mjs +198 -8
  12. package/dist/__generated__/sdk-client-base.mjs.map +1 -1
  13. package/dist/__generated__/version.d.ts +1 -1
  14. package/dist/__generated__/version.js +1 -1
  15. package/dist/__generated__/version.mjs +1 -1
  16. package/dist/__inputs__/public_api.types.d.ts +629 -494
  17. package/dist/__inputs__/public_api.types.d.ts.map +1 -1
  18. package/dist/__polyfills__/jest.setup.webcrypto.d.ts +2 -0
  19. package/dist/__polyfills__/jest.setup.webcrypto.d.ts.map +1 -0
  20. package/dist/__stampers__/api/base.d.ts +11 -5
  21. package/dist/__stampers__/api/base.d.ts.map +1 -1
  22. package/dist/__stampers__/api/base.js +32 -10
  23. package/dist/__stampers__/api/base.js.map +1 -1
  24. package/dist/__stampers__/api/base.mjs +32 -10
  25. package/dist/__stampers__/api/base.mjs.map +1 -1
  26. package/dist/__stampers__/api/web/stamper.d.ts.map +1 -1
  27. package/dist/__stampers__/api/web/stamper.js +2 -4
  28. package/dist/__stampers__/api/web/stamper.js.map +1 -1
  29. package/dist/__stampers__/api/web/stamper.mjs +2 -4
  30. package/dist/__stampers__/api/web/stamper.mjs.map +1 -1
  31. package/dist/__types__/base.d.ts +195 -75
  32. package/dist/__types__/base.d.ts.map +1 -1
  33. package/dist/__types__/base.js +14 -13
  34. package/dist/__types__/base.js.map +1 -1
  35. package/dist/__types__/base.mjs +15 -14
  36. package/dist/__types__/base.mjs.map +1 -1
  37. package/dist/__wallet__/stamper.d.ts.map +1 -1
  38. package/dist/__wallet__/stamper.js +8 -7
  39. package/dist/__wallet__/stamper.js.map +1 -1
  40. package/dist/__wallet__/stamper.mjs +9 -8
  41. package/dist/__wallet__/stamper.mjs.map +1 -1
  42. package/dist/__wallet__/wallet-connect/base.d.ts +3 -0
  43. package/dist/__wallet__/wallet-connect/base.d.ts.map +1 -1
  44. package/dist/__wallet__/wallet-connect/base.js +32 -8
  45. package/dist/__wallet__/wallet-connect/base.js.map +1 -1
  46. package/dist/__wallet__/wallet-connect/base.mjs +32 -8
  47. package/dist/__wallet__/wallet-connect/base.mjs.map +1 -1
  48. package/dist/__wallet__/wallet-connect/client.d.ts +28 -3
  49. package/dist/__wallet__/wallet-connect/client.d.ts.map +1 -1
  50. package/dist/__wallet__/wallet-connect/client.js +54 -5
  51. package/dist/__wallet__/wallet-connect/client.js.map +1 -1
  52. package/dist/__wallet__/wallet-connect/client.mjs +54 -5
  53. package/dist/__wallet__/wallet-connect/client.mjs.map +1 -1
  54. package/dist/index.d.ts +2 -1
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +2 -10
  57. package/dist/index.js.map +1 -1
  58. package/dist/index.mjs +2 -2
  59. package/dist/utils.d.ts +61 -13
  60. package/dist/utils.d.ts.map +1 -1
  61. package/dist/utils.js +184 -32
  62. package/dist/utils.js.map +1 -1
  63. package/dist/utils.mjs +179 -33
  64. package/dist/utils.mjs.map +1 -1
  65. package/package.json +10 -9
@@ -23,9 +23,9 @@ class TurnkeyClient {
23
23
  * - Handles both web and React Native environments, automatically selecting the appropriate passkey creation flow.
24
24
  * - The resulting attestation and challenge can be used to register the passkey with Turnkey.
25
25
  *
26
- * @param params.name - name of the passkey. If not provided, defaults to "A Passkey".
27
- * @param params.displayName - display name for the passkey. If not provided, defaults to "A Passkey".
26
+ * @param params.name - display name for the passkey (defaults to a generated name based on the current timestamp).
28
27
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
28
+ * @param params.challenge - challenge string to use for passkey registration. If not provided, a new challenge will be generated.
29
29
  * @returns A promise that resolves to an object containing:
30
30
  * - attestation: attestation object returned from the passkey creation process.
31
31
  * - encodedChallenge: encoded challenge string used for passkey registration.
@@ -33,16 +33,16 @@ class TurnkeyClient {
33
33
  */
34
34
  this.createPasskey = async (params) => {
35
35
  return utils.withTurnkeyErrorHandling(async () => {
36
- const name = params?.name || "A Passkey";
37
- const displayName = params?.displayName || "A Passkey";
36
+ const name = utils.isValidPasskeyName(params?.name || `passkey-${Date.now()}`);
38
37
  let passkey;
39
38
  if (utils.isWeb()) {
40
39
  const res = await this.passkeyStamper?.createWebPasskey({
41
40
  publicKey: {
42
41
  user: {
43
42
  name,
44
- displayName,
43
+ displayName: name,
45
44
  },
45
+ ...(params?.challenge && { challenge: params.challenge }),
46
46
  },
47
47
  });
48
48
  if (!res) {
@@ -56,7 +56,7 @@ class TurnkeyClient {
56
56
  else if (utils.isReactNative()) {
57
57
  const res = await this.passkeyStamper?.createReactNativePasskey({
58
58
  name,
59
- displayName,
59
+ displayName: name,
60
60
  });
61
61
  if (!res) {
62
62
  throw new sdkTypes.TurnkeyError("Failed to create React Native passkey", sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR);
@@ -127,21 +127,23 @@ class TurnkeyClient {
127
127
  * @param params.publicKey - public key to use for authentication. If not provided, a new key pair will be generated.
128
128
  * @param params.sessionKey - session key to use for session creation (defaults to the default session key).
129
129
  * @param params.expirationSeconds - session expiration time in seconds (defaults to the configured default).
130
- * @returns A promise that resolves to a signed JWT session token.
130
+ * @returns A promise that resolves to a {@link PasskeyAuthResult}, which includes:
131
+ * - `sessionToken`: the signed JWT session token.
132
+ * - `credentialId`: an empty string.
131
133
  * @throws {TurnkeyError} If there is an error during the passkey login process or if the user cancels the passkey prompt.
132
134
  */
133
135
  this.loginWithPasskey = async (params) => {
134
- let generatedKeyPair = undefined;
136
+ let generatedPublicKey = undefined;
135
137
  return await utils.withTurnkeyErrorHandling(async () => {
136
- generatedKeyPair =
138
+ generatedPublicKey =
137
139
  params?.publicKey || (await this.apiKeyStamper?.createKeyPair());
138
140
  const sessionKey = params?.sessionKey || base.SessionKey.DefaultSessionkey;
139
141
  const expirationSeconds = params?.expirationSeconds || base.DEFAULT_SESSION_EXPIRATION_IN_SECONDS;
140
- if (!generatedKeyPair) {
142
+ if (!generatedPublicKey) {
141
143
  throw new sdkTypes.TurnkeyError("A publickey could not be found or generated.", sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR);
142
144
  }
143
145
  const sessionResponse = await this.httpClient.stampLogin({
144
- publicKey: generatedKeyPair,
146
+ publicKey: generatedPublicKey,
145
147
  organizationId: this.config.organizationId,
146
148
  expirationSeconds,
147
149
  }, base.StamperType.Passkey);
@@ -149,8 +151,14 @@ class TurnkeyClient {
149
151
  sessionToken: sessionResponse.session,
150
152
  sessionKey,
151
153
  });
152
- generatedKeyPair = undefined; // Key pair was successfully used, set to null to prevent cleanup
153
- return sessionResponse.session;
154
+ generatedPublicKey = undefined; // Key pair was successfully used, set to null to prevent cleanup
155
+ return {
156
+ sessionToken: sessionResponse.session,
157
+ // TODO: can we return the credentialId here?
158
+ // from a quick glance this is going to be difficult
159
+ // for now we return an empty string
160
+ credentialId: "",
161
+ };
154
162
  }, {
155
163
  errorMessage: "Unable to log in with the provided passkey",
156
164
  errorCode: sdkTypes.TurnkeyErrorCodes.PASSKEY_LOGIN_AUTH_ERROR,
@@ -162,9 +170,9 @@ class TurnkeyClient {
162
170
  },
163
171
  }, {
164
172
  finallyFn: async () => {
165
- if (generatedKeyPair) {
173
+ if (generatedPublicKey) {
166
174
  try {
167
- await this.apiKeyStamper?.deleteKeyPair(generatedKeyPair);
175
+ await this.apiKeyStamper?.deleteKeyPair(generatedPublicKey);
168
176
  }
169
177
  catch (cleanupError) {
170
178
  throw new sdkTypes.TurnkeyError(`Failed to clean up generated key pair`, sdkTypes.TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
@@ -182,23 +190,26 @@ class TurnkeyClient {
182
190
  * - Automatically generates a new API key pair for authentication and session management.
183
191
  * - Stores the resulting session token and manages cleanup of unused key pairs.
184
192
  *
193
+ * @param params.passkeyDisplayName - display name for the passkey (defaults to a generated name based on the current timestamp).
185
194
  * @param params.createSubOrgParams - parameters for creating a sub-organization (e.g., authenticators, user metadata).
186
195
  * @param params.sessionKey - session key to use for storing the session (defaults to the default session key).
187
- * @param params.passkeyDisplayName - display name for the passkey (defaults to a generated name based on the current timestamp).
188
196
  * @param params.expirationSeconds - session expiration time in seconds (defaults to the configured default).
189
- * @returns A promise that resolves to a signed JWT session token for the new sub-organization.
197
+ * @param params.challenge - challenge string to use for passkey registration. If not provided, a new challenge will be generated.
198
+ * @returns A promise that resolves to a {@link PasskeyAuthResult}, which includes:
199
+ * - `sessionToken`: the signed JWT session token.
200
+ * - `credentialId`: the credential ID associated with the passkey created.
190
201
  * @throws {TurnkeyError} If there is an error during passkey creation, sub-organization creation, or session storage.
191
202
  */
192
203
  this.signUpWithPasskey = async (params) => {
193
204
  const { createSubOrgParams, passkeyDisplayName, sessionKey = base.SessionKey.DefaultSessionkey, expirationSeconds = base.DEFAULT_SESSION_EXPIRATION_IN_SECONDS, } = params || {};
194
- let generatedKeyPair = undefined;
205
+ let generatedPublicKey = undefined;
195
206
  return utils.withTurnkeyErrorHandling(async () => {
196
- generatedKeyPair = await this.apiKeyStamper?.createKeyPair();
207
+ generatedPublicKey = await this.apiKeyStamper?.createKeyPair();
197
208
  const passkeyName = passkeyDisplayName || `passkey-${Date.now()}`;
198
209
  // A passkey will be created automatically when you call this function. The name is passed in
199
210
  const passkey = await this.createPasskey({
200
211
  name: passkeyName,
201
- displayName: passkeyName,
212
+ ...(params?.challenge && { challenge: params.challenge }),
202
213
  });
203
214
  if (!passkey) {
204
215
  throw new sdkTypes.TurnkeyError("Failed to create passkey: encoded challenge or attestation is missing", sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR);
@@ -217,8 +228,8 @@ class TurnkeyClient {
217
228
  ],
218
229
  apiKeys: [
219
230
  {
220
- apiKeyName: `passkey-auth-${generatedKeyPair}`,
221
- publicKey: generatedKeyPair,
231
+ apiKeyName: `passkey-auth-${generatedPublicKey}`,
232
+ publicKey: generatedPublicKey,
222
233
  curveType: "API_KEY_CURVE_P256",
223
234
  expirationSeconds: "60",
224
235
  },
@@ -230,28 +241,31 @@ class TurnkeyClient {
230
241
  throw new sdkTypes.TurnkeyError(`Sign up failed`, sdkTypes.TurnkeyErrorCodes.PASSKEY_SIGNUP_AUTH_ERROR);
231
242
  }
232
243
  const newGeneratedKeyPair = await this.apiKeyStamper?.createKeyPair();
233
- this.apiKeyStamper?.setPublicKeyOverride(generatedKeyPair);
244
+ this.apiKeyStamper?.setTemporaryPublicKey(generatedPublicKey);
234
245
  const sessionResponse = await this.httpClient.stampLogin({
235
246
  publicKey: newGeneratedKeyPair,
236
247
  organizationId: this.config.organizationId,
237
248
  expirationSeconds,
238
249
  });
239
- await this.apiKeyStamper?.deleteKeyPair(generatedKeyPair);
250
+ await this.apiKeyStamper?.deleteKeyPair(generatedPublicKey);
240
251
  await this.storeSession({
241
252
  sessionToken: sessionResponse.session,
242
253
  sessionKey,
243
254
  });
244
- generatedKeyPair = undefined; // Key pair was successfully used, set to null to prevent cleanup
245
- return sessionResponse.session;
255
+ generatedPublicKey = undefined; // Key pair was successfully used, set to null to prevent cleanup
256
+ return {
257
+ sessionToken: sessionResponse.session,
258
+ credentialId: passkey.attestation.credentialId,
259
+ };
246
260
  }, {
247
261
  errorCode: sdkTypes.TurnkeyErrorCodes.PASSKEY_SIGNUP_AUTH_ERROR,
248
262
  errorMessage: "Failed to sign up with passkey",
249
263
  }, {
250
264
  finallyFn: async () => {
251
- this.apiKeyStamper?.clearPublicKeyOverride();
252
- if (generatedKeyPair) {
265
+ this.apiKeyStamper?.clearTemporaryPublicKey();
266
+ if (generatedPublicKey) {
253
267
  try {
254
- await this.apiKeyStamper?.deleteKeyPair(generatedKeyPair);
268
+ await this.apiKeyStamper?.deleteKeyPair(generatedPublicKey);
255
269
  }
256
270
  catch (cleanupError) {
257
271
  throw new sdkTypes.TurnkeyError(`Failed to clean up generated key pair`, sdkTypes.TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
@@ -322,24 +336,34 @@ class TurnkeyClient {
322
336
  });
323
337
  };
324
338
  /**
325
- * Switches the specified wallet provider to a different blockchain chain.
339
+ * Switches the wallet provider associated with a given wallet account
340
+ * to a different chain.
326
341
  *
327
- * - Requires the wallet manager and its connector to be initialized.
328
- * - The wallet provider must have at least one connected address.
329
- * - Does nothing if the wallet provider is already on the desired chain.
342
+ * - Requires the wallet manager and its connector to be initialized
343
+ * - Only works for connected wallet accounts
344
+ * - Looks up the provider for the given account address
345
+ * - Does nothing if the provider is already on the desired chain.
330
346
  *
331
- * @param walletProvider - wallet provider to switch.
332
- * @param chainOrId - target chain as a chain ID string or SwitchableChain object.
347
+ * @param params.walletAccount - The wallet account whose provider should be switched.
348
+ * @param params.chainOrId - The target chain, specified as a chain ID string or a SwitchableChain object.
349
+ * @param params.walletProviders - Optional list of wallet providers to search; falls back to `getWalletProviders()` if omitted.
333
350
  * @returns A promise that resolves once the chain switch is complete.
351
+ *
334
352
  * @throws {TurnkeyError} If the wallet manager is uninitialized, the provider is not connected, or the switch fails.
335
353
  */
336
- this.switchWalletProviderChain = async (walletProvider, chainOrId) => {
354
+ this.switchWalletAccountChain = async (params) => {
355
+ const { walletAccount, chainOrId, walletProviders } = params;
337
356
  return utils.withTurnkeyErrorHandling(async () => {
338
357
  if (!this.walletManager?.connector) {
339
358
  throw new sdkTypes.TurnkeyError("Wallet connector is not initialized", sdkTypes.TurnkeyErrorCodes.WALLET_MANAGER_COMPONENT_NOT_INITIALIZED);
340
359
  }
341
- if (walletProvider.connectedAddresses.length === 0) {
342
- throw new sdkTypes.TurnkeyError("You can not switch chains for a provider that is not connected", sdkTypes.TurnkeyErrorCodes.INVALID_REQUEST);
360
+ if (walletAccount.source === base.WalletSource.Embedded) {
361
+ throw new sdkTypes.TurnkeyError("You can only switch chains for connected wallet accounts", sdkTypes.TurnkeyErrorCodes.NOT_FOUND);
362
+ }
363
+ const providers = walletProviders ?? (await this.getWalletProviders());
364
+ const walletProvider = utils.findWalletProviderFromAddress(walletAccount.address, providers);
365
+ if (!walletProvider) {
366
+ throw new sdkTypes.TurnkeyError("Wallet provider not found", sdkTypes.TurnkeyErrorCodes.SWITCH_WALLET_CHAIN_ERROR);
343
367
  }
344
368
  // if the wallet provider is already on the desired chain, do nothing
345
369
  if (walletProvider.chainInfo.namespace === chainOrId) {
@@ -364,7 +388,9 @@ class TurnkeyClient {
364
388
  * @param params.publicKey - optional public key to associate with the session (generated if not provided).
365
389
  * @param params.sessionKey - optional key to store the session under (defaults to the default session key).
366
390
  * @param params.expirationSeconds - optional session expiration time in seconds (defaults to the configured default).
367
- * @returns A promise that resolves to the created session token.
391
+ * @returns A promise that resolves to a {@link WalletAuthResult}, which includes:
392
+ * - `sessionToken`: the signed JWT session token.
393
+ * - `address`: the authenticated wallet address.
368
394
  * @throws {TurnkeyError} If the wallet stamper is uninitialized, a public key cannot be found or generated, or login fails.
369
395
  */
370
396
  this.loginWithWallet = async (params) => {
@@ -389,14 +415,19 @@ class TurnkeyClient {
389
415
  sessionToken: sessionResponse.session,
390
416
  sessionKey,
391
417
  });
392
- return sessionResponse.session;
418
+ // TODO (Moe): What happens if a user connects to MetaMask on Ethereum,
419
+ // then switches to a Solana account within MetaMask? Will this flow break?
420
+ return {
421
+ sessionToken: sessionResponse.session,
422
+ address: utils.addressFromPublicKey(walletProvider.chainInfo.namespace, publicKey),
423
+ };
393
424
  }, {
394
425
  errorMessage: "Unable to log in with the provided wallet",
395
426
  errorCode: sdkTypes.TurnkeyErrorCodes.WALLET_LOGIN_AUTH_ERROR,
396
427
  }, {
397
428
  finallyFn: async () => {
398
429
  // Clean up the generated key pair if it wasn't successfully used
399
- this.apiKeyStamper?.clearPublicKeyOverride();
430
+ this.apiKeyStamper?.clearTemporaryPublicKey();
400
431
  if (publicKey) {
401
432
  try {
402
433
  await this.apiKeyStamper?.deleteKeyPair(publicKey);
@@ -421,17 +452,19 @@ class TurnkeyClient {
421
452
  * @param params.createSubOrgParams - parameters for creating a sub-organization (e.g., authenticators, user metadata).
422
453
  * @param params.sessionKey - session key to use for storing the session (defaults to the default session key).
423
454
  * @param params.expirationSeconds - session expiration time in seconds (defaults to the configured default).
424
- * @returns A promise that resolves to a signed JWT session token for the new sub-organization.
455
+ * @returns A promise that resolves to a {@link WalletAuthResult}, which includes:
456
+ * - `sessionToken`: the signed JWT session token.
457
+ * - `address`: the authenticated wallet address.
425
458
  * @throws {TurnkeyError} If there is an error during wallet authentication, sub-organization creation, session storage, or cleanup.
426
459
  */
427
460
  this.signUpWithWallet = async (params) => {
428
461
  const { walletProvider, createSubOrgParams, sessionKey = base.SessionKey.DefaultSessionkey, expirationSeconds = base.DEFAULT_SESSION_EXPIRATION_IN_SECONDS, } = params;
429
- let generatedKeyPair = undefined;
462
+ let generatedPublicKey = undefined;
430
463
  return utils.withTurnkeyErrorHandling(async () => {
431
464
  if (!this.walletManager?.stamper) {
432
465
  throw new sdkTypes.TurnkeyError("Wallet stamper is not initialized", sdkTypes.TurnkeyErrorCodes.WALLET_MANAGER_COMPONENT_NOT_INITIALIZED);
433
466
  }
434
- generatedKeyPair = await this.apiKeyStamper?.createKeyPair();
467
+ generatedPublicKey = await this.apiKeyStamper?.createKeyPair();
435
468
  this.walletManager.stamper.setProvider(walletProvider.interfaceType, walletProvider);
436
469
  const publicKey = await this.walletManager.stamper.getPublicKey(walletProvider.interfaceType, walletProvider);
437
470
  if (!publicKey) {
@@ -444,13 +477,11 @@ class TurnkeyClient {
444
477
  {
445
478
  apiKeyName: `wallet-auth:${publicKey}`,
446
479
  publicKey: publicKey,
447
- curveType: utils.isEthereumWallet(walletProvider)
448
- ? "API_KEY_CURVE_SECP256K1"
449
- : "API_KEY_CURVE_ED25519",
480
+ curveType: utils.getCurveTypeFromProvider(walletProvider),
450
481
  },
451
482
  {
452
- apiKeyName: `wallet-auth-${generatedKeyPair}`,
453
- publicKey: generatedKeyPair,
483
+ apiKeyName: `wallet-auth-${generatedPublicKey}`,
484
+ publicKey: generatedPublicKey,
454
485
  curveType: "API_KEY_CURVE_P256",
455
486
  expirationSeconds: "60",
456
487
  },
@@ -462,29 +493,34 @@ class TurnkeyClient {
462
493
  throw new sdkTypes.TurnkeyError(`Sign up failed`, sdkTypes.TurnkeyErrorCodes.WALLET_SIGNUP_AUTH_ERROR);
463
494
  }
464
495
  const newGeneratedKeyPair = await this.apiKeyStamper?.createKeyPair();
465
- this.apiKeyStamper?.setPublicKeyOverride(generatedKeyPair);
496
+ this.apiKeyStamper?.setTemporaryPublicKey(generatedPublicKey);
466
497
  const sessionResponse = await this.httpClient.stampLogin({
467
498
  publicKey: newGeneratedKeyPair,
468
499
  organizationId: this.config.organizationId,
469
500
  expirationSeconds,
470
501
  });
471
- await this.apiKeyStamper?.deleteKeyPair(generatedKeyPair);
502
+ await this.apiKeyStamper?.deleteKeyPair(generatedPublicKey);
472
503
  await this.storeSession({
473
504
  sessionToken: sessionResponse.session,
474
505
  sessionKey,
475
506
  });
476
- generatedKeyPair = undefined; // Key pair was successfully used, set to null to prevent cleanup
477
- return sessionResponse.session;
507
+ generatedPublicKey = undefined; // Key pair was successfully used, set to null to prevent cleanup
508
+ // TODO (Moe): What happens if a user connects to MetaMask on Ethereum,
509
+ // then switches to a Solana account within MetaMask? Will this flow break?
510
+ return {
511
+ sessionToken: sessionResponse.session,
512
+ address: utils.addressFromPublicKey(walletProvider.chainInfo.namespace, publicKey),
513
+ };
478
514
  }, {
479
515
  errorMessage: "Failed to sign up with wallet",
480
516
  errorCode: sdkTypes.TurnkeyErrorCodes.WALLET_SIGNUP_AUTH_ERROR,
481
517
  }, {
482
518
  finallyFn: async () => {
483
519
  // Clean up the generated key pair if it wasn't successfully used
484
- this.apiKeyStamper?.clearPublicKeyOverride();
485
- if (generatedKeyPair) {
520
+ this.apiKeyStamper?.clearTemporaryPublicKey();
521
+ if (generatedPublicKey) {
486
522
  try {
487
- await this.apiKeyStamper?.deleteKeyPair(generatedKeyPair);
523
+ await this.apiKeyStamper?.deleteKeyPair(generatedPublicKey);
488
524
  }
489
525
  catch (cleanupError) {
490
526
  throw new sdkTypes.TurnkeyError("Failed to clean up generated key pair", sdkTypes.TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
@@ -507,7 +543,10 @@ class TurnkeyClient {
507
543
  * @param params.createSubOrgParams - optional parameters for creating a sub-organization (e.g., authenticators, user metadata).
508
544
  * @param params.sessionKey - session key to use for storing the session (defaults to the default session key).
509
545
  * @param params.expirationSeconds - session expiration time in seconds (defaults to the configured default).
510
- * @returns A promise that resolves to a signed JWT session token for the sub-organization (new or existing).
546
+ * @returns A promise that resolves to an object containing:
547
+ * - `sessionToken`: the signed JWT session token.
548
+ * - `address`: the authenticated wallet address.
549
+ * - `action`: whether the flow resulted in a login or signup ({@link AuthAction}).
511
550
  * @throws {TurnkeyError} If there is an error during wallet authentication, sub-organization creation, or session storage.
512
551
  */
513
552
  this.loginOrSignupWithWallet = async (params) => {
@@ -515,19 +554,19 @@ class TurnkeyClient {
515
554
  const sessionKey = params.sessionKey || base.SessionKey.DefaultSessionkey;
516
555
  const walletProvider = params.walletProvider;
517
556
  const expirationSeconds = params.expirationSeconds || base.DEFAULT_SESSION_EXPIRATION_IN_SECONDS;
518
- let generatedKeyPair = undefined;
557
+ let generatedPublicKey = undefined;
519
558
  return utils.withTurnkeyErrorHandling(async () => {
520
559
  if (!this.walletManager?.stamper) {
521
560
  throw new sdkTypes.TurnkeyError("Wallet stamper is not initialized", sdkTypes.TurnkeyErrorCodes.WALLET_MANAGER_COMPONENT_NOT_INITIALIZED);
522
561
  }
523
- generatedKeyPair = await this.apiKeyStamper?.createKeyPair();
562
+ generatedPublicKey = await this.apiKeyStamper?.createKeyPair();
524
563
  this.walletManager.stamper.setProvider(walletProvider.interfaceType, walletProvider);
525
- // here we sign the request with the wallet, but we don't send it to the Turnkey yet
564
+ // here we sign the request with the wallet, but we don't send it to Turnkey yet
526
565
  // this is because we need to check if the subOrg exists first, and create one if it doesn't
527
- // once we have the subOrg for the publicKey, we then can send the request to the Turnkey
566
+ // once we have the subOrg for the publicKey, we then can send the request to Turnkey
528
567
  const signedRequest = await utils.withTurnkeyErrorHandling(async () => {
529
568
  return this.httpClient.stampStampLogin({
530
- publicKey: generatedKeyPair,
569
+ publicKey: generatedPublicKey,
531
570
  organizationId: this.config.organizationId,
532
571
  expirationSeconds,
533
572
  }, base.StamperType.Wallet);
@@ -564,7 +603,7 @@ class TurnkeyClient {
564
603
  throw new sdkTypes.TurnkeyError(`Unsupported interface type: ${walletProvider.interfaceType}`, sdkTypes.TurnkeyErrorCodes.INVALID_REQUEST);
565
604
  }
566
605
  // here we check if the subOrg exists and create one
567
- // then we send off the stamped request to the Turnkey
606
+ // then we send off the stamped request to Turnkey
568
607
  const accountRes = await this.httpClient.proxyGetAccount({
569
608
  filterType: base.FilterType.PublicKey,
570
609
  filterValue: publicKey,
@@ -582,9 +621,7 @@ class TurnkeyClient {
582
621
  {
583
622
  apiKeyName: `wallet-auth:${publicKey}`,
584
623
  publicKey: publicKey,
585
- curveType: utils.isEthereumWallet(walletProvider)
586
- ? "API_KEY_CURVE_SECP256K1"
587
- : "API_KEY_CURVE_ED25519",
624
+ curveType: utils.getCurveTypeFromProvider(walletProvider),
588
625
  },
589
626
  ],
590
627
  },
@@ -594,7 +631,7 @@ class TurnkeyClient {
594
631
  throw new sdkTypes.TurnkeyError(`Sign up failed`, sdkTypes.TurnkeyErrorCodes.WALLET_SIGNUP_AUTH_ERROR);
595
632
  }
596
633
  }
597
- // now we can send the stamped request to the Turnkey
634
+ // now we can send the stamped request to Turnkey
598
635
  const headers = {
599
636
  "Content-Type": "application/json",
600
637
  [signedRequest.stamp.stampHeaderName]: signedRequest.stamp.stampHeaderValue,
@@ -617,14 +654,19 @@ class TurnkeyClient {
617
654
  sessionToken: sessionToken,
618
655
  sessionKey,
619
656
  });
620
- return sessionToken;
657
+ return {
658
+ sessionToken: sessionToken,
659
+ address: utils.addressFromPublicKey(walletProvider.chainInfo.namespace, publicKey),
660
+ // if the subOrganizationId exists, it means the user is logging in
661
+ action: subOrganizationId ? sdkTypes.AuthAction.LOGIN : sdkTypes.AuthAction.SIGNUP,
662
+ };
621
663
  }, {
622
664
  errorCode: sdkTypes.TurnkeyErrorCodes.WALLET_LOGIN_OR_SIGNUP_ERROR,
623
665
  errorMessage: "Failed to log in or sign up with wallet",
624
666
  catchFn: async () => {
625
- if (generatedKeyPair) {
667
+ if (generatedPublicKey) {
626
668
  try {
627
- await this.apiKeyStamper?.deleteKeyPair(generatedKeyPair);
669
+ await this.apiKeyStamper?.deleteKeyPair(generatedPublicKey);
628
670
  }
629
671
  catch (cleanupError) {
630
672
  throw new sdkTypes.TurnkeyError(`Failed to clean up generated key pair`, sdkTypes.TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
@@ -726,7 +768,8 @@ class TurnkeyClient {
726
768
  * @param params.publicKey - public key to use for authentication. If not provided, a new key pair will be generated.
727
769
  * @param params.invalidateExisting - flag to invalidate existing session for the user.
728
770
  * @param params.sessionKey - session key to use for session creation (defaults to the default session key).
729
- * @returns A promise that resolves to a signed JWT session token.
771
+ * @returns A promise that resolves to a {@link BaseAuthResult}, which includes:
772
+ * - `sessionToken`: the signed JWT session token.
730
773
  * @throws {TurnkeyError} If there is an error during the OTP login process or if key pair cleanup fails.
731
774
  */
732
775
  this.loginWithOtp = async (params) => {
@@ -748,7 +791,9 @@ class TurnkeyClient {
748
791
  sessionToken: loginRes.session,
749
792
  sessionKey,
750
793
  });
751
- return loginRes.session;
794
+ return {
795
+ sessionToken: loginRes.session,
796
+ };
752
797
  }, {
753
798
  errorMessage: "Failed to log in with OTP",
754
799
  errorCode: sdkTypes.TurnkeyErrorCodes.OTP_LOGIN_ERROR,
@@ -780,7 +825,8 @@ class TurnkeyClient {
780
825
  * @param params.createSubOrgParams - parameters for creating a sub-organization (e.g., authenticators, user metadata).
781
826
  * @param params.invalidateExisting - flag to invalidate existing session for the user.
782
827
  * @param params.sessionKey - session key to use for session creation (defaults to the default session key).
783
- * @returns A promise that resolves to a signed JWT session token for the new sub-organization.
828
+ * @returns A promise that resolves to a {@link BaseAuthResult}, which includes:
829
+ * - `sessionToken`: the signed JWT session token.
784
830
  * @throws {TurnkeyError} If there is an error during the OTP sign-up process or session storage.
785
831
  */
786
832
  this.signUpWithOtp = async (params) => {
@@ -795,14 +841,14 @@ class TurnkeyClient {
795
841
  },
796
842
  });
797
843
  return utils.withTurnkeyErrorHandling(async () => {
798
- const generatedKeyPair = await this.apiKeyStamper?.createKeyPair();
844
+ const generatedPublicKey = await this.apiKeyStamper?.createKeyPair();
799
845
  const res = await this.httpClient.proxySignup(signUpBody);
800
846
  if (!res) {
801
847
  throw new sdkTypes.TurnkeyError(`Auth proxy OTP sign up failed`, sdkTypes.TurnkeyErrorCodes.OTP_SIGNUP_ERROR);
802
848
  }
803
849
  return await this.loginWithOtp({
804
850
  verificationToken,
805
- publicKey: generatedKeyPair,
851
+ publicKey: generatedPublicKey,
806
852
  ...(invalidateExisting && { invalidateExisting }),
807
853
  ...(sessionKey && { sessionKey }),
808
854
  });
@@ -828,7 +874,10 @@ class TurnkeyClient {
828
874
  * @param params.invalidateExisting - flag to invalidate existing sessions for the user.
829
875
  * @param params.sessionKey - session key to use for session creation (defaults to the default session key).
830
876
  * @param params.createSubOrgParams - parameters for sub-organization creation (e.g., authenticators, user metadata).
831
- * @returns A promise that resolves to a signed JWT session token for the user.
877
+ * @returns A promise that resolves to an object containing:
878
+ * - `sessionToken`: the signed JWT session token.
879
+ * - `verificationToken`: the OTP verification token.
880
+ * - `action`: whether the flow resulted in a login or signup ({@link AuthAction}).
832
881
  * @throws {TurnkeyError} If there is an error during OTP verification, sign-up, or login.
833
882
  */
834
883
  this.completeOtp = async (params) => {
@@ -844,24 +893,32 @@ class TurnkeyClient {
844
893
  throw new sdkTypes.TurnkeyError("No verification token returned from OTP verification", sdkTypes.TurnkeyErrorCodes.VERIFY_OTP_ERROR);
845
894
  }
846
895
  if (!subOrganizationId) {
847
- return await this.signUpWithOtp({
896
+ const signUpRes = await this.signUpWithOtp({
848
897
  verificationToken,
849
- contact: contact,
850
- otpType: otpType,
851
- ...(createSubOrgParams && {
852
- createSubOrgParams,
853
- }),
898
+ contact,
899
+ otpType,
900
+ ...(createSubOrgParams && { createSubOrgParams }),
854
901
  ...(invalidateExisting && { invalidateExisting }),
855
902
  ...(sessionKey && { sessionKey }),
856
903
  });
904
+ return {
905
+ ...signUpRes,
906
+ verificationToken,
907
+ action: sdkTypes.AuthAction.SIGNUP,
908
+ };
857
909
  }
858
910
  else {
859
- return await this.loginWithOtp({
911
+ const loginRes = await this.loginWithOtp({
860
912
  verificationToken,
861
913
  ...(publicKey && { publicKey }),
862
914
  ...(invalidateExisting && { invalidateExisting }),
863
915
  ...(sessionKey && { sessionKey }),
864
916
  });
917
+ return {
918
+ ...loginRes,
919
+ verificationToken,
920
+ action: sdkTypes.AuthAction.LOGIN,
921
+ };
865
922
  }
866
923
  }, {
867
924
  errorMessage: "Failed to complete OTP process",
@@ -878,12 +935,14 @@ class TurnkeyClient {
878
935
  * - Handles session storage and management, and supports invalidating existing sessions if specified.
879
936
  *
880
937
  * @param params.oidcToken - OIDC token received after successful authentication with the OAuth provider.
881
- * @param params.publicKey - public key to use for authentication. Must be generated prior to calling this function.
938
+ * @param params.publicKey - public key to use for authentication. Must be generated prior to calling this function, this is because the OIDC nonce has to be set to `sha256(publicKey)`.
882
939
  * @param params.providerName - name of the OAuth provider (defaults to a generated name with a timestamp).
883
940
  * @param params.sessionKey - session key to use for session creation (defaults to the default session key).
884
941
  * @param params.invalidateExisting - flag to invalidate existing sessions for the user.
885
942
  * @param params.createSubOrgParams - parameters for sub-organization creation (e.g., authenticators, user metadata).
886
- * @returns A promise that resolves to a signed JWT session token for the user.
943
+ * @returns A promise that resolves to an object containing:
944
+ * - `sessionToken`: the signed JWT session token.
945
+ * - `action`: whether the flow resulted in a login or signup ({@link AuthAction}).
887
946
  * @throws {TurnkeyError} If there is an error during the OAuth completion process, such as account lookup, sign-up, or login.
888
947
  */
889
948
  this.completeOauth = async (params) => {
@@ -898,22 +957,31 @@ class TurnkeyClient {
898
957
  }
899
958
  const subOrganizationId = accountRes.organizationId;
900
959
  if (subOrganizationId) {
901
- return this.loginWithOauth({
960
+ const loginRes = await this.loginWithOauth({
902
961
  oidcToken,
903
962
  publicKey,
904
963
  invalidateExisting,
905
964
  sessionKey,
906
965
  });
966
+ return {
967
+ ...loginRes,
968
+ action: sdkTypes.AuthAction.LOGIN,
969
+ };
907
970
  }
908
971
  else {
909
- return this.signUpWithOauth({
972
+ const signUpRes = await this.signUpWithOauth({
910
973
  oidcToken,
911
974
  publicKey,
912
975
  providerName,
976
+ sessionKey,
913
977
  ...(createSubOrgParams && {
914
978
  createSubOrgParams,
915
979
  }),
916
980
  });
981
+ return {
982
+ ...signUpRes,
983
+ action: sdkTypes.AuthAction.SIGNUP,
984
+ };
917
985
  }
918
986
  }, {
919
987
  errorMessage: "Failed to complete OAuth process",
@@ -932,7 +1000,8 @@ class TurnkeyClient {
932
1000
  * @param params.publicKey - public key to use for authentication. Must be generated prior to calling this function.
933
1001
  * @param params.invalidateExisting - flag to invalidate existing sessions for the user.
934
1002
  * @param params.sessionKey - session key to use for session creation (defaults to the default session key).
935
- * @returns A promise that resolves to a signed JWT session token.
1003
+ * @returns A promise that resolves to a {@link BaseAuthResult}, which includes:
1004
+ * - `sessionToken`: the signed JWT session token.
936
1005
  * @throws {TurnkeyError} If there is an error during the OAuth login process or if key pair cleanup fails.
937
1006
  */
938
1007
  this.loginWithOauth = async (params) => {
@@ -956,7 +1025,7 @@ class TurnkeyClient {
956
1025
  sessionToken: loginRes.session,
957
1026
  sessionKey,
958
1027
  });
959
- return loginRes.session;
1028
+ return { sessionToken: loginRes.session };
960
1029
  }, {
961
1030
  errorMessage: "Failed to complete OAuth login",
962
1031
  errorCode: sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR,
@@ -992,11 +1061,12 @@ class TurnkeyClient {
992
1061
  * @param params.providerName - name of the OAuth provider (e.g., "Google", "Apple").
993
1062
  * @param params.createSubOrgParams - parameters for sub-organization creation (e.g., authenticators, user metadata).
994
1063
  * @param params.sessionKey - session key to use for session creation (defaults to the default session key).
995
- * @returns A promise that resolves to a signed JWT session token for the new sub-organization.
1064
+ * @returns A promise that resolves to a {@link BaseAuthResult}, which includes:
1065
+ * - `sessionToken`: the signed JWT session token.
996
1066
  * @throws {TurnkeyError} If there is an error during the OAuth sign-up or login process.
997
1067
  */
998
1068
  this.signUpWithOauth = async (params) => {
999
- const { oidcToken, publicKey, providerName, createSubOrgParams } = params;
1069
+ const { oidcToken, publicKey, providerName, createSubOrgParams, sessionKey, } = params;
1000
1070
  return utils.withTurnkeyErrorHandling(async () => {
1001
1071
  const signUpBody = utils.buildSignUpBody({
1002
1072
  createSubOrgParams: {
@@ -1016,6 +1086,7 @@ class TurnkeyClient {
1016
1086
  return await this.loginWithOauth({
1017
1087
  oidcToken,
1018
1088
  publicKey: publicKey,
1089
+ ...(sessionKey && { sessionKey }),
1019
1090
  });
1020
1091
  }, {
1021
1092
  errorMessage: "Failed to sign up with OAuth",
@@ -1032,11 +1103,12 @@ class TurnkeyClient {
1032
1103
  * - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1033
1104
  *
1034
1105
  * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1106
+ * @param params.walletProviders - array of wallet providers to use for fetching wallets.
1035
1107
  * @returns A promise that resolves to an array of `Wallet` objects.
1036
1108
  * @throws {TurnkeyError} If no active session is found or if there is an error fetching wallets.
1037
1109
  */
1038
1110
  this.fetchWallets = async (params) => {
1039
- const { stampWith } = params || {};
1111
+ const { stampWith, walletProviders } = params || {};
1040
1112
  const session = await this.storageManager.getActiveSession();
1041
1113
  if (!session) {
1042
1114
  throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
@@ -1062,9 +1134,11 @@ class TurnkeyClient {
1062
1134
  // if wallet connecting is disabled we return only embedded wallets
1063
1135
  if (!this.walletManager?.connector)
1064
1136
  return embedded;
1065
- const providers = await this.getWalletProviders();
1137
+ const providers = walletProviders ?? (await this.getWalletProviders());
1066
1138
  const groupedProviders = new Map();
1067
1139
  for (const provider of providers) {
1140
+ // connected wallets don't all have some uuid we can use for the walletId
1141
+ // so what we do is we use a normalized version of the name for the wallet, like "metamask" or "phantom-wallet"
1068
1142
  const walletId = provider.info?.name?.toLowerCase().replace(/\s+/g, "-") ||
1069
1143
  "unknown";
1070
1144
  const group = groupedProviders.get(walletId) || [];
@@ -1146,31 +1220,69 @@ class TurnkeyClient {
1146
1220
  return [];
1147
1221
  const connected = [];
1148
1222
  const providers = walletProviders ?? (await this.getWalletProviders());
1223
+ // Context: connected wallets don't all have some uuid we can use for the walletId so what
1224
+ // we do is we use a normalized version of the name for the wallet, like "metamask"
1225
+ // or "phantom-wallet"
1226
+ //
1227
+ // when fetching accounts, we select all providers with this normalized walletId.
1228
+ // A single wallet can map to multiple providers if it supports multiple chains
1229
+ // (e.g. MetaMask for Ethereum and MetaMask for Solana)
1149
1230
  const matching = providers.filter((p) => p.info?.name?.toLowerCase().replace(/\s+/g, "-") ===
1150
1231
  wallet.walletId && p.connectedAddresses.length > 0);
1232
+ const sign = this.walletManager.connector.sign.bind(this.walletManager.connector);
1233
+ const user = await this.fetchUser({
1234
+ stampWith,
1235
+ });
1236
+ const { ethereum: ethereumAddresses, solana: solanaAddresses } = utils.getAuthenticatorAddresses(user);
1151
1237
  for (const provider of matching) {
1152
1238
  const timestamp = utils.toExternalTimestamp();
1153
1239
  for (const address of provider.connectedAddresses) {
1154
- const account = {
1155
- walletAccountId: `${wallet.walletId}-${provider.interfaceType}-${address}`,
1156
- organizationId: session.organizationId,
1157
- walletId: wallet.walletId,
1158
- curve: utils.isEthereumWallet(provider)
1159
- ? base.Curve.SECP256K1
1160
- : base.Curve.ED25519,
1161
- pathFormat: "PATH_FORMAT_BIP32",
1162
- path: base.WalletSource.Connected,
1163
- source: base.WalletSource.Connected,
1164
- addressFormat: utils.isEthereumWallet(provider)
1165
- ? "ADDRESS_FORMAT_ETHEREUM"
1166
- : "ADDRESS_FORMAT_SOLANA",
1167
- address,
1168
- createdAt: timestamp,
1169
- updatedAt: timestamp,
1170
- ...utils.getWalletAccountMethods(this.walletManager.connector.sign.bind(this.walletManager.connector), provider),
1171
- ...(utils.isSolanaWallet(provider) && { publicKey: address }),
1172
- };
1173
- connected.push(account);
1240
+ if (utils.isEthereumProvider(provider)) {
1241
+ const evmAccount = {
1242
+ walletAccountId: `${wallet.walletId}-${provider.interfaceType}-${address}`,
1243
+ organizationId: session.organizationId,
1244
+ walletId: wallet.walletId,
1245
+ pathFormat: "PATH_FORMAT_BIP32",
1246
+ path: base.WalletSource.Connected,
1247
+ source: base.WalletSource.Connected,
1248
+ address,
1249
+ createdAt: timestamp,
1250
+ updatedAt: timestamp,
1251
+ // ethereum specific
1252
+ curve: base.Curve.SECP256K1,
1253
+ addressFormat: "ADDRESS_FORMAT_ETHEREUM",
1254
+ chainInfo: provider.chainInfo,
1255
+ isAuthenticator: ethereumAddresses.includes(address.toLowerCase()),
1256
+ signMessage: (msg) => sign(msg, provider, base.SignIntent.SignMessage),
1257
+ signAndSendTransaction: (tx) => sign(tx, provider, base.SignIntent.SignAndSendTransaction),
1258
+ };
1259
+ connected.push(evmAccount);
1260
+ continue;
1261
+ }
1262
+ if (utils.isSolanaProvider(provider)) {
1263
+ const solAccount = {
1264
+ walletAccountId: `${wallet.walletId}-${provider.interfaceType}-${address}`,
1265
+ organizationId: session.organizationId,
1266
+ walletId: wallet.walletId,
1267
+ pathFormat: "PATH_FORMAT_BIP32",
1268
+ path: base.WalletSource.Connected,
1269
+ source: base.WalletSource.Connected,
1270
+ address,
1271
+ createdAt: timestamp,
1272
+ updatedAt: timestamp,
1273
+ // solana specific
1274
+ publicKey: address,
1275
+ curve: base.Curve.ED25519,
1276
+ addressFormat: "ADDRESS_FORMAT_SOLANA",
1277
+ chainInfo: provider.chainInfo,
1278
+ isAuthenticator: solanaAddresses.includes(address),
1279
+ signMessage: (msg) => sign(msg, provider, base.SignIntent.SignMessage),
1280
+ signTransaction: (tx) => sign(tx, provider, base.SignIntent.SignTransaction),
1281
+ };
1282
+ connected.push(solAccount);
1283
+ continue;
1284
+ }
1285
+ throw new Error(`Unsupported wallet chain: ${provider.chainInfo}. Supported chains are Ethereum and Solana.`);
1174
1286
  }
1175
1287
  }
1176
1288
  return connected;
@@ -1179,6 +1291,33 @@ class TurnkeyClient {
1179
1291
  errorCode: sdkTypes.TurnkeyErrorCodes.FETCH_WALLET_ACCOUNTS_ERROR,
1180
1292
  });
1181
1293
  };
1294
+ /**
1295
+ * Fetches all private keys for the current user.
1296
+ *
1297
+ * - Retrieves private keys from the Turnkey API.
1298
+ * - Supports stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1299
+ *
1300
+ * @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
1301
+ * @returns A promise that resolves to an array of `v1PrivateKey` objects.
1302
+ * @throws {TurnkeyError} If no active session is found or if there is an error fetching private keys.
1303
+ */
1304
+ this.fetchPrivateKeys = async (params) => {
1305
+ const { stampWith } = params || {};
1306
+ const session = await this.storageManager.getActiveSession();
1307
+ if (!session) {
1308
+ throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
1309
+ }
1310
+ return utils.withTurnkeyErrorHandling(async () => {
1311
+ const res = await this.httpClient.getPrivateKeys({ organizationId: session.organizationId }, stampWith);
1312
+ if (!res) {
1313
+ throw new sdkTypes.TurnkeyError("Failed to fetch private keys", sdkTypes.TurnkeyErrorCodes.BAD_RESPONSE);
1314
+ }
1315
+ return res.privateKeys;
1316
+ }, {
1317
+ errorMessage: "Failed to fetch private keys",
1318
+ errorCode: sdkTypes.TurnkeyErrorCodes.FETCH_PRIVATE_KEYS_ERROR,
1319
+ });
1320
+ };
1182
1321
  /**
1183
1322
  * Signs a message using the specified wallet account.
1184
1323
  *
@@ -1268,15 +1407,16 @@ class TurnkeyClient {
1268
1407
  const { walletAccount, unsignedTransaction, transactionType, stampWith } = params;
1269
1408
  return utils.withTurnkeyErrorHandling(async () => {
1270
1409
  if (walletAccount.source === base.WalletSource.Connected) {
1271
- // this is a connected wallet account
1272
- if (!walletAccount.signTransaction) {
1273
- const isEthereum = walletAccount.addressFormat === "ADDRESS_FORMAT_ETHEREUM";
1274
- const reason = isEthereum
1275
- ? "Ethereum connected wallets do not support raw transaction signing due to EIP-1193 limitations."
1276
- : "This connected wallet does not support raw transaction signing.";
1277
- throw new sdkTypes.TurnkeyError(`Failed to sign transaction: ${reason} ${isEthereum ? "Use signAndSendTransaction instead." : ""}`, sdkTypes.TurnkeyErrorCodes.SIGN_TRANSACTION_ERROR);
1410
+ switch (walletAccount.chainInfo.namespace) {
1411
+ case base.Chain.Ethereum:
1412
+ throw new sdkTypes.TurnkeyError("Ethereum connected wallets do not support raw transaction signing. Use signAndSendTransaction instead.", sdkTypes.TurnkeyErrorCodes.INVALID_REQUEST);
1413
+ case base.Chain.Solana:
1414
+ // not sure why typescript isn't inferring the type here
1415
+ // if namespace is Chain.Solana, then it must be a ConnectedSolanaWalletAccount
1416
+ return walletAccount.signTransaction(unsignedTransaction);
1417
+ default:
1418
+ throw new sdkTypes.TurnkeyError("Unsupported connected wallet type.", sdkTypes.TurnkeyErrorCodes.INVALID_REQUEST);
1278
1419
  }
1279
- return await walletAccount?.signTransaction(unsignedTransaction);
1280
1420
  }
1281
1421
  // this is an embedded wallet account
1282
1422
  const signTransaction = await this.httpClient.signTransaction({
@@ -1315,19 +1455,17 @@ class TurnkeyClient {
1315
1455
  return utils.withTurnkeyErrorHandling(async () => {
1316
1456
  if (walletAccount.source === base.WalletSource.Connected) {
1317
1457
  // this is a connected wallet account
1318
- switch (transactionType) {
1319
- case "TRANSACTION_TYPE_ETHEREUM":
1320
- if (!walletAccount.signAndSendTransaction) {
1321
- throw new sdkTypes.TurnkeyError("This connected wallet does not support signAndSendTransaction.", sdkTypes.TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR);
1322
- }
1458
+ switch (walletAccount.chainInfo.namespace) {
1459
+ case base.Chain.Ethereum:
1460
+ // not sure why typescript isn't inferring the type here
1461
+ // if namespace is Chain.Ethereum, then it must be a ConnectedEthereumWalletAccount
1323
1462
  return await walletAccount.signAndSendTransaction(unsignedTransaction);
1324
- case "TRANSACTION_TYPE_SOLANA":
1463
+ case base.Chain.Solana:
1325
1464
  if (!rpcUrl) {
1326
1465
  throw new sdkTypes.TurnkeyError("Missing rpcUrl: connected Solana wallets require an RPC URL to broadcast transactions.", sdkTypes.TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR);
1327
1466
  }
1328
- if (!walletAccount.signTransaction) {
1329
- throw new sdkTypes.TurnkeyError("This connected wallet does not support signAndSendTransaction.", sdkTypes.TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR);
1330
- }
1467
+ // not sure why typescript isn't inferring the type here
1468
+ // if namespace is Chain.Solana, then it must be a ConnectedSolanaWalletAccount
1331
1469
  const signature = await walletAccount.signTransaction(unsignedTransaction);
1332
1470
  return await utils.broadcastTransaction({
1333
1471
  signedTransaction: signature,
@@ -1712,7 +1850,6 @@ class TurnkeyClient {
1712
1850
  this.addPasskey = async (params) => {
1713
1851
  const { stampWith } = params || {};
1714
1852
  const name = params?.name || `Turnkey Passkey-${Date.now()}`;
1715
- const displayName = params?.displayName || name;
1716
1853
  return utils.withTurnkeyErrorHandling(async () => {
1717
1854
  const session = await this.storageManager.getActiveSession();
1718
1855
  if (!session) {
@@ -1721,7 +1858,6 @@ class TurnkeyClient {
1721
1858
  const userId = params?.userId || session.userId;
1722
1859
  const { encodedChallenge, attestation } = await this.createPasskey({
1723
1860
  name,
1724
- displayName,
1725
1861
  ...(stampWith && { stampWith }),
1726
1862
  });
1727
1863
  if (!attestation || !encodedChallenge) {
@@ -2145,11 +2281,7 @@ class TurnkeyClient {
2145
2281
  if (!sessionToken)
2146
2282
  return;
2147
2283
  utils.withTurnkeyErrorHandling(async () => {
2148
- const sessionToReplace = await this.storageManager.getSession(sessionKey);
2149
2284
  await this.storageManager.storeSession(sessionToken, sessionKey);
2150
- if (sessionToReplace) {
2151
- await this.apiKeyStamper?.deleteKeyPair(sessionToReplace.publicKey);
2152
- }
2153
2285
  }, {
2154
2286
  errorMessage: "Failed to store session",
2155
2287
  errorCode: sdkTypes.TurnkeyErrorCodes.STORE_SESSION_ERROR,
@@ -2199,9 +2331,8 @@ class TurnkeyClient {
2199
2331
  this.clearAllSessions = async () => {
2200
2332
  utils.withTurnkeyErrorHandling(async () => {
2201
2333
  const sessionKeys = await this.storageManager.listSessionKeys();
2202
- if (sessionKeys.length === 0) {
2203
- throw new sdkTypes.TurnkeyError("No sessions found to clear.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
2204
- }
2334
+ if (sessionKeys.length === 0)
2335
+ return;
2205
2336
  for (const sessionKey of sessionKeys) {
2206
2337
  this.clearSession({ sessionKey });
2207
2338
  }
@@ -2419,7 +2550,7 @@ class TurnkeyClient {
2419
2550
  }
2420
2551
  const publicKey = await this.apiKeyStamper.createKeyPair(externalKeyPair ? externalKeyPair : undefined);
2421
2552
  if (storeOverride && publicKey) {
2422
- await this.apiKeyStamper.setPublicKeyOverride(publicKey);
2553
+ await this.apiKeyStamper.setTemporaryPublicKey(publicKey);
2423
2554
  }
2424
2555
  return publicKey;
2425
2556
  }, {