@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.
- package/dist/__clients__/core.d.ts +88 -35
- package/dist/__clients__/core.d.ts.map +1 -1
- package/dist/__clients__/core.js +271 -140
- package/dist/__clients__/core.js.map +1 -1
- package/dist/__clients__/core.mjs +274 -143
- package/dist/__clients__/core.mjs.map +1 -1
- package/dist/__generated__/sdk-client-base.d.ts +13 -0
- package/dist/__generated__/sdk-client-base.d.ts.map +1 -1
- package/dist/__generated__/sdk-client-base.js +198 -8
- package/dist/__generated__/sdk-client-base.js.map +1 -1
- package/dist/__generated__/sdk-client-base.mjs +198 -8
- package/dist/__generated__/sdk-client-base.mjs.map +1 -1
- package/dist/__generated__/version.d.ts +1 -1
- package/dist/__generated__/version.js +1 -1
- package/dist/__generated__/version.mjs +1 -1
- package/dist/__inputs__/public_api.types.d.ts +629 -494
- package/dist/__inputs__/public_api.types.d.ts.map +1 -1
- package/dist/__polyfills__/jest.setup.webcrypto.d.ts +2 -0
- package/dist/__polyfills__/jest.setup.webcrypto.d.ts.map +1 -0
- package/dist/__stampers__/api/base.d.ts +11 -5
- package/dist/__stampers__/api/base.d.ts.map +1 -1
- package/dist/__stampers__/api/base.js +32 -10
- package/dist/__stampers__/api/base.js.map +1 -1
- package/dist/__stampers__/api/base.mjs +32 -10
- package/dist/__stampers__/api/base.mjs.map +1 -1
- package/dist/__stampers__/api/web/stamper.d.ts.map +1 -1
- package/dist/__stampers__/api/web/stamper.js +2 -4
- package/dist/__stampers__/api/web/stamper.js.map +1 -1
- package/dist/__stampers__/api/web/stamper.mjs +2 -4
- package/dist/__stampers__/api/web/stamper.mjs.map +1 -1
- package/dist/__types__/base.d.ts +195 -75
- package/dist/__types__/base.d.ts.map +1 -1
- package/dist/__types__/base.js +14 -13
- package/dist/__types__/base.js.map +1 -1
- package/dist/__types__/base.mjs +15 -14
- package/dist/__types__/base.mjs.map +1 -1
- package/dist/__wallet__/stamper.d.ts.map +1 -1
- package/dist/__wallet__/stamper.js +8 -7
- package/dist/__wallet__/stamper.js.map +1 -1
- package/dist/__wallet__/stamper.mjs +9 -8
- package/dist/__wallet__/stamper.mjs.map +1 -1
- package/dist/__wallet__/wallet-connect/base.d.ts +3 -0
- package/dist/__wallet__/wallet-connect/base.d.ts.map +1 -1
- package/dist/__wallet__/wallet-connect/base.js +32 -8
- package/dist/__wallet__/wallet-connect/base.js.map +1 -1
- package/dist/__wallet__/wallet-connect/base.mjs +32 -8
- package/dist/__wallet__/wallet-connect/base.mjs.map +1 -1
- package/dist/__wallet__/wallet-connect/client.d.ts +28 -3
- package/dist/__wallet__/wallet-connect/client.d.ts.map +1 -1
- package/dist/__wallet__/wallet-connect/client.js +54 -5
- package/dist/__wallet__/wallet-connect/client.js.map +1 -1
- package/dist/__wallet__/wallet-connect/client.mjs +54 -5
- package/dist/__wallet__/wallet-connect/client.mjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/utils.d.ts +61 -13
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +184 -32
- package/dist/utils.js.map +1 -1
- package/dist/utils.mjs +179 -33
- package/dist/utils.mjs.map +1 -1
- package/package.json +10 -9
package/dist/__clients__/core.js
CHANGED
|
@@ -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
|
|
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 ||
|
|
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
|
|
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
|
|
136
|
+
let generatedPublicKey = undefined;
|
|
135
137
|
return await utils.withTurnkeyErrorHandling(async () => {
|
|
136
|
-
|
|
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 (!
|
|
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:
|
|
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
|
-
|
|
153
|
-
return
|
|
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 (
|
|
173
|
+
if (generatedPublicKey) {
|
|
166
174
|
try {
|
|
167
|
-
await this.apiKeyStamper?.deleteKeyPair(
|
|
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
|
-
* @
|
|
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
|
|
205
|
+
let generatedPublicKey = undefined;
|
|
195
206
|
return utils.withTurnkeyErrorHandling(async () => {
|
|
196
|
-
|
|
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
|
-
|
|
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-${
|
|
221
|
-
publicKey:
|
|
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?.
|
|
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(
|
|
250
|
+
await this.apiKeyStamper?.deleteKeyPair(generatedPublicKey);
|
|
240
251
|
await this.storeSession({
|
|
241
252
|
sessionToken: sessionResponse.session,
|
|
242
253
|
sessionKey,
|
|
243
254
|
});
|
|
244
|
-
|
|
245
|
-
return
|
|
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?.
|
|
252
|
-
if (
|
|
265
|
+
this.apiKeyStamper?.clearTemporaryPublicKey();
|
|
266
|
+
if (generatedPublicKey) {
|
|
253
267
|
try {
|
|
254
|
-
await this.apiKeyStamper?.deleteKeyPair(
|
|
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
|
|
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
|
-
* -
|
|
329
|
-
* -
|
|
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
|
|
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.
|
|
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 (
|
|
342
|
-
throw new sdkTypes.TurnkeyError("You can
|
|
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
|
|
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
|
-
|
|
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?.
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
448
|
-
? "API_KEY_CURVE_SECP256K1"
|
|
449
|
-
: "API_KEY_CURVE_ED25519",
|
|
480
|
+
curveType: utils.getCurveTypeFromProvider(walletProvider),
|
|
450
481
|
},
|
|
451
482
|
{
|
|
452
|
-
apiKeyName: `wallet-auth-${
|
|
453
|
-
publicKey:
|
|
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?.
|
|
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(
|
|
502
|
+
await this.apiKeyStamper?.deleteKeyPair(generatedPublicKey);
|
|
472
503
|
await this.storeSession({
|
|
473
504
|
sessionToken: sessionResponse.session,
|
|
474
505
|
sessionKey,
|
|
475
506
|
});
|
|
476
|
-
|
|
477
|
-
|
|
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?.
|
|
485
|
-
if (
|
|
520
|
+
this.apiKeyStamper?.clearTemporaryPublicKey();
|
|
521
|
+
if (generatedPublicKey) {
|
|
486
522
|
try {
|
|
487
|
-
await this.apiKeyStamper?.deleteKeyPair(
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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 (
|
|
667
|
+
if (generatedPublicKey) {
|
|
626
668
|
try {
|
|
627
|
-
await this.apiKeyStamper?.deleteKeyPair(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
|
|
896
|
+
const signUpRes = await this.signUpWithOtp({
|
|
848
897
|
verificationToken,
|
|
849
|
-
contact
|
|
850
|
-
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
: base.
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
:
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
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
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
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 (
|
|
1319
|
-
case
|
|
1320
|
-
|
|
1321
|
-
|
|
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
|
|
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
|
-
|
|
1329
|
-
|
|
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
|
-
|
|
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.
|
|
2553
|
+
await this.apiKeyStamper.setTemporaryPublicKey(publicKey);
|
|
2423
2554
|
}
|
|
2424
2555
|
return publicKey;
|
|
2425
2556
|
}, {
|