@turnkey/core 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.MD +5 -0
- package/dist/__clients__/core.d.ts +1010 -0
- package/dist/__clients__/core.d.ts.map +1 -0
- package/dist/__clients__/core.js +2358 -0
- package/dist/__clients__/core.js.map +1 -0
- package/dist/__clients__/core.mjs +2356 -0
- package/dist/__clients__/core.mjs.map +1 -0
- package/dist/__generated__/sdk-client-base.d.ts +211 -0
- package/dist/__generated__/sdk-client-base.d.ts.map +1 -0
- package/dist/__generated__/sdk-client-base.js +3219 -0
- package/dist/__generated__/sdk-client-base.js.map +1 -0
- package/dist/__generated__/sdk-client-base.mjs +3217 -0
- package/dist/__generated__/sdk-client-base.mjs.map +1 -0
- package/dist/__generated__/version.d.ts +2 -0
- package/dist/__generated__/version.d.ts.map +1 -0
- package/dist/__generated__/version.js +6 -0
- package/dist/__generated__/version.js.map +1 -0
- package/dist/__generated__/version.mjs +4 -0
- package/dist/__generated__/version.mjs.map +1 -0
- package/dist/__inputs__/public_api.types.d.ts +5170 -0
- package/dist/__inputs__/public_api.types.d.ts.map +1 -0
- package/dist/__polyfills__/window.d.ts +15 -0
- package/dist/__polyfills__/window.d.ts.map +1 -0
- package/dist/__polyfills__/window.js +30 -0
- package/dist/__polyfills__/window.js.map +1 -0
- package/dist/__polyfills__/window.mjs +28 -0
- package/dist/__polyfills__/window.mjs.map +1 -0
- package/dist/__stampers__/api/base.d.ts +20 -0
- package/dist/__stampers__/api/base.d.ts.map +1 -0
- package/dist/__stampers__/api/base.js +67 -0
- package/dist/__stampers__/api/base.js.map +1 -0
- package/dist/__stampers__/api/base.mjs +65 -0
- package/dist/__stampers__/api/base.mjs.map +1 -0
- package/dist/__stampers__/api/mobile/stamper.d.ts +13 -0
- package/dist/__stampers__/api/mobile/stamper.d.ts.map +1 -0
- package/dist/__stampers__/api/mobile/stamper.js +69 -0
- package/dist/__stampers__/api/mobile/stamper.js.map +1 -0
- package/dist/__stampers__/api/mobile/stamper.mjs +67 -0
- package/dist/__stampers__/api/mobile/stamper.mjs.map +1 -0
- package/dist/__stampers__/api/web/stamper.d.ts +14 -0
- package/dist/__stampers__/api/web/stamper.d.ts.map +1 -0
- package/dist/__stampers__/api/web/stamper.js +212 -0
- package/dist/__stampers__/api/web/stamper.js.map +1 -0
- package/dist/__stampers__/api/web/stamper.mjs +210 -0
- package/dist/__stampers__/api/web/stamper.mjs.map +1 -0
- package/dist/__stampers__/passkey/base.d.ts +18 -0
- package/dist/__stampers__/passkey/base.d.ts.map +1 -0
- package/dist/__stampers__/passkey/base.js +138 -0
- package/dist/__stampers__/passkey/base.js.map +1 -0
- package/dist/__stampers__/passkey/base.mjs +134 -0
- package/dist/__stampers__/passkey/base.mjs.map +1 -0
- package/dist/__storage__/base.d.ts +3 -0
- package/dist/__storage__/base.d.ts.map +1 -0
- package/dist/__storage__/base.js +27 -0
- package/dist/__storage__/base.js.map +1 -0
- package/dist/__storage__/base.mjs +25 -0
- package/dist/__storage__/base.mjs.map +1 -0
- package/dist/__storage__/mobile/storage.d.ts +18 -0
- package/dist/__storage__/mobile/storage.d.ts.map +1 -0
- package/dist/__storage__/mobile/storage.js +74 -0
- package/dist/__storage__/mobile/storage.js.map +1 -0
- package/dist/__storage__/mobile/storage.mjs +72 -0
- package/dist/__storage__/mobile/storage.mjs.map +1 -0
- package/dist/__storage__/web/storage.d.ts +19 -0
- package/dist/__storage__/web/storage.d.ts.map +1 -0
- package/dist/__storage__/web/storage.js +79 -0
- package/dist/__storage__/web/storage.js.map +1 -0
- package/dist/__storage__/web/storage.mjs +77 -0
- package/dist/__storage__/web/storage.mjs.map +1 -0
- package/dist/__types__/base.d.ts +407 -0
- package/dist/__types__/base.d.ts.map +1 -0
- package/dist/__types__/base.js +88 -0
- package/dist/__types__/base.js.map +1 -0
- package/dist/__types__/base.mjs +84 -0
- package/dist/__types__/base.mjs.map +1 -0
- package/dist/__wallet__/base.d.ts +3 -0
- package/dist/__wallet__/base.d.ts.map +1 -0
- package/dist/__wallet__/base.js +24 -0
- package/dist/__wallet__/base.js.map +1 -0
- package/dist/__wallet__/base.mjs +22 -0
- package/dist/__wallet__/base.mjs.map +1 -0
- package/dist/__wallet__/connector.d.ts +33 -0
- package/dist/__wallet__/connector.d.ts.map +1 -0
- package/dist/__wallet__/connector.js +63 -0
- package/dist/__wallet__/connector.js.map +1 -0
- package/dist/__wallet__/connector.mjs +61 -0
- package/dist/__wallet__/connector.mjs.map +1 -0
- package/dist/__wallet__/mobile/manager.d.ts +35 -0
- package/dist/__wallet__/mobile/manager.d.ts.map +1 -0
- package/dist/__wallet__/mobile/manager.js +95 -0
- package/dist/__wallet__/mobile/manager.js.map +1 -0
- package/dist/__wallet__/mobile/manager.mjs +93 -0
- package/dist/__wallet__/mobile/manager.mjs.map +1 -0
- package/dist/__wallet__/stamper.d.ts +19 -0
- package/dist/__wallet__/stamper.d.ts.map +1 -0
- package/dist/__wallet__/stamper.js +116 -0
- package/dist/__wallet__/stamper.js.map +1 -0
- package/dist/__wallet__/stamper.mjs +113 -0
- package/dist/__wallet__/stamper.mjs.map +1 -0
- package/dist/__wallet__/wallet-connect/base.d.ts +83 -0
- package/dist/__wallet__/wallet-connect/base.d.ts.map +1 -0
- package/dist/__wallet__/wallet-connect/base.js +362 -0
- package/dist/__wallet__/wallet-connect/base.js.map +1 -0
- package/dist/__wallet__/wallet-connect/base.mjs +360 -0
- package/dist/__wallet__/wallet-connect/base.mjs.map +1 -0
- package/dist/__wallet__/wallet-connect/client.d.ts +78 -0
- package/dist/__wallet__/wallet-connect/client.d.ts.map +1 -0
- package/dist/__wallet__/wallet-connect/client.js +139 -0
- package/dist/__wallet__/wallet-connect/client.js.map +1 -0
- package/dist/__wallet__/wallet-connect/client.mjs +137 -0
- package/dist/__wallet__/wallet-connect/client.mjs.map +1 -0
- package/dist/__wallet__/web/manager.d.ts +41 -0
- package/dist/__wallet__/web/manager.d.ts.map +1 -0
- package/dist/__wallet__/web/manager.js +115 -0
- package/dist/__wallet__/web/manager.js.map +1 -0
- package/dist/__wallet__/web/manager.mjs +113 -0
- package/dist/__wallet__/web/manager.mjs.map +1 -0
- package/dist/__wallet__/web/native/ethereum.d.ts +67 -0
- package/dist/__wallet__/web/native/ethereum.d.ts.map +1 -0
- package/dist/__wallet__/web/native/ethereum.js +248 -0
- package/dist/__wallet__/web/native/ethereum.js.map +1 -0
- package/dist/__wallet__/web/native/ethereum.mjs +245 -0
- package/dist/__wallet__/web/native/ethereum.mjs.map +1 -0
- package/dist/__wallet__/web/native/solana.d.ts +19 -0
- package/dist/__wallet__/web/native/solana.d.ts.map +1 -0
- package/dist/__wallet__/web/native/solana.js +149 -0
- package/dist/__wallet__/web/native/solana.js.map +1 -0
- package/dist/__wallet__/web/native/solana.mjs +146 -0
- package/dist/__wallet__/web/native/solana.mjs.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +8 -0
- package/dist/index.mjs.map +1 -0
- package/dist/turnkey-helpers.d.ts +68 -0
- package/dist/turnkey-helpers.d.ts.map +1 -0
- package/dist/turnkey-helpers.js +466 -0
- package/dist/turnkey-helpers.js.map +1 -0
- package/dist/turnkey-helpers.mjs +399 -0
- package/dist/turnkey-helpers.mjs.map +1 -0
- package/dist/utils.d.ts +54 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +596 -0
- package/dist/utils.js.map +1 -0
- package/dist/utils.mjs +574 -0
- package/dist/utils.mjs.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,2358 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var sdkClientBase = require('../__generated__/sdk-client-base.js');
|
|
4
|
+
var sdkTypes = require('@turnkey/sdk-types');
|
|
5
|
+
var base = require('../__types__/base.js');
|
|
6
|
+
var utils = require('../utils.js');
|
|
7
|
+
var base$1 = require('../__storage__/base.js');
|
|
8
|
+
var base$2 = require('../__stampers__/api/base.js');
|
|
9
|
+
var base$3 = require('../__stampers__/passkey/base.js');
|
|
10
|
+
var turnkeyHelpers = require('../turnkey-helpers.js');
|
|
11
|
+
var jwtDecode = require('jwt-decode');
|
|
12
|
+
var base$4 = require('../__wallet__/base.js');
|
|
13
|
+
var ethers = require('ethers');
|
|
14
|
+
|
|
15
|
+
class TurnkeyClient {
|
|
16
|
+
constructor(config,
|
|
17
|
+
// Users can pass in their own stampers, or we will create them. Should we remove this?
|
|
18
|
+
apiKeyStamper, passkeyStamper, walletManager) {
|
|
19
|
+
/**
|
|
20
|
+
* Creates a new passkey authenticator for the user.
|
|
21
|
+
*
|
|
22
|
+
* - This function generates a new passkey attestation and challenge, suitable for registration with the user's device.
|
|
23
|
+
* - Handles both web and React Native environments, automatically selecting the appropriate passkey creation flow.
|
|
24
|
+
* - The resulting attestation and challenge can be used to register the passkey with Turnkey.
|
|
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".
|
|
28
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
29
|
+
* @returns A promise that resolves to an object containing:
|
|
30
|
+
* - attestation: attestation object returned from the passkey creation process.
|
|
31
|
+
* - encodedChallenge: encoded challenge string used for passkey registration.
|
|
32
|
+
* @throws {TurnkeyError} If there is an error during passkey creation, or if the platform is unsupported.
|
|
33
|
+
*/
|
|
34
|
+
this.createPasskey = async (params) => {
|
|
35
|
+
try {
|
|
36
|
+
const name = params?.name || "A Passkey";
|
|
37
|
+
const displayName = params?.displayName || "A Passkey";
|
|
38
|
+
let passkey;
|
|
39
|
+
if (utils.isWeb()) {
|
|
40
|
+
const res = await this.passkeyStamper?.createWebPasskey({
|
|
41
|
+
publicKey: {
|
|
42
|
+
user: {
|
|
43
|
+
name,
|
|
44
|
+
displayName,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
if (!res) {
|
|
49
|
+
throw new sdkTypes.TurnkeyError("Failed to create React Native passkey", sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
50
|
+
}
|
|
51
|
+
passkey = {
|
|
52
|
+
encodedChallenge: res?.encodedChallenge,
|
|
53
|
+
attestation: res?.attestation,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
else if (utils.isReactNative()) {
|
|
57
|
+
const res = await this.passkeyStamper?.createReactNativePasskey({
|
|
58
|
+
name,
|
|
59
|
+
displayName,
|
|
60
|
+
});
|
|
61
|
+
if (!res) {
|
|
62
|
+
throw new sdkTypes.TurnkeyError("Failed to create React Native passkey", sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
63
|
+
}
|
|
64
|
+
passkey = {
|
|
65
|
+
encodedChallenge: res?.challenge,
|
|
66
|
+
attestation: res?.attestation,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
throw new sdkTypes.TurnkeyError("Unsupported platform for passkey creation", sdkTypes.TurnkeyErrorCodes.INVALID_REQUEST);
|
|
71
|
+
}
|
|
72
|
+
return passkey;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (error?.message?.includes("timed out or was not allowed")) {
|
|
76
|
+
throw new sdkTypes.TurnkeyError("Passkey creation was cancelled by the user.", sdkTypes.TurnkeyErrorCodes.SELECT_PASSKEY_CANCELLED, error);
|
|
77
|
+
}
|
|
78
|
+
else if (error instanceof sdkTypes.TurnkeyError) {
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
throw new sdkTypes.TurnkeyError(`Failed to create passkey`, sdkTypes.TurnkeyErrorCodes.CREATE_PASSKEY_ERROR, error);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Logs out the current client session.
|
|
86
|
+
*
|
|
87
|
+
* - This function clears the specified session and removes any associated key pairs from storage.
|
|
88
|
+
* - If a sessionKey is provided, it logs out from that session; otherwise, it logs out from the active session.
|
|
89
|
+
* - Cleans up any api keys associated with the session.
|
|
90
|
+
*
|
|
91
|
+
* @param params.sessionKey - session key to specify which session to log out from (defaults to the active session).
|
|
92
|
+
* @returns A promise that resolves when the logout process is complete.
|
|
93
|
+
* @throws {TurnkeyError} If there is no active session or if there is an error during the logout process.
|
|
94
|
+
*/
|
|
95
|
+
this.logout = async (params) => {
|
|
96
|
+
try {
|
|
97
|
+
if (params?.sessionKey) {
|
|
98
|
+
const session = await this.storageManager.getSession(params.sessionKey);
|
|
99
|
+
this.storageManager.clearSession(params.sessionKey);
|
|
100
|
+
this.apiKeyStamper?.deleteKeyPair(session?.publicKey);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
const sessionKey = await this.storageManager.getActiveSessionKey();
|
|
104
|
+
const session = await this.storageManager.getActiveSession();
|
|
105
|
+
if (sessionKey) {
|
|
106
|
+
this.storageManager.clearSession(sessionKey);
|
|
107
|
+
this.apiKeyStamper?.deleteKeyPair(session?.publicKey);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
throw new sdkTypes.TurnkeyError("No active session found to log out from.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
116
|
+
throw error;
|
|
117
|
+
throw new sdkTypes.TurnkeyError(`Failed to log out`, sdkTypes.TurnkeyErrorCodes.LOGOUT_ERROR, error);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Logs in a user using a passkey, optionally specifying the public key, session key, and session expiration.
|
|
122
|
+
*
|
|
123
|
+
* - This function initiates the login process with a passkey and handles session creation and storage.
|
|
124
|
+
* - If a public key is not provided, a new key pair will be generated for authentication.
|
|
125
|
+
* - If a session key is not provided, the default session key will be used.
|
|
126
|
+
* - The session expiration can be customized via the expirationSeconds parameter.
|
|
127
|
+
* - Handles cleanup of unused key pairs if login fails.
|
|
128
|
+
*
|
|
129
|
+
* @param params.publicKey - public key to use for authentication. If not provided, a new key pair will be generated.
|
|
130
|
+
* @param params.sessionKey - session key to use for session creation (defaults to the default session key).
|
|
131
|
+
* @param params.expirationSeconds - session expiration time in seconds (defaults to the configured default).
|
|
132
|
+
* @returns A promise that resolves to a signed JWT session token.
|
|
133
|
+
* @throws {TurnkeyError} If there is an error during the passkey login process or if the user cancels the passkey prompt.
|
|
134
|
+
*/
|
|
135
|
+
this.loginWithPasskey = async (params) => {
|
|
136
|
+
let generatedKeyPair = null;
|
|
137
|
+
try {
|
|
138
|
+
const publicKey = params?.publicKey || (await this.apiKeyStamper?.createKeyPair());
|
|
139
|
+
const sessionKey = params?.sessionKey || base.SessionKey.DefaultSessionkey;
|
|
140
|
+
const expirationSeconds = params?.expirationSeconds || base.DEFAULT_SESSION_EXPIRATION_IN_SECONDS;
|
|
141
|
+
if (!publicKey) {
|
|
142
|
+
throw new sdkTypes.TurnkeyError("A publickey could not be found or generated.", sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
143
|
+
}
|
|
144
|
+
const sessionResponse = await this.httpClient.stampLogin({
|
|
145
|
+
publicKey,
|
|
146
|
+
organizationId: this.config.organizationId,
|
|
147
|
+
expirationSeconds,
|
|
148
|
+
}, base.StamperType.Passkey);
|
|
149
|
+
await this.storeSession({
|
|
150
|
+
sessionToken: sessionResponse.session,
|
|
151
|
+
sessionKey,
|
|
152
|
+
});
|
|
153
|
+
// Key pair was successfully used, set to null to prevent cleanup
|
|
154
|
+
generatedKeyPair = null;
|
|
155
|
+
return sessionResponse.session;
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
if (error?.message?.includes("timed out or was not allowed"))
|
|
159
|
+
throw new sdkTypes.TurnkeyError("Passkey login was cancelled by the user.", sdkTypes.TurnkeyErrorCodes.SELECT_PASSKEY_CANCELLED, error);
|
|
160
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
161
|
+
throw error;
|
|
162
|
+
throw new sdkTypes.TurnkeyError(`Unable to log in with the provided passkey`, sdkTypes.TurnkeyErrorCodes.PASSKEY_LOGIN_AUTH_ERROR, error);
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
// Clean up the generated key pair if it wasn't successfully used
|
|
166
|
+
if (generatedKeyPair) {
|
|
167
|
+
try {
|
|
168
|
+
await this.apiKeyStamper?.deleteKeyPair(generatedKeyPair);
|
|
169
|
+
}
|
|
170
|
+
catch (cleanupError) {
|
|
171
|
+
throw new sdkTypes.TurnkeyError(`Failed to clean up generated key pair`, sdkTypes.TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* Signs up a user using a passkey, creating a new sub-organization and session.
|
|
178
|
+
*
|
|
179
|
+
* - This function creates a new passkey authenticator and uses it to register a new sub-organization for the user.
|
|
180
|
+
* - Handles both passkey creation and sub-organization creation in a single flow.
|
|
181
|
+
* - Optionally accepts additional sub-organization parameters, a custom session key, a custom passkey display name, and a custom session expiration.
|
|
182
|
+
* - Automatically generates a new API key pair for authentication and session management.
|
|
183
|
+
* - Stores the resulting session token and manages cleanup of unused key pairs.
|
|
184
|
+
*
|
|
185
|
+
* @param params.createSubOrgParams - parameters for creating a sub-organization (e.g., authenticators, user metadata).
|
|
186
|
+
* @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
|
+
* @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.
|
|
190
|
+
* @throws {TurnkeyError} If there is an error during passkey creation, sub-organization creation, or session storage.
|
|
191
|
+
*/
|
|
192
|
+
this.signUpWithPasskey = async (params) => {
|
|
193
|
+
const { createSubOrgParams, passkeyDisplayName, sessionKey = base.SessionKey.DefaultSessionkey, expirationSeconds = base.DEFAULT_SESSION_EXPIRATION_IN_SECONDS, } = params || {};
|
|
194
|
+
let generatedKeyPair = null;
|
|
195
|
+
try {
|
|
196
|
+
generatedKeyPair = await this.apiKeyStamper?.createKeyPair();
|
|
197
|
+
const passkeyName = passkeyDisplayName || `passkey-${Date.now()}`;
|
|
198
|
+
// A passkey will be created automatically when you call this function. The name is passed in
|
|
199
|
+
const passkey = await this.createPasskey({
|
|
200
|
+
name: passkeyName,
|
|
201
|
+
displayName: passkeyName,
|
|
202
|
+
});
|
|
203
|
+
if (!passkey) {
|
|
204
|
+
throw new sdkTypes.TurnkeyError("Failed to create passkey: encoded challenge or attestation is missing", sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
205
|
+
}
|
|
206
|
+
const signUpBody = utils.buildSignUpBody({
|
|
207
|
+
createSubOrgParams: {
|
|
208
|
+
...createSubOrgParams,
|
|
209
|
+
authenticators: [
|
|
210
|
+
...(createSubOrgParams?.authenticators ?? []), // Any extra authenticators can be passed into createSubOrgParams
|
|
211
|
+
{
|
|
212
|
+
// Add our passkey that we made earlier.
|
|
213
|
+
authenticatorName: passkeyName, // Ensure the name in orgData is the same name as the created passkey
|
|
214
|
+
challenge: passkey.encodedChallenge,
|
|
215
|
+
attestation: passkey.attestation,
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
apiKeys: [
|
|
219
|
+
{
|
|
220
|
+
apiKeyName: `passkey-auth-${generatedKeyPair}`,
|
|
221
|
+
publicKey: generatedKeyPair,
|
|
222
|
+
curveType: "API_KEY_CURVE_P256",
|
|
223
|
+
expirationSeconds: "60",
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
const res = await this.httpClient.proxySignup(signUpBody);
|
|
229
|
+
if (!res) {
|
|
230
|
+
throw new sdkTypes.TurnkeyError(`Sign up failed`, sdkTypes.TurnkeyErrorCodes.PASSKEY_SIGNUP_AUTH_ERROR);
|
|
231
|
+
}
|
|
232
|
+
const newGeneratedKeyPair = await this.apiKeyStamper?.createKeyPair();
|
|
233
|
+
this.apiKeyStamper?.setPublicKeyOverride(generatedKeyPair);
|
|
234
|
+
const sessionResponse = await this.httpClient.stampLogin({
|
|
235
|
+
publicKey: newGeneratedKeyPair,
|
|
236
|
+
organizationId: this.config.organizationId,
|
|
237
|
+
expirationSeconds,
|
|
238
|
+
});
|
|
239
|
+
await this.apiKeyStamper?.deleteKeyPair(generatedKeyPair);
|
|
240
|
+
await this.storeSession({
|
|
241
|
+
sessionToken: sessionResponse.session,
|
|
242
|
+
sessionKey,
|
|
243
|
+
});
|
|
244
|
+
generatedKeyPair = null; // Key pair was successfully used, set to null to prevent cleanup
|
|
245
|
+
return sessionResponse.session;
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
249
|
+
throw error;
|
|
250
|
+
throw new sdkTypes.TurnkeyError(`Failed to sign up with passkey`, sdkTypes.TurnkeyErrorCodes.PASSKEY_SIGNUP_AUTH_ERROR, error);
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
// Clean up the generated key pair if it wasn't successfully used
|
|
254
|
+
this.apiKeyStamper?.clearPublicKeyOverride();
|
|
255
|
+
if (generatedKeyPair) {
|
|
256
|
+
try {
|
|
257
|
+
await this.apiKeyStamper?.deleteKeyPair(generatedKeyPair);
|
|
258
|
+
}
|
|
259
|
+
catch (cleanupError) {
|
|
260
|
+
throw new sdkTypes.TurnkeyError(`Failed to clean up generated key pair`, sdkTypes.TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* Retrieves wallet providers from the initialized wallet manager.
|
|
267
|
+
*
|
|
268
|
+
* - Optionally filters providers by the specified blockchain chain.
|
|
269
|
+
* - Throws an error if the wallet manager is not initialized.
|
|
270
|
+
*
|
|
271
|
+
* @param chain - optional blockchain chain to filter the returned providers.
|
|
272
|
+
* @returns A promise that resolves to an array of wallet providers.
|
|
273
|
+
* @throws {TurnkeyError} If the wallet manager is uninitialized or provider retrieval fails.
|
|
274
|
+
*/
|
|
275
|
+
this.getWalletProviders = async (chain) => {
|
|
276
|
+
try {
|
|
277
|
+
if (!this.walletManager) {
|
|
278
|
+
throw new sdkTypes.TurnkeyError("Wallet manager is not initialized", sdkTypes.TurnkeyErrorCodes.WALLET_MANAGER_COMPONENT_NOT_INITIALIZED);
|
|
279
|
+
}
|
|
280
|
+
return await this.walletManager.getProviders(chain);
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
throw new sdkTypes.TurnkeyError(`Unable to get wallet providers`, sdkTypes.TurnkeyErrorCodes.FETCH_WALLETS_ERROR, error);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
/**
|
|
287
|
+
* Connects the specified wallet account.
|
|
288
|
+
*
|
|
289
|
+
* - Requires the wallet manager and its connector to be initialized.
|
|
290
|
+
*
|
|
291
|
+
* @param walletProvider - wallet provider to connect.
|
|
292
|
+
* @returns A promise that resolves once the wallet account is connected.
|
|
293
|
+
* @throws {TurnkeyError} If the wallet manager is uninitialized or the connection fails.
|
|
294
|
+
*/
|
|
295
|
+
this.connectWalletAccount = async (walletProvider) => {
|
|
296
|
+
if (!this.walletManager?.connector) {
|
|
297
|
+
throw new sdkTypes.TurnkeyError("Wallet connector is not initialized", sdkTypes.TurnkeyErrorCodes.WALLET_MANAGER_COMPONENT_NOT_INITIALIZED);
|
|
298
|
+
}
|
|
299
|
+
try {
|
|
300
|
+
await this.walletManager.connector.connectWalletAccount(walletProvider);
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
throw new sdkTypes.TurnkeyError("Unable to connect wallet account", sdkTypes.TurnkeyErrorCodes.CONNECT_WALLET_ACCOUNT_ERROR, error);
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
/**
|
|
307
|
+
* Disconnects the specified wallet account.
|
|
308
|
+
*
|
|
309
|
+
* - Requires the wallet manager and its connector to be initialized.
|
|
310
|
+
*
|
|
311
|
+
* @param walletProvider - wallet provider to disconnect.
|
|
312
|
+
* @returns A promise that resolves once the wallet account is disconnected.
|
|
313
|
+
* @throws {TurnkeyError} If the wallet manager is uninitialized or the disconnection fails.
|
|
314
|
+
*/
|
|
315
|
+
this.disconnectWalletAccount = async (walletProvider) => {
|
|
316
|
+
if (!this.walletManager?.connector) {
|
|
317
|
+
throw new sdkTypes.TurnkeyError("Wallet connector is not initialized", sdkTypes.TurnkeyErrorCodes.WALLET_MANAGER_COMPONENT_NOT_INITIALIZED);
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
await this.walletManager.connector.disconnectWalletAccount(walletProvider);
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
throw new sdkTypes.TurnkeyError("Unable to disconnect wallet account", sdkTypes.TurnkeyErrorCodes.DISCONNECT_WALLET_ACCOUNT_ERROR, error);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
/**
|
|
327
|
+
* Switches the specified wallet provider to a different blockchain chain.
|
|
328
|
+
*
|
|
329
|
+
* - Requires the wallet manager and its connector to be initialized.
|
|
330
|
+
* - The wallet provider must have at least one connected address.
|
|
331
|
+
* - Does nothing if the wallet provider is already on the desired chain.
|
|
332
|
+
*
|
|
333
|
+
* @param walletProvider - wallet provider to switch.
|
|
334
|
+
* @param chainOrId - target chain as a chain ID string or SwitchableChain object.
|
|
335
|
+
* @returns A promise that resolves once the chain switch is complete.
|
|
336
|
+
* @throws {TurnkeyError} If the wallet manager is uninitialized, the provider is not connected, or the switch fails.
|
|
337
|
+
*/
|
|
338
|
+
this.switchWalletProviderChain = async (walletProvider, chainOrId) => {
|
|
339
|
+
if (!this.walletManager?.connector) {
|
|
340
|
+
throw new sdkTypes.TurnkeyError("Wallet connector is not initialized", sdkTypes.TurnkeyErrorCodes.WALLET_MANAGER_COMPONENT_NOT_INITIALIZED);
|
|
341
|
+
}
|
|
342
|
+
if (walletProvider.connectedAddresses.length === 0) {
|
|
343
|
+
throw new sdkTypes.TurnkeyError("You can not switch chains for a provider that is not connected", sdkTypes.TurnkeyErrorCodes.INVALID_REQUEST);
|
|
344
|
+
}
|
|
345
|
+
// if the wallet provider is already on the desired chain, do nothing
|
|
346
|
+
if (walletProvider.chainInfo.namespace === chainOrId) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
await this.walletManager.connector.switchChain(walletProvider, chainOrId);
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
throw new sdkTypes.TurnkeyError("Unable to switch wallet account chain", sdkTypes.TurnkeyErrorCodes.SWITCH_WALLET_CHAIN_ERROR, error);
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
/**
|
|
357
|
+
* Logs in a user using the specified wallet provider.
|
|
358
|
+
*
|
|
359
|
+
* - This function logs in a user by authenticating with the provided wallet provider via a wallet-based signature.
|
|
360
|
+
* - If a public key is not provided, a new one will be generated for authentication.
|
|
361
|
+
* - Optionally accepts a custom session key and session expiration time.
|
|
362
|
+
* - Stores the resulting session token under the specified session key, or the default session key if not provided.
|
|
363
|
+
* - Throws an error if a public key cannot be found or generated, or if the login process fails.
|
|
364
|
+
*
|
|
365
|
+
* @param params.walletProvider - wallet provider to use for authentication.
|
|
366
|
+
* @param params.publicKey - optional public key to associate with the session (generated if not provided).
|
|
367
|
+
* @param params.sessionKey - optional key to store the session under (defaults to the default session key).
|
|
368
|
+
* @param params.expirationSeconds - optional session expiration time in seconds (defaults to the configured default).
|
|
369
|
+
* @returns A promise that resolves to the created session token.
|
|
370
|
+
* @throws {TurnkeyError} If the wallet stamper is uninitialized, a public key cannot be found or generated, or login fails.
|
|
371
|
+
*/
|
|
372
|
+
this.loginWithWallet = async (params) => {
|
|
373
|
+
if (!this.walletManager?.stamper) {
|
|
374
|
+
throw new sdkTypes.TurnkeyError("Wallet stamper is not initialized", sdkTypes.TurnkeyErrorCodes.WALLET_MANAGER_COMPONENT_NOT_INITIALIZED);
|
|
375
|
+
}
|
|
376
|
+
try {
|
|
377
|
+
const publicKey = params.publicKey || (await this.apiKeyStamper?.createKeyPair());
|
|
378
|
+
const sessionKey = params.sessionKey || base.SessionKey.DefaultSessionkey;
|
|
379
|
+
const walletProvider = params.walletProvider;
|
|
380
|
+
const expirationSeconds = params?.expirationSeconds || base.DEFAULT_SESSION_EXPIRATION_IN_SECONDS;
|
|
381
|
+
if (!publicKey) {
|
|
382
|
+
throw new sdkTypes.TurnkeyError("A publickey could not be found or generated.", sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
383
|
+
}
|
|
384
|
+
this.walletManager.stamper.setProvider(walletProvider.interfaceType, walletProvider);
|
|
385
|
+
const sessionResponse = await this.httpClient.stampLogin({
|
|
386
|
+
publicKey,
|
|
387
|
+
organizationId: this.config.organizationId,
|
|
388
|
+
expirationSeconds,
|
|
389
|
+
}, base.StamperType.Wallet);
|
|
390
|
+
await this.storeSession({
|
|
391
|
+
sessionToken: sessionResponse.session,
|
|
392
|
+
sessionKey,
|
|
393
|
+
});
|
|
394
|
+
return sessionResponse.session;
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
398
|
+
throw error;
|
|
399
|
+
throw new sdkTypes.TurnkeyError(`Unable to log in with the provided wallet`, sdkTypes.TurnkeyErrorCodes.WALLET_LOGIN_AUTH_ERROR, error);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
/**
|
|
403
|
+
* Signs up a user using a wallet, creating a new sub-organization and session.
|
|
404
|
+
*
|
|
405
|
+
* - This function creates a new wallet authenticator and uses it to register a new sub-organization for the user.
|
|
406
|
+
* - Handles both wallet authentication and sub-organization creation in a single flow.
|
|
407
|
+
* - Optionally accepts additional sub-organization parameters, a custom session key, and a custom session expiration.
|
|
408
|
+
* - Automatically generates additional API key pairs for authentication and session management.
|
|
409
|
+
* - Stores the resulting session token under the specified session key, or the default session key if not provided, and manages cleanup of unused key pairs.
|
|
410
|
+
*
|
|
411
|
+
* @param params.walletProvider - wallet provider to use for authentication.
|
|
412
|
+
* @param params.createSubOrgParams - parameters for creating a sub-organization (e.g., authenticators, user metadata).
|
|
413
|
+
* @param params.sessionKey - session key to use for storing the session (defaults to the default session key).
|
|
414
|
+
* @param params.expirationSeconds - session expiration time in seconds (defaults to the configured default).
|
|
415
|
+
* @returns A promise that resolves to a signed JWT session token for the new sub-organization.
|
|
416
|
+
* @throws {TurnkeyError} If there is an error during wallet authentication, sub-organization creation, session storage, or cleanup.
|
|
417
|
+
*/
|
|
418
|
+
this.signUpWithWallet = async (params) => {
|
|
419
|
+
const { walletProvider, createSubOrgParams, sessionKey = base.SessionKey.DefaultSessionkey, expirationSeconds = base.DEFAULT_SESSION_EXPIRATION_IN_SECONDS, } = params;
|
|
420
|
+
if (!this.walletManager?.stamper) {
|
|
421
|
+
throw new sdkTypes.TurnkeyError("Wallet stamper is not initialized", sdkTypes.TurnkeyErrorCodes.WALLET_MANAGER_COMPONENT_NOT_INITIALIZED);
|
|
422
|
+
}
|
|
423
|
+
let generatedKeyPair = null;
|
|
424
|
+
try {
|
|
425
|
+
generatedKeyPair = await this.apiKeyStamper?.createKeyPair();
|
|
426
|
+
this.walletManager.stamper.setProvider(walletProvider.interfaceType, walletProvider);
|
|
427
|
+
const publicKey = await this.walletManager.stamper.getPublicKey(walletProvider.interfaceType, walletProvider);
|
|
428
|
+
if (!publicKey) {
|
|
429
|
+
throw new sdkTypes.TurnkeyError("Failed to get public key from wallet", sdkTypes.TurnkeyErrorCodes.WALLET_SIGNUP_AUTH_ERROR);
|
|
430
|
+
}
|
|
431
|
+
const signUpBody = utils.buildSignUpBody({
|
|
432
|
+
createSubOrgParams: {
|
|
433
|
+
...createSubOrgParams,
|
|
434
|
+
apiKeys: [
|
|
435
|
+
{
|
|
436
|
+
apiKeyName: `wallet-auth:${publicKey}`,
|
|
437
|
+
publicKey: publicKey,
|
|
438
|
+
curveType: utils.isEthereumWallet(walletProvider)
|
|
439
|
+
? "API_KEY_CURVE_SECP256K1"
|
|
440
|
+
: "API_KEY_CURVE_ED25519",
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
apiKeyName: `wallet-auth-${generatedKeyPair}`,
|
|
444
|
+
publicKey: generatedKeyPair,
|
|
445
|
+
curveType: "API_KEY_CURVE_P256",
|
|
446
|
+
expirationSeconds: "60",
|
|
447
|
+
},
|
|
448
|
+
],
|
|
449
|
+
},
|
|
450
|
+
});
|
|
451
|
+
const res = await this.httpClient.proxySignup(signUpBody);
|
|
452
|
+
if (!res) {
|
|
453
|
+
throw new sdkTypes.TurnkeyError(`Sign up failed`, sdkTypes.TurnkeyErrorCodes.WALLET_SIGNUP_AUTH_ERROR);
|
|
454
|
+
}
|
|
455
|
+
const newGeneratedKeyPair = await this.apiKeyStamper?.createKeyPair();
|
|
456
|
+
this.apiKeyStamper?.setPublicKeyOverride(generatedKeyPair);
|
|
457
|
+
const sessionResponse = await this.httpClient.stampLogin({
|
|
458
|
+
publicKey: newGeneratedKeyPair,
|
|
459
|
+
organizationId: this.config.organizationId,
|
|
460
|
+
expirationSeconds,
|
|
461
|
+
});
|
|
462
|
+
await this.apiKeyStamper?.deleteKeyPair(generatedKeyPair);
|
|
463
|
+
await this.storeSession({
|
|
464
|
+
sessionToken: sessionResponse.session,
|
|
465
|
+
sessionKey,
|
|
466
|
+
});
|
|
467
|
+
generatedKeyPair = null; // Key pair was successfully used, set to null to prevent cleanup
|
|
468
|
+
return sessionResponse.session;
|
|
469
|
+
}
|
|
470
|
+
catch (error) {
|
|
471
|
+
throw new sdkTypes.TurnkeyError("Failed to sign up with wallet", sdkTypes.TurnkeyErrorCodes.WALLET_SIGNUP_AUTH_ERROR, error);
|
|
472
|
+
}
|
|
473
|
+
finally {
|
|
474
|
+
// Clean up the generated key pair if it wasn't successfully used
|
|
475
|
+
this.apiKeyStamper?.clearPublicKeyOverride();
|
|
476
|
+
if (generatedKeyPair) {
|
|
477
|
+
try {
|
|
478
|
+
await this.apiKeyStamper?.deleteKeyPair(generatedKeyPair);
|
|
479
|
+
}
|
|
480
|
+
catch (cleanupError) {
|
|
481
|
+
throw new sdkTypes.TurnkeyError("Failed to clean up generated key pair", sdkTypes.TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
/**
|
|
487
|
+
* Logs in an existing user or signs up a new user using a wallet, creating a new sub-organization if needed.
|
|
488
|
+
*
|
|
489
|
+
* - This function attempts to log in the user by stamping a login request with the provided wallet.
|
|
490
|
+
* - If the wallet’s public key is not associated with an existing sub-organization, a new one is created.
|
|
491
|
+
* - Handles both wallet authentication and sub-organization creation in a single flow.
|
|
492
|
+
* - For Ethereum wallets, derives the public key from the signed request header; for Solana wallets, retrieves it directly from the wallet.
|
|
493
|
+
* - Optionally accepts additional sub-organization parameters, a custom session key, and a custom session expiration.
|
|
494
|
+
* - Stores the resulting session token under the specified session key, or the default session key if not provided.
|
|
495
|
+
*
|
|
496
|
+
* @param params.walletProvider - wallet provider to use for authentication.
|
|
497
|
+
* @param params.createSubOrgParams - optional parameters for creating a sub-organization (e.g., authenticators, user metadata).
|
|
498
|
+
* @param params.sessionKey - session key to use for storing the session (defaults to the default session key).
|
|
499
|
+
* @param params.expirationSeconds - session expiration time in seconds (defaults to the configured default).
|
|
500
|
+
* @returns A promise that resolves to a signed JWT session token for the sub-organization (new or existing).
|
|
501
|
+
* @throws {TurnkeyError} If there is an error during wallet authentication, sub-organization creation, or session storage.
|
|
502
|
+
*/
|
|
503
|
+
this.loginOrSignupWithWallet = async (params) => {
|
|
504
|
+
if (!this.walletManager?.stamper) {
|
|
505
|
+
throw new sdkTypes.TurnkeyError("Wallet stamper is not initialized", sdkTypes.TurnkeyErrorCodes.WALLET_MANAGER_COMPONENT_NOT_INITIALIZED);
|
|
506
|
+
}
|
|
507
|
+
const createSubOrgParams = params.createSubOrgParams;
|
|
508
|
+
const sessionKey = params.sessionKey || base.SessionKey.DefaultSessionkey;
|
|
509
|
+
const walletProvider = params.walletProvider;
|
|
510
|
+
const expirationSeconds = params.expirationSeconds || base.DEFAULT_SESSION_EXPIRATION_IN_SECONDS;
|
|
511
|
+
let generatedKeyPair = null;
|
|
512
|
+
try {
|
|
513
|
+
generatedKeyPair = await this.apiKeyStamper?.createKeyPair();
|
|
514
|
+
this.walletManager.stamper.setProvider(walletProvider.interfaceType, walletProvider);
|
|
515
|
+
// here we sign the request with the wallet, but we don't send it to the Turnkey yet
|
|
516
|
+
// this is because we need to check if the subOrg exists first, and create one if it doesn't
|
|
517
|
+
// once we have the subOrg for the publicKey, we then can send the request to the Turnkey
|
|
518
|
+
const signedRequest = await this.httpClient.stampStampLogin({
|
|
519
|
+
publicKey: generatedKeyPair,
|
|
520
|
+
organizationId: this.config.organizationId,
|
|
521
|
+
expirationSeconds,
|
|
522
|
+
}, base.StamperType.Wallet);
|
|
523
|
+
if (!signedRequest) {
|
|
524
|
+
throw new sdkTypes.TurnkeyError("Failed to create stamped request for wallet login", sdkTypes.TurnkeyErrorCodes.BAD_RESPONSE);
|
|
525
|
+
}
|
|
526
|
+
let publicKey;
|
|
527
|
+
switch (walletProvider.chainInfo.namespace) {
|
|
528
|
+
case base.Chain.Ethereum: {
|
|
529
|
+
// for Ethereum, there is no way to get the public key from the wallet address
|
|
530
|
+
// so we derive it from the signed request
|
|
531
|
+
publicKey = utils.getPublicKeyFromStampHeader(signedRequest.stamp.stampHeaderValue);
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
case base.Chain.Solana: {
|
|
535
|
+
// for Solana, we can get the public key from the wallet address
|
|
536
|
+
// since the wallet address is the public key
|
|
537
|
+
// this doesn't require any action from the user as long as the wallet is connected
|
|
538
|
+
// which it has to be since they just called stampStampLogin()
|
|
539
|
+
publicKey = await this.walletManager.stamper.getPublicKey(walletProvider.interfaceType, walletProvider);
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
default:
|
|
543
|
+
throw new sdkTypes.TurnkeyError(`Unsupported interface type: ${walletProvider.interfaceType}`, sdkTypes.TurnkeyErrorCodes.INVALID_REQUEST);
|
|
544
|
+
}
|
|
545
|
+
// here we check if the subOrg exists and create one
|
|
546
|
+
// then we send off the stamped request to the Turnkey
|
|
547
|
+
const accountRes = await this.httpClient.proxyGetAccount({
|
|
548
|
+
filterType: base.FilterType.PublicKey,
|
|
549
|
+
filterValue: publicKey,
|
|
550
|
+
});
|
|
551
|
+
if (!accountRes) {
|
|
552
|
+
throw new sdkTypes.TurnkeyError(`Account fetch failed`, sdkTypes.TurnkeyErrorCodes.ACCOUNT_FETCH_ERROR);
|
|
553
|
+
}
|
|
554
|
+
const subOrganizationId = accountRes.organizationId;
|
|
555
|
+
// if there is no subOrganizationId, we create one
|
|
556
|
+
if (!subOrganizationId) {
|
|
557
|
+
const signUpBody = utils.buildSignUpBody({
|
|
558
|
+
createSubOrgParams: {
|
|
559
|
+
...createSubOrgParams,
|
|
560
|
+
apiKeys: [
|
|
561
|
+
{
|
|
562
|
+
apiKeyName: `wallet-auth:${publicKey}`,
|
|
563
|
+
publicKey: publicKey,
|
|
564
|
+
curveType: utils.isEthereumWallet(walletProvider)
|
|
565
|
+
? "API_KEY_CURVE_SECP256K1"
|
|
566
|
+
: "API_KEY_CURVE_ED25519",
|
|
567
|
+
},
|
|
568
|
+
],
|
|
569
|
+
},
|
|
570
|
+
});
|
|
571
|
+
const res = await this.httpClient.proxySignup(signUpBody);
|
|
572
|
+
if (!res) {
|
|
573
|
+
throw new sdkTypes.TurnkeyError(`Sign up failed`, sdkTypes.TurnkeyErrorCodes.WALLET_SIGNUP_AUTH_ERROR);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
// now we can send the stamped request to the Turnkey
|
|
577
|
+
const headers = {
|
|
578
|
+
"Content-Type": "application/json",
|
|
579
|
+
[signedRequest.stamp.stampHeaderName]: signedRequest.stamp.stampHeaderValue,
|
|
580
|
+
};
|
|
581
|
+
const res = await fetch(signedRequest.url, {
|
|
582
|
+
method: "POST",
|
|
583
|
+
headers,
|
|
584
|
+
body: signedRequest.body,
|
|
585
|
+
});
|
|
586
|
+
if (!res.ok) {
|
|
587
|
+
const errorText = await res.text();
|
|
588
|
+
throw new sdkTypes.TurnkeyNetworkError(`Stamped request failed`, res.status, sdkTypes.TurnkeyErrorCodes.WALLET_LOGIN_AUTH_ERROR, errorText);
|
|
589
|
+
}
|
|
590
|
+
const sessionResponse = await res.json();
|
|
591
|
+
const sessionToken = sessionResponse.activity.result.stampLoginResult?.session;
|
|
592
|
+
if (!sessionToken) {
|
|
593
|
+
throw new sdkTypes.TurnkeyError("Session token not found in the response", sdkTypes.TurnkeyErrorCodes.BAD_RESPONSE);
|
|
594
|
+
}
|
|
595
|
+
await this.storeSession({
|
|
596
|
+
sessionToken: sessionToken,
|
|
597
|
+
sessionKey,
|
|
598
|
+
});
|
|
599
|
+
return sessionToken;
|
|
600
|
+
}
|
|
601
|
+
catch (error) {
|
|
602
|
+
throw new sdkTypes.TurnkeyError(`Unable to log in or signup with the provided wallet`, sdkTypes.TurnkeyErrorCodes.WALLET_LOGIN_OR_SIGNUP_ERROR, error);
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
/**
|
|
606
|
+
* Initializes the OTP process by sending an OTP code to the provided contact.
|
|
607
|
+
*
|
|
608
|
+
* - This function initiates the OTP flow by sending a one-time password (OTP) code to the user's contact information (email address or phone number) via the auth proxy.
|
|
609
|
+
* - Supports both email and SMS OTP types.
|
|
610
|
+
* - Returns an OTP ID that is required for subsequent OTP verification.
|
|
611
|
+
*
|
|
612
|
+
* @param params.otpType - type of OTP to initialize (OtpType.Email or OtpType.Sms).
|
|
613
|
+
* @param params.contact - contact information for the user (e.g., email address or phone number).
|
|
614
|
+
* @returns A promise that resolves to the OTP ID required for verification.
|
|
615
|
+
* @throws {TurnkeyError} If there is an error during the OTP initialization process or if the maximum number of OTPs has been reached.
|
|
616
|
+
*/
|
|
617
|
+
this.initOtp = async (params) => {
|
|
618
|
+
try {
|
|
619
|
+
const initOtpRes = await this.httpClient.proxyInitOtp(params);
|
|
620
|
+
if (!initOtpRes || !initOtpRes.otpId) {
|
|
621
|
+
throw new sdkTypes.TurnkeyError("Failed to initialize OTP: otpId is missing", sdkTypes.TurnkeyErrorCodes.INIT_OTP_ERROR);
|
|
622
|
+
}
|
|
623
|
+
return initOtpRes.otpId;
|
|
624
|
+
}
|
|
625
|
+
catch (error) {
|
|
626
|
+
if (error instanceof sdkTypes.TurnkeyNetworkError) {
|
|
627
|
+
if (error.message.includes("Max number of OTPs have been initiated")) {
|
|
628
|
+
throw new sdkTypes.TurnkeyError("Max number of OTPs have been initiated", sdkTypes.TurnkeyErrorCodes.MAX_OTP_INITIATED_ERROR);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
632
|
+
throw error;
|
|
633
|
+
throw new sdkTypes.TurnkeyError(`Failed to initialize OTP`, sdkTypes.TurnkeyErrorCodes.INIT_OTP_ERROR, error);
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
/**
|
|
637
|
+
* Verifies the OTP code sent to the user.
|
|
638
|
+
*
|
|
639
|
+
* - This function verifies the OTP code entered by the user against the OTP sent to their contact information (email or phone) using the auth proxy.
|
|
640
|
+
* - If verification is successful, it returns the sub-organization ID associated with the contact (if it exists) and a verification token.
|
|
641
|
+
* - The verification token can be used for subsequent login or sign-up flows.
|
|
642
|
+
* - Handles both email and SMS OTP types.
|
|
643
|
+
*
|
|
644
|
+
* @param params.otpId - ID of the OTP to verify (returned from `initOtp`).
|
|
645
|
+
* @param params.otpCode - OTP code entered by the user.
|
|
646
|
+
* @param params.contact - contact information for the user (e.g., email address or phone number).
|
|
647
|
+
* @param params.otpType - type of OTP being verified (OtpType.Email or OtpType.Sms).
|
|
648
|
+
* @returns A promise that resolves to an object containing:
|
|
649
|
+
* - subOrganizationId: sub-organization ID if the contact is already associated with a sub-organization, or an empty string if not.
|
|
650
|
+
* - verificationToken: verification token to be used for login or sign-up.
|
|
651
|
+
* @throws {TurnkeyError} If there is an error during the OTP verification process, such as an invalid code or network failure.
|
|
652
|
+
*/
|
|
653
|
+
this.verifyOtp = async (params) => {
|
|
654
|
+
const { otpId, otpCode, contact, otpType } = params;
|
|
655
|
+
try {
|
|
656
|
+
const verifyOtpRes = await this.httpClient.proxyVerifyOtp({
|
|
657
|
+
otpId: otpId,
|
|
658
|
+
otpCode: otpCode,
|
|
659
|
+
});
|
|
660
|
+
if (!verifyOtpRes) {
|
|
661
|
+
throw new sdkTypes.TurnkeyError(`OTP verification failed`, sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
662
|
+
}
|
|
663
|
+
const accountRes = await this.httpClient.proxyGetAccount({
|
|
664
|
+
filterType: base.OtpTypeToFilterTypeMap[otpType],
|
|
665
|
+
filterValue: contact,
|
|
666
|
+
});
|
|
667
|
+
if (!accountRes) {
|
|
668
|
+
throw new sdkTypes.TurnkeyError(`Account fetch failed`, sdkTypes.TurnkeyErrorCodes.ACCOUNT_FETCH_ERROR);
|
|
669
|
+
}
|
|
670
|
+
const subOrganizationId = accountRes.organizationId;
|
|
671
|
+
return {
|
|
672
|
+
subOrganizationId: subOrganizationId,
|
|
673
|
+
verificationToken: verifyOtpRes.verificationToken,
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
catch (error) {
|
|
677
|
+
if (error instanceof base.TurnkeyRequestError &&
|
|
678
|
+
error.message.includes("Invalid OTP code")) {
|
|
679
|
+
throw new sdkTypes.TurnkeyError("Invalid OTP code provided", sdkTypes.TurnkeyErrorCodes.INVALID_OTP_CODE, error.message);
|
|
680
|
+
}
|
|
681
|
+
else if (error instanceof sdkTypes.TurnkeyError)
|
|
682
|
+
throw error;
|
|
683
|
+
throw new sdkTypes.TurnkeyError(`Failed to verify OTP`, sdkTypes.TurnkeyErrorCodes.VERIFY_OTP_ERROR, error);
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
/**
|
|
687
|
+
* Logs in a user using an OTP verification token.
|
|
688
|
+
*
|
|
689
|
+
* - This function logs in a user using the verification token received after OTP verification (from email or SMS).
|
|
690
|
+
* - If a public key is not provided, a new API key pair will be generated for authentication.
|
|
691
|
+
* - Optionally invalidates any existing sessions for the user if `invalidateExisting` is set to true.
|
|
692
|
+
* - Stores the resulting session token under the specified session key, or the default session key if not provided.
|
|
693
|
+
* - Handles cleanup of unused key pairs if login fails.
|
|
694
|
+
*
|
|
695
|
+
* @param params.verificationToken - verification token received after OTP verification.
|
|
696
|
+
* @param params.publicKey - public key to use for authentication. If not provided, a new key pair will be generated.
|
|
697
|
+
* @param params.invalidateExisting - flag to invalidate existing session for the user.
|
|
698
|
+
* @param params.sessionKey - session key to use for session creation (defaults to the default session key).
|
|
699
|
+
* @returns A promise that resolves to a signed JWT session token.
|
|
700
|
+
* @throws {TurnkeyError} If there is an error during the OTP login process or if key pair cleanup fails.
|
|
701
|
+
*/
|
|
702
|
+
this.loginWithOtp = async (params) => {
|
|
703
|
+
const { verificationToken, invalidateExisting = false, publicKey = await this.apiKeyStamper?.createKeyPair(), sessionKey = base.SessionKey.DefaultSessionkey, } = params;
|
|
704
|
+
try {
|
|
705
|
+
const res = await this.httpClient.proxyOtpLogin({
|
|
706
|
+
verificationToken,
|
|
707
|
+
publicKey: publicKey,
|
|
708
|
+
invalidateExisting,
|
|
709
|
+
});
|
|
710
|
+
if (!res) {
|
|
711
|
+
throw new sdkTypes.TurnkeyError(`Auth proxy OTP login failed`, sdkTypes.TurnkeyErrorCodes.OTP_LOGIN_ERROR);
|
|
712
|
+
}
|
|
713
|
+
const loginRes = await res;
|
|
714
|
+
if (!loginRes.session) {
|
|
715
|
+
throw new sdkTypes.TurnkeyError("No session returned from OTP login", sdkTypes.TurnkeyErrorCodes.OTP_LOGIN_ERROR);
|
|
716
|
+
}
|
|
717
|
+
await this.storeSession({
|
|
718
|
+
sessionToken: loginRes.session,
|
|
719
|
+
sessionKey,
|
|
720
|
+
});
|
|
721
|
+
return loginRes.session;
|
|
722
|
+
}
|
|
723
|
+
catch (error) {
|
|
724
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
725
|
+
throw error;
|
|
726
|
+
// Clean up the generated key pair if it wasn't successfully used
|
|
727
|
+
if (publicKey) {
|
|
728
|
+
try {
|
|
729
|
+
await this.apiKeyStamper?.deleteKeyPair(publicKey);
|
|
730
|
+
}
|
|
731
|
+
catch (cleanupError) {
|
|
732
|
+
throw new sdkTypes.TurnkeyError(`Failed to clean up generated key pair`, sdkTypes.TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
throw new sdkTypes.TurnkeyError(`Failed to log in with OTP`, sdkTypes.TurnkeyErrorCodes.OTP_LOGIN_ERROR, error);
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
/**
|
|
739
|
+
* Signs up a user using an OTP verification token.
|
|
740
|
+
*
|
|
741
|
+
* - This function signs up a user using the verification token received after OTP verification (from email or SMS).
|
|
742
|
+
* - Creates a new sub-organization for the user with the provided parameters and associates the contact (email or phone) with the sub-organization.
|
|
743
|
+
* - Automatically generates a new API key pair for authentication and session management.
|
|
744
|
+
* - Stores the resulting session token under the specified session key, or the default session key if not provided.
|
|
745
|
+
* - Handles both email and SMS OTP types, and supports additional sub-organization creation parameters.
|
|
746
|
+
*
|
|
747
|
+
* @param params.verificationToken - verification token received after OTP verification.
|
|
748
|
+
* @param params.contact - contact information for the user (e.g., email address or phone number).
|
|
749
|
+
* @param params.otpType - type of OTP being used (OtpType.Email or OtpType.Sms).
|
|
750
|
+
* @param params.createSubOrgParams - parameters for creating a sub-organization (e.g., authenticators, user metadata).
|
|
751
|
+
* @param params.invalidateExisting - flag to invalidate existing session for the user.
|
|
752
|
+
* @param params.sessionKey - session key to use for session creation (defaults to the default session key).
|
|
753
|
+
* @returns A promise that resolves to a signed JWT session token for the new sub-organization.
|
|
754
|
+
* @throws {TurnkeyError} If there is an error during the OTP sign-up process or session storage.
|
|
755
|
+
*/
|
|
756
|
+
this.signUpWithOtp = async (params) => {
|
|
757
|
+
const { verificationToken, contact, otpType, createSubOrgParams, invalidateExisting, sessionKey, } = params;
|
|
758
|
+
const signUpBody = utils.buildSignUpBody({
|
|
759
|
+
createSubOrgParams: {
|
|
760
|
+
...createSubOrgParams,
|
|
761
|
+
...(otpType === base.OtpType.Email
|
|
762
|
+
? { userEmail: contact }
|
|
763
|
+
: { userPhoneNumber: contact }),
|
|
764
|
+
verificationToken,
|
|
765
|
+
},
|
|
766
|
+
});
|
|
767
|
+
const generatedKeyPair = await this.apiKeyStamper?.createKeyPair();
|
|
768
|
+
try {
|
|
769
|
+
const res = await this.httpClient.proxySignup(signUpBody);
|
|
770
|
+
if (!res) {
|
|
771
|
+
throw new sdkTypes.TurnkeyError(`Auth proxy OTP sign up failed`, sdkTypes.TurnkeyErrorCodes.OTP_SIGNUP_ERROR);
|
|
772
|
+
}
|
|
773
|
+
return await this.loginWithOtp({
|
|
774
|
+
verificationToken,
|
|
775
|
+
publicKey: generatedKeyPair,
|
|
776
|
+
...(invalidateExisting && { invalidateExisting }),
|
|
777
|
+
...(sessionKey && { sessionKey }),
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
catch (error) {
|
|
781
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
782
|
+
throw error;
|
|
783
|
+
throw new sdkTypes.TurnkeyError(`Failed to sign up with OTP`, sdkTypes.TurnkeyErrorCodes.OTP_SIGNUP_ERROR, error);
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
/**
|
|
787
|
+
* Completes the OTP authentication flow by verifying the OTP code and then either signing up or logging in the user.
|
|
788
|
+
*
|
|
789
|
+
* - This function first verifies the OTP code for the provided contact and OTP type.
|
|
790
|
+
* - If the contact is not associated with an existing sub-organization, it will automatically create a new sub-organization and complete the sign-up flow.
|
|
791
|
+
* - If the contact is already associated with a sub-organization, it will complete the login flow.
|
|
792
|
+
* - Supports passing a custom public key for authentication, invalidating existing session, specifying a session key, and providing additional sub-organization creation parameters.
|
|
793
|
+
* - Handles both email and SMS OTP types.
|
|
794
|
+
*
|
|
795
|
+
* @param params.otpId - ID of the OTP to complete (returned from `initOtp`).
|
|
796
|
+
* @param params.otpCode - OTP code entered by the user.
|
|
797
|
+
* @param params.contact - contact information for the user (e.g., email address or phone number).
|
|
798
|
+
* @param params.otpType - type of OTP being completed (OtpType.Email or OtpType.Sms).
|
|
799
|
+
* @param params.publicKey - public key to use for authentication. If not provided, a new key pair may be generated.
|
|
800
|
+
* @param params.invalidateExisting - flag to invalidate existing sessions for the user.
|
|
801
|
+
* @param params.sessionKey - session key to use for session creation (defaults to the default session key).
|
|
802
|
+
* @param params.createSubOrgParams - parameters for sub-organization creation (e.g., authenticators, user metadata).
|
|
803
|
+
* @returns A promise that resolves to a signed JWT session token for the user.
|
|
804
|
+
* @throws {TurnkeyError} If there is an error during OTP verification, sign-up, or login.
|
|
805
|
+
*/
|
|
806
|
+
this.completeOtp = async (params) => {
|
|
807
|
+
const { otpId, otpCode, contact, otpType, publicKey, invalidateExisting = false, sessionKey, createSubOrgParams, } = params;
|
|
808
|
+
try {
|
|
809
|
+
const { subOrganizationId, verificationToken } = await this.verifyOtp({
|
|
810
|
+
otpId: otpId,
|
|
811
|
+
otpCode: otpCode,
|
|
812
|
+
contact: contact,
|
|
813
|
+
otpType: otpType,
|
|
814
|
+
});
|
|
815
|
+
if (!verificationToken) {
|
|
816
|
+
throw new sdkTypes.TurnkeyError("No verification token returned from OTP verification", sdkTypes.TurnkeyErrorCodes.VERIFY_OTP_ERROR);
|
|
817
|
+
}
|
|
818
|
+
if (!subOrganizationId) {
|
|
819
|
+
return await this.signUpWithOtp({
|
|
820
|
+
verificationToken,
|
|
821
|
+
contact: contact,
|
|
822
|
+
otpType: otpType,
|
|
823
|
+
...(createSubOrgParams && {
|
|
824
|
+
createSubOrgParams,
|
|
825
|
+
}),
|
|
826
|
+
...(invalidateExisting && { invalidateExisting }),
|
|
827
|
+
...(sessionKey && { sessionKey }),
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
return await this.loginWithOtp({
|
|
832
|
+
verificationToken,
|
|
833
|
+
...(publicKey && { publicKey }),
|
|
834
|
+
...(invalidateExisting && { invalidateExisting }),
|
|
835
|
+
...(sessionKey && { sessionKey }),
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
catch (error) {
|
|
840
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
841
|
+
throw error;
|
|
842
|
+
throw new sdkTypes.TurnkeyError(`Failed to complete OTP process`, sdkTypes.TurnkeyErrorCodes.OTP_COMPLETION_ERROR, error);
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
/**
|
|
846
|
+
* Completes the OAuth authentication flow by either signing up or logging in the user, depending on whether a sub-organization already exists for the provided OIDC token.
|
|
847
|
+
*
|
|
848
|
+
* - This function first checks if there is an existing sub-organization associated with the OIDC token.
|
|
849
|
+
* - If a sub-organization exists, it proceeds with the OAuth login flow.
|
|
850
|
+
* - If no sub-organization exists, it creates a new sub-organization and completes the sign-up flow.
|
|
851
|
+
* - Optionally accepts a custom OAuth provider name, session key, and additional sub-organization creation parameters.
|
|
852
|
+
* - Handles session storage and management, and supports invalidating existing sessions if specified.
|
|
853
|
+
*
|
|
854
|
+
* @param params.oidcToken - OIDC token received after successful authentication with the OAuth provider.
|
|
855
|
+
* @param params.publicKey - public key to use for authentication. Must be generated prior to calling this function.
|
|
856
|
+
* @param params.providerName - name of the OAuth provider (defaults to a generated name with a timestamp).
|
|
857
|
+
* @param params.sessionKey - session key to use for session creation (defaults to the default session key).
|
|
858
|
+
* @param params.invalidateExisting - flag to invalidate existing sessions for the user.
|
|
859
|
+
* @param params.createSubOrgParams - parameters for sub-organization creation (e.g., authenticators, user metadata).
|
|
860
|
+
* @returns A promise that resolves to a signed JWT session token for the user.
|
|
861
|
+
* @throws {TurnkeyError} If there is an error during the OAuth completion process, such as account lookup, sign-up, or login.
|
|
862
|
+
*/
|
|
863
|
+
this.completeOauth = async (params) => {
|
|
864
|
+
const { oidcToken, publicKey, createSubOrgParams, providerName = "OpenID Connect Provider" + Date.now(), sessionKey = base.SessionKey.DefaultSessionkey, invalidateExisting = false, } = params;
|
|
865
|
+
try {
|
|
866
|
+
const accountRes = await this.httpClient.proxyGetAccount({
|
|
867
|
+
filterType: "OIDC_TOKEN",
|
|
868
|
+
filterValue: oidcToken,
|
|
869
|
+
});
|
|
870
|
+
if (!accountRes) {
|
|
871
|
+
throw new sdkTypes.TurnkeyError(`Account fetch failed`, sdkTypes.TurnkeyErrorCodes.ACCOUNT_FETCH_ERROR);
|
|
872
|
+
}
|
|
873
|
+
const subOrganizationId = accountRes.organizationId;
|
|
874
|
+
if (subOrganizationId) {
|
|
875
|
+
return this.loginWithOauth({
|
|
876
|
+
oidcToken,
|
|
877
|
+
publicKey,
|
|
878
|
+
invalidateExisting,
|
|
879
|
+
sessionKey,
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
else {
|
|
883
|
+
return this.signUpWithOauth({
|
|
884
|
+
oidcToken,
|
|
885
|
+
publicKey,
|
|
886
|
+
providerName,
|
|
887
|
+
...(createSubOrgParams && {
|
|
888
|
+
createSubOrgParams,
|
|
889
|
+
}),
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
catch (error) {
|
|
894
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
895
|
+
throw error;
|
|
896
|
+
throw new sdkTypes.TurnkeyError(`Failed to handle Google OAuth login`, sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR, error);
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
/**
|
|
900
|
+
* Logs in a user using OAuth authentication.
|
|
901
|
+
*
|
|
902
|
+
* - This function logs in a user using the provided OIDC token and public key.
|
|
903
|
+
* - Optionally invalidates any existing sessions for the user if `invalidateExisting` is set to true.
|
|
904
|
+
* - Stores the resulting session token under the specified session key, or the default session key if not provided.
|
|
905
|
+
* - Handles cleanup of unused key pairs if login fails.
|
|
906
|
+
*
|
|
907
|
+
* @param params.oidcToken - OIDC token received after successful authentication with the OAuth provider.
|
|
908
|
+
* @param params.publicKey - public key to use for authentication. Must be generated prior to calling this function.
|
|
909
|
+
* @param params.invalidateExisting - flag to invalidate existing sessions for the user.
|
|
910
|
+
* @param params.sessionKey - session key to use for session creation (defaults to the default session key).
|
|
911
|
+
* @returns A promise that resolves to a signed JWT session token.
|
|
912
|
+
* @throws {TurnkeyError} If there is an error during the OAuth login process or if key pair cleanup fails.
|
|
913
|
+
*/
|
|
914
|
+
this.loginWithOauth = async (params) => {
|
|
915
|
+
const { oidcToken, invalidateExisting = false, publicKey, sessionKey = base.SessionKey.DefaultSessionkey, } = params;
|
|
916
|
+
if (!publicKey) {
|
|
917
|
+
throw new sdkTypes.TurnkeyError("Public key must be provided to log in with OAuth. Please create a key pair first.", sdkTypes.TurnkeyErrorCodes.MISSING_PARAMS);
|
|
918
|
+
}
|
|
919
|
+
try {
|
|
920
|
+
const loginRes = await this.httpClient.proxyOAuthLogin({
|
|
921
|
+
oidcToken,
|
|
922
|
+
publicKey,
|
|
923
|
+
invalidateExisting,
|
|
924
|
+
});
|
|
925
|
+
if (!loginRes) {
|
|
926
|
+
throw new sdkTypes.TurnkeyError(`Auth proxy OAuth login failed`, sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
|
|
927
|
+
}
|
|
928
|
+
if (!loginRes.session) {
|
|
929
|
+
throw new sdkTypes.TurnkeyError("No session returned from oauth login", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
|
|
930
|
+
}
|
|
931
|
+
await this.storeSession({
|
|
932
|
+
sessionToken: loginRes.session,
|
|
933
|
+
sessionKey,
|
|
934
|
+
});
|
|
935
|
+
return loginRes.session;
|
|
936
|
+
}
|
|
937
|
+
catch (error) {
|
|
938
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
939
|
+
throw error;
|
|
940
|
+
// Clean up the generated key pair if it wasn't successfully used
|
|
941
|
+
if (publicKey) {
|
|
942
|
+
try {
|
|
943
|
+
await this.apiKeyStamper?.deleteKeyPair(publicKey);
|
|
944
|
+
}
|
|
945
|
+
catch (cleanupError) {
|
|
946
|
+
throw new sdkTypes.TurnkeyError(`Failed to clean up generated key pair`, sdkTypes.TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
throw new sdkTypes.TurnkeyError(`Failed to log in with oauth`, sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR, error);
|
|
950
|
+
}
|
|
951
|
+
};
|
|
952
|
+
/**
|
|
953
|
+
* Signs up a user using OAuth authentication.
|
|
954
|
+
*
|
|
955
|
+
* - This function creates a new sub-organization for the user using the provided OIDC token, public key, and provider name.
|
|
956
|
+
* - Handles the full OAuth sign-up flow, including sub-organization creation and session management.
|
|
957
|
+
* - Optionally accepts additional sub-organization creation parameters and a custom session key.
|
|
958
|
+
* - After successful sign-up, automatically logs in the user and returns a signed JWT session token.
|
|
959
|
+
*
|
|
960
|
+
* @param params.oidcToken - OIDC token received after successful authentication with the OAuth provider.
|
|
961
|
+
* @param params.publicKey - public key to associate with the new sub-organization.
|
|
962
|
+
* @param params.providerName - name of the OAuth provider (e.g., "Google", "Apple").
|
|
963
|
+
* @param params.createSubOrgParams - parameters for sub-organization creation (e.g., authenticators, user metadata).
|
|
964
|
+
* @param params.sessionKey - session key to use for session creation (defaults to the default session key).
|
|
965
|
+
* @returns A promise that resolves to a signed JWT session token for the new sub-organization.
|
|
966
|
+
* @throws {TurnkeyError} If there is an error during the OAuth sign-up or login process.
|
|
967
|
+
*/
|
|
968
|
+
this.signUpWithOauth = async (params) => {
|
|
969
|
+
const { oidcToken, publicKey, providerName, createSubOrgParams } = params;
|
|
970
|
+
try {
|
|
971
|
+
const signUpBody = utils.buildSignUpBody({
|
|
972
|
+
createSubOrgParams: {
|
|
973
|
+
...createSubOrgParams,
|
|
974
|
+
oauthProviders: [
|
|
975
|
+
{
|
|
976
|
+
providerName,
|
|
977
|
+
oidcToken,
|
|
978
|
+
},
|
|
979
|
+
],
|
|
980
|
+
},
|
|
981
|
+
});
|
|
982
|
+
const res = await this.httpClient.proxySignup(signUpBody);
|
|
983
|
+
if (!res) {
|
|
984
|
+
throw new sdkTypes.TurnkeyError(`Auth proxy OAuth signup failed`, sdkTypes.TurnkeyErrorCodes.OAUTH_SIGNUP_ERROR);
|
|
985
|
+
}
|
|
986
|
+
return await this.loginWithOauth({
|
|
987
|
+
oidcToken,
|
|
988
|
+
publicKey: publicKey,
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
catch (error) {
|
|
992
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
993
|
+
throw error;
|
|
994
|
+
throw new sdkTypes.TurnkeyError(`Failed to sign up with OAuth`, sdkTypes.TurnkeyErrorCodes.OAUTH_SIGNUP_ERROR, error);
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
/**
|
|
998
|
+
* Fetches all wallets for the current user, including both embedded and connected wallets.
|
|
999
|
+
*
|
|
1000
|
+
* - Retrieves all wallets associated with the organizationId from the current active session.
|
|
1001
|
+
* - For each embedded wallet, automatically fetches and attaches all associated wallet accounts.
|
|
1002
|
+
* - For connected wallets (e.g., browser extensions or external providers), groups providers by wallet name and attaches all connected accounts.
|
|
1003
|
+
* - Returns both embedded and connected wallets in a single array, each with their respective accounts populated.
|
|
1004
|
+
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1005
|
+
*
|
|
1006
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1007
|
+
* @returns A promise that resolves to an array of `Wallet` objects.
|
|
1008
|
+
* @throws {TurnkeyError} If no active session is found or if there is an error fetching wallets.
|
|
1009
|
+
*/
|
|
1010
|
+
this.fetchWallets = async (params) => {
|
|
1011
|
+
const { stampWith } = params || {};
|
|
1012
|
+
const session = await this.storageManager.getActiveSession();
|
|
1013
|
+
if (!session) {
|
|
1014
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1015
|
+
}
|
|
1016
|
+
try {
|
|
1017
|
+
const res = await this.httpClient.getWallets({ organizationId: session.organizationId }, stampWith);
|
|
1018
|
+
if (!res || !res.wallets) {
|
|
1019
|
+
throw new sdkTypes.TurnkeyError("No wallets found in the response", sdkTypes.TurnkeyErrorCodes.BAD_RESPONSE);
|
|
1020
|
+
}
|
|
1021
|
+
const embedded = await Promise.all(res.wallets.map(async (wallet) => {
|
|
1022
|
+
const embeddedWallet = {
|
|
1023
|
+
...wallet,
|
|
1024
|
+
source: base.WalletSource.Embedded,
|
|
1025
|
+
accounts: [],
|
|
1026
|
+
};
|
|
1027
|
+
const accounts = await this.fetchWalletAccounts({
|
|
1028
|
+
wallet: embeddedWallet,
|
|
1029
|
+
...(stampWith !== undefined && { stampWith }),
|
|
1030
|
+
});
|
|
1031
|
+
embeddedWallet.accounts = accounts;
|
|
1032
|
+
return embeddedWallet;
|
|
1033
|
+
}));
|
|
1034
|
+
// if wallet connecting is disabled we return only embedded wallets
|
|
1035
|
+
if (!this.walletManager?.connector)
|
|
1036
|
+
return embedded;
|
|
1037
|
+
const providers = await this.getWalletProviders();
|
|
1038
|
+
const groupedProviders = new Map();
|
|
1039
|
+
for (const provider of providers) {
|
|
1040
|
+
const walletId = provider.info?.name?.toLowerCase().replace(/\s+/g, "-") || "unknown";
|
|
1041
|
+
const group = groupedProviders.get(walletId) || [];
|
|
1042
|
+
group.push(provider);
|
|
1043
|
+
groupedProviders.set(walletId, group);
|
|
1044
|
+
}
|
|
1045
|
+
const connected = (await Promise.all(Array.from(groupedProviders.entries()).map(async ([walletId, grouped]) => {
|
|
1046
|
+
const timestamp = utils.toExternalTimestamp();
|
|
1047
|
+
const wallet = {
|
|
1048
|
+
source: base.WalletSource.Connected,
|
|
1049
|
+
walletId,
|
|
1050
|
+
walletName: grouped[0]?.info?.name ?? "Unknown",
|
|
1051
|
+
createdAt: timestamp,
|
|
1052
|
+
updatedAt: timestamp,
|
|
1053
|
+
exported: false,
|
|
1054
|
+
imported: false,
|
|
1055
|
+
accounts: [],
|
|
1056
|
+
};
|
|
1057
|
+
const accounts = await this.fetchWalletAccounts({
|
|
1058
|
+
wallet,
|
|
1059
|
+
walletProviders: grouped,
|
|
1060
|
+
...(stampWith !== undefined && { stampWith }),
|
|
1061
|
+
});
|
|
1062
|
+
wallet.accounts = accounts;
|
|
1063
|
+
return wallet;
|
|
1064
|
+
}))).filter((wallet) => wallet.accounts.length > 0);
|
|
1065
|
+
return [...embedded, ...connected];
|
|
1066
|
+
}
|
|
1067
|
+
catch (error) {
|
|
1068
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1069
|
+
throw error;
|
|
1070
|
+
throw new sdkTypes.TurnkeyError("Failed to fetch wallets", sdkTypes.TurnkeyErrorCodes.FETCH_WALLETS_ERROR, error);
|
|
1071
|
+
}
|
|
1072
|
+
};
|
|
1073
|
+
/**
|
|
1074
|
+
* Fetches all accounts for a specific wallet, including both embedded and connected wallet accounts.
|
|
1075
|
+
*
|
|
1076
|
+
* - For embedded wallets, retrieves accounts from the Turnkey API, supporting pagination (defaults to the first page with a limit of 100 accounts).
|
|
1077
|
+
* - For connected wallets (e.g., browser extensions or external providers), constructs account objects for each connected address from the provided or discovered wallet providers.
|
|
1078
|
+
* - Automatically determines the account type and populates relevant fields such as address, curve, and signing capability.
|
|
1079
|
+
* - Optionally allows filtering by a specific set of wallet providers and supports custom pagination options.
|
|
1080
|
+
* - Supports stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1081
|
+
*
|
|
1082
|
+
* @param params.wallet - wallet for which to fetch accounts.
|
|
1083
|
+
* @param params.walletProviders - list of wallet providers to filter by (used for connected wallets).
|
|
1084
|
+
* @param params.paginationOptions - pagination options for embedded wallets.
|
|
1085
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1086
|
+
* @returns A promise that resolves to an array of `v1WalletAccount` objects.
|
|
1087
|
+
* @throws {TurnkeyError} If no active session is found or if there is an error fetching wallet accounts.
|
|
1088
|
+
*/
|
|
1089
|
+
this.fetchWalletAccounts = async (params) => {
|
|
1090
|
+
const { wallet, stampWith, walletProviders, paginationOptions } = params;
|
|
1091
|
+
const session = await this.storageManager.getActiveSession();
|
|
1092
|
+
if (!session) {
|
|
1093
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1094
|
+
}
|
|
1095
|
+
// this is an embedded wallet so we fetch accounts from Turnkey
|
|
1096
|
+
if (wallet.source === base.WalletSource.Embedded) {
|
|
1097
|
+
const embedded = [];
|
|
1098
|
+
const res = await this.httpClient.getWalletAccounts({
|
|
1099
|
+
walletId: wallet.walletId,
|
|
1100
|
+
organizationId: session.organizationId,
|
|
1101
|
+
paginationOptions: paginationOptions || { limit: "100" },
|
|
1102
|
+
}, stampWith);
|
|
1103
|
+
if (!res || !res.accounts) {
|
|
1104
|
+
throw new sdkTypes.TurnkeyError("No wallet accounts found in the response", sdkTypes.TurnkeyErrorCodes.BAD_RESPONSE);
|
|
1105
|
+
}
|
|
1106
|
+
for (const account of res.accounts) {
|
|
1107
|
+
embedded.push({
|
|
1108
|
+
...account,
|
|
1109
|
+
source: base.WalletSource.Embedded,
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
return embedded;
|
|
1113
|
+
}
|
|
1114
|
+
// this is an external wallet so we fetch accounts from the connected wallet provider
|
|
1115
|
+
// if wallet connecting is disabled we return only embedded wallets
|
|
1116
|
+
// we should never reach this point if wallet connecting is disabled
|
|
1117
|
+
if (!this.walletManager?.connector)
|
|
1118
|
+
return [];
|
|
1119
|
+
const connected = [];
|
|
1120
|
+
const providers = walletProviders ?? (await this.getWalletProviders());
|
|
1121
|
+
const matching = providers.filter((p) => p.info?.name?.toLowerCase().replace(/\s+/g, "-") === wallet.walletId &&
|
|
1122
|
+
p.connectedAddresses.length > 0);
|
|
1123
|
+
for (const provider of matching) {
|
|
1124
|
+
const timestamp = utils.toExternalTimestamp();
|
|
1125
|
+
for (const address of provider.connectedAddresses) {
|
|
1126
|
+
const account = {
|
|
1127
|
+
walletAccountId: `${wallet.walletId}-${provider.interfaceType}-${address}`,
|
|
1128
|
+
organizationId: session.organizationId,
|
|
1129
|
+
walletId: wallet.walletId,
|
|
1130
|
+
curve: utils.isEthereumWallet(provider) ? base.Curve.SECP256K1 : base.Curve.ED25519,
|
|
1131
|
+
pathFormat: "PATH_FORMAT_BIP32",
|
|
1132
|
+
path: base.WalletSource.Connected,
|
|
1133
|
+
source: base.WalletSource.Connected,
|
|
1134
|
+
addressFormat: utils.isEthereumWallet(provider)
|
|
1135
|
+
? "ADDRESS_FORMAT_ETHEREUM"
|
|
1136
|
+
: "ADDRESS_FORMAT_SOLANA",
|
|
1137
|
+
address,
|
|
1138
|
+
createdAt: timestamp,
|
|
1139
|
+
updatedAt: timestamp,
|
|
1140
|
+
...utils.getWalletAccountMethods(this.walletManager.connector.sign.bind(this.walletManager.connector), provider),
|
|
1141
|
+
...(utils.isSolanaWallet(provider) && { publicKey: address }),
|
|
1142
|
+
};
|
|
1143
|
+
connected.push(account);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
return connected;
|
|
1147
|
+
};
|
|
1148
|
+
/**
|
|
1149
|
+
* Signs a message using the specified wallet account.
|
|
1150
|
+
*
|
|
1151
|
+
* - Supports both embedded and connected wallets.
|
|
1152
|
+
* - For **connected wallets**:
|
|
1153
|
+
* - Delegates signing to the wallet provider’s native signing method.
|
|
1154
|
+
* - **Important:** For Ethereum wallets (e.g., MetaMask), signatures follow [EIP-191](https://eips.ethereum.org/EIPS/eip-191).
|
|
1155
|
+
* The message is automatically prefixed with `"\x19Ethereum Signed Message:\n" + message length`
|
|
1156
|
+
* before signing. As a result, this signature **cannot be used as a raw transaction signature**
|
|
1157
|
+
* or broadcast on-chain.
|
|
1158
|
+
* - For **embedded wallets**, uses the Turnkey API to sign the message directly.
|
|
1159
|
+
* - Automatically handles message encoding and hashing based on the wallet account’s address format,
|
|
1160
|
+
* unless explicitly overridden.
|
|
1161
|
+
*
|
|
1162
|
+
* @param params.message - message to sign.
|
|
1163
|
+
* @param params.walletAccount - wallet account to use for signing.
|
|
1164
|
+
* @param params.encoding - override for the payload encoding (defaults to the encoding appropriate for the address type).
|
|
1165
|
+
* @param params.hashFunction - override for the hash function (defaults to the hash function appropriate for the address type).
|
|
1166
|
+
* @param params.stampWith - stamper to tag the signing request (e.g., Passkey, ApiKey, or Wallet).
|
|
1167
|
+
* @param params.addEthereumPrefix - whether to prefix the message with Ethereum's `"\x19Ethereum Signed Message:\n"` string.
|
|
1168
|
+
* - If `true` (default for Ethereum), the message is prefixed before signing.
|
|
1169
|
+
* - If `false`:
|
|
1170
|
+
* - Connected wallets will throw an error because they always prefix automatically.
|
|
1171
|
+
* - Embedded wallets will sign the raw message without any prefix.
|
|
1172
|
+
*
|
|
1173
|
+
* @returns A promise resolving to a `v1SignRawPayloadResult` containing the signature and metadata.
|
|
1174
|
+
* @throws {TurnkeyError} If signing fails, if the wallet account does not support signing, or if the response is invalid.
|
|
1175
|
+
*/
|
|
1176
|
+
this.signMessage = async (params) => {
|
|
1177
|
+
const { message, walletAccount, stampWith, addEthereumPrefix } = params;
|
|
1178
|
+
const hashFunction = params.hashFunction || utils.getHashFunction(walletAccount.addressFormat);
|
|
1179
|
+
const payloadEncoding = params.encoding || utils.getEncodingType(walletAccount.addressFormat);
|
|
1180
|
+
try {
|
|
1181
|
+
const isEthereum = walletAccount.addressFormat === "ADDRESS_FORMAT_ETHEREUM";
|
|
1182
|
+
if (walletAccount.source === base.WalletSource.Connected) {
|
|
1183
|
+
// this is a connected wallet
|
|
1184
|
+
if (!addEthereumPrefix && isEthereum) {
|
|
1185
|
+
throw new sdkTypes.TurnkeyError("Connected Ethereum wallets automatically prefix messages. Use `addEthereumPrefix: true`.", sdkTypes.TurnkeyErrorCodes.SIGN_MESSAGE_ERROR);
|
|
1186
|
+
}
|
|
1187
|
+
let encodedMessage = message;
|
|
1188
|
+
if (isEthereum) {
|
|
1189
|
+
encodedMessage = utils.getEncodedMessage(walletAccount.addressFormat, message);
|
|
1190
|
+
}
|
|
1191
|
+
const sigHex = await walletAccount.signMessage(encodedMessage);
|
|
1192
|
+
return utils.splitSignature(sigHex, walletAccount.addressFormat);
|
|
1193
|
+
}
|
|
1194
|
+
// this is an embedded wallet
|
|
1195
|
+
let messageToEncode = message;
|
|
1196
|
+
if (addEthereumPrefix && isEthereum) {
|
|
1197
|
+
const prefix = `\x19Ethereum Signed Message:\n${ethers.toUtf8Bytes(message).length}`;
|
|
1198
|
+
messageToEncode = prefix + message;
|
|
1199
|
+
}
|
|
1200
|
+
const encodedMessage = utils.getEncodedMessage(walletAccount.addressFormat, messageToEncode);
|
|
1201
|
+
const response = await this.httpClient.signRawPayload({
|
|
1202
|
+
signWith: walletAccount.address,
|
|
1203
|
+
payload: encodedMessage,
|
|
1204
|
+
encoding: payloadEncoding,
|
|
1205
|
+
hashFunction,
|
|
1206
|
+
}, stampWith);
|
|
1207
|
+
if (response.activity.failure) {
|
|
1208
|
+
throw new sdkTypes.TurnkeyError("Failed to sign message, no signed payload returned", sdkTypes.TurnkeyErrorCodes.SIGN_MESSAGE_ERROR);
|
|
1209
|
+
}
|
|
1210
|
+
return response.activity.result
|
|
1211
|
+
.signRawPayloadResult;
|
|
1212
|
+
}
|
|
1213
|
+
catch (error) {
|
|
1214
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1215
|
+
throw error;
|
|
1216
|
+
throw new sdkTypes.TurnkeyError(`Failed to sign message - ${error?.message ? error.message : "Unknown error"}`, sdkTypes.TurnkeyErrorCodes.SIGN_MESSAGE_ERROR);
|
|
1217
|
+
}
|
|
1218
|
+
};
|
|
1219
|
+
/**
|
|
1220
|
+
* Signs a transaction using the specified wallet account.
|
|
1221
|
+
*
|
|
1222
|
+
* - This function signs a blockchain transaction using the provided wallet address and transaction data.
|
|
1223
|
+
* - Supports all Turnkey-supported blockchain networks (e.g., Ethereum, Solana, Tron).
|
|
1224
|
+
* - Automatically determines the appropriate signing method based on the transaction type.
|
|
1225
|
+
* - Delegates signing to the Turnkey API, which returns the signed transaction and related metadata.
|
|
1226
|
+
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1227
|
+
*
|
|
1228
|
+
* @param params.walletAccount - wallet account to use for signing the transaction.
|
|
1229
|
+
* @param params.unsignedTransaction - unsigned transaction data (serialized as a string) to be signed.
|
|
1230
|
+
* @param params.transactionType - type of transaction (e.g., "TRANSACTION_TYPE_ETHEREUM", "TRANSACTION_TYPE_SOLANA", "TRANSACTION_TYPE_TRON").
|
|
1231
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1232
|
+
* @returns A promise that resolves to a `TSignTransactionResponse` object containing the signed transaction and any additional signing metadata.
|
|
1233
|
+
* @throws {TurnkeyError} If there is an error signing the transaction or if the response is invalid.
|
|
1234
|
+
*/
|
|
1235
|
+
this.signTransaction = async (params) => {
|
|
1236
|
+
const { walletAccount, unsignedTransaction, transactionType, stampWith } = params;
|
|
1237
|
+
try {
|
|
1238
|
+
if (walletAccount.source === base.WalletSource.Connected) {
|
|
1239
|
+
// this is a connected wallet account
|
|
1240
|
+
if (!walletAccount.signTransaction) {
|
|
1241
|
+
const isEthereum = walletAccount.addressFormat === "ADDRESS_FORMAT_ETHEREUM";
|
|
1242
|
+
const reason = isEthereum
|
|
1243
|
+
? "Ethereum connected wallets do not support raw transaction signing due to EIP-1193 limitations."
|
|
1244
|
+
: "This connected wallet does not support raw transaction signing.";
|
|
1245
|
+
throw new sdkTypes.TurnkeyError(`Failed to sign transaction: ${reason} ${isEthereum ? "Use signAndSendTransaction instead." : ""}`, sdkTypes.TurnkeyErrorCodes.SIGN_TRANSACTION_ERROR);
|
|
1246
|
+
}
|
|
1247
|
+
return await walletAccount?.signTransaction(unsignedTransaction);
|
|
1248
|
+
}
|
|
1249
|
+
// this is an embedded wallet account
|
|
1250
|
+
const signTransaction = await this.httpClient.signTransaction({
|
|
1251
|
+
signWith: walletAccount.address,
|
|
1252
|
+
unsignedTransaction,
|
|
1253
|
+
type: transactionType,
|
|
1254
|
+
}, stampWith);
|
|
1255
|
+
return signTransaction.signedTransaction;
|
|
1256
|
+
}
|
|
1257
|
+
catch (error) {
|
|
1258
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1259
|
+
throw error;
|
|
1260
|
+
throw new sdkTypes.TurnkeyError(`Failed to sign transaction`, sdkTypes.TurnkeyErrorCodes.SIGN_TRANSACTION_ERROR, error);
|
|
1261
|
+
}
|
|
1262
|
+
};
|
|
1263
|
+
/**
|
|
1264
|
+
* Signs and broadcasts a transaction using the specified wallet account.
|
|
1265
|
+
*
|
|
1266
|
+
* - For **connected wallets**:
|
|
1267
|
+
* - Calls the wallet’s native `signAndSendTransaction` method.
|
|
1268
|
+
* - Does **not** require an `rpcUrl`.
|
|
1269
|
+
*
|
|
1270
|
+
* - For **embedded wallets**:
|
|
1271
|
+
* - Signs the transaction using the Turnkey API.
|
|
1272
|
+
* - Requires an `rpcUrl` to broadcast the transaction.
|
|
1273
|
+
* - Broadcasts the transaction using a JSON-RPC client.
|
|
1274
|
+
*
|
|
1275
|
+
* @param params.walletAccount - wallet account to use for signing and sending.
|
|
1276
|
+
* @param params.unsignedTransaction - unsigned transaction (serialized string).
|
|
1277
|
+
* @param params.transactionType - transaction type (e.g., "TRANSACTION_TYPE_SOLANA").
|
|
1278
|
+
* @param params.rpcUrl - required for embedded wallets to broadcast the signed transaction.
|
|
1279
|
+
* @param params.stampWith - optional stamper to tag the signing request.
|
|
1280
|
+
* @returns A promise that resolves to a transaction signature or hash.
|
|
1281
|
+
* @throws {TurnkeyError} If signing or broadcasting fails.
|
|
1282
|
+
*/
|
|
1283
|
+
this.signAndSendTransaction = async (params) => {
|
|
1284
|
+
const { walletAccount, unsignedTransaction, transactionType, rpcUrl, stampWith, } = params;
|
|
1285
|
+
try {
|
|
1286
|
+
if (walletAccount.source === base.WalletSource.Connected) {
|
|
1287
|
+
// this is a connected wallet account
|
|
1288
|
+
switch (transactionType) {
|
|
1289
|
+
case "TRANSACTION_TYPE_ETHEREUM":
|
|
1290
|
+
if (!walletAccount.signAndSendTransaction) {
|
|
1291
|
+
throw new sdkTypes.TurnkeyError("This connected wallet does not support signAndSendTransaction.", sdkTypes.TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR);
|
|
1292
|
+
}
|
|
1293
|
+
return await walletAccount.signAndSendTransaction(unsignedTransaction);
|
|
1294
|
+
case "TRANSACTION_TYPE_SOLANA":
|
|
1295
|
+
if (!rpcUrl) {
|
|
1296
|
+
throw new sdkTypes.TurnkeyError("Missing rpcUrl: connected Solana wallets require an RPC URL to broadcast transactions.", sdkTypes.TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR);
|
|
1297
|
+
}
|
|
1298
|
+
if (!walletAccount.signTransaction) {
|
|
1299
|
+
throw new sdkTypes.TurnkeyError("This connected wallet does not support signAndSendTransaction.", sdkTypes.TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR);
|
|
1300
|
+
}
|
|
1301
|
+
const signature = await walletAccount.signTransaction(unsignedTransaction);
|
|
1302
|
+
return await utils.broadcastTransaction({
|
|
1303
|
+
signedTransaction: signature,
|
|
1304
|
+
rpcUrl,
|
|
1305
|
+
transactionType,
|
|
1306
|
+
});
|
|
1307
|
+
default:
|
|
1308
|
+
throw new sdkTypes.TurnkeyError("Connected wallets do not support signAndSendTransaction for this transaction type.", sdkTypes.TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
// this is an embedded wallet account
|
|
1312
|
+
// embedded wallet requires an RPC URL to broadcast
|
|
1313
|
+
// since Turnkey does not broadcast transactions directly
|
|
1314
|
+
if (!rpcUrl) {
|
|
1315
|
+
throw new sdkTypes.TurnkeyError("Missing rpcUrl: embedded wallets require an RPC URL to broadcast transactions.", sdkTypes.TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR);
|
|
1316
|
+
}
|
|
1317
|
+
const signTransactionResponse = await this.httpClient.signTransaction({
|
|
1318
|
+
signWith: walletAccount.address,
|
|
1319
|
+
unsignedTransaction,
|
|
1320
|
+
type: transactionType,
|
|
1321
|
+
}, stampWith);
|
|
1322
|
+
const signedTx = signTransactionResponse.signedTransaction;
|
|
1323
|
+
const txHash = await utils.broadcastTransaction({
|
|
1324
|
+
signedTransaction: signedTx,
|
|
1325
|
+
rpcUrl,
|
|
1326
|
+
transactionType,
|
|
1327
|
+
});
|
|
1328
|
+
return txHash;
|
|
1329
|
+
}
|
|
1330
|
+
catch (error) {
|
|
1331
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1332
|
+
throw error;
|
|
1333
|
+
throw new sdkTypes.TurnkeyError(`Failed to sign and send transaction`, sdkTypes.TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR, error);
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
/**
|
|
1337
|
+
* Fetches the user details for the current session or a specified user.
|
|
1338
|
+
*
|
|
1339
|
+
* - Retrieves user details from the Turnkey API using the provided userId and organizationId, or defaults to those from the active session.
|
|
1340
|
+
* - If no userId is provided, the userId from the current session is used.
|
|
1341
|
+
* - If no organizationId is provided, the organizationId from the current session is used.
|
|
1342
|
+
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1343
|
+
* - Ensures that an active session exists before making the request.
|
|
1344
|
+
*
|
|
1345
|
+
* @param params.organizationId - organization ID to specify the sub-organization (defaults to the current session's organizationId).
|
|
1346
|
+
* @param params.userId - user ID to fetch specific user details (defaults to the current session's userId).
|
|
1347
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1348
|
+
* @returns A promise that resolves to a `v1User` object containing the user details.
|
|
1349
|
+
* @throws {TurnkeyError} If there is no active session, if there is no userId, or if there is an error fetching user details.
|
|
1350
|
+
*/
|
|
1351
|
+
this.fetchUser = async (params) => {
|
|
1352
|
+
const { stampWith } = params || {};
|
|
1353
|
+
const session = await this.storageManager.getActiveSession();
|
|
1354
|
+
if (!session) {
|
|
1355
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1356
|
+
}
|
|
1357
|
+
const userId = params?.userId || session.userId;
|
|
1358
|
+
if (!userId) {
|
|
1359
|
+
throw new sdkTypes.TurnkeyError("User ID must be provided to fetch user", sdkTypes.TurnkeyErrorCodes.INVALID_REQUEST);
|
|
1360
|
+
}
|
|
1361
|
+
const organizationId = params?.organizationId || session.organizationId;
|
|
1362
|
+
try {
|
|
1363
|
+
const userResponse = await this.httpClient.getUser({ organizationId, userId }, stampWith);
|
|
1364
|
+
if (!userResponse || !userResponse.user) {
|
|
1365
|
+
throw new sdkTypes.TurnkeyError("No user found in the response", sdkTypes.TurnkeyErrorCodes.BAD_RESPONSE);
|
|
1366
|
+
}
|
|
1367
|
+
return userResponse.user;
|
|
1368
|
+
}
|
|
1369
|
+
catch (error) {
|
|
1370
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1371
|
+
throw error;
|
|
1372
|
+
throw new sdkTypes.TurnkeyError(`Failed to fetch user`, sdkTypes.TurnkeyErrorCodes.FETCH_USER_ERROR, error);
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
/**
|
|
1376
|
+
* Updates the user's email address.
|
|
1377
|
+
*
|
|
1378
|
+
* - This function updates the user's email address and, if provided, verifies it using a verification token (typically from an OTP flow).
|
|
1379
|
+
* - If a userId is provided, it updates the email for that specific user; otherwise, it uses the current session's userId.
|
|
1380
|
+
* - If a verificationToken is not provided, the email will be updated but will not be marked as verified.
|
|
1381
|
+
* - Automatically ensures an active session exists before making the request.
|
|
1382
|
+
* - Handles session management and error reporting for both update and verification flows.
|
|
1383
|
+
*
|
|
1384
|
+
* @param params.email - new email address to set for the user.
|
|
1385
|
+
* @param params.verificationToken - verification token from OTP email verification (required if verifying the email).
|
|
1386
|
+
* @param params.userId - user ID to update a specific user's email (defaults to the current session's userId).
|
|
1387
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1388
|
+
* @returns A promise that resolves to the userId of the updated user.
|
|
1389
|
+
* @throws {TurnkeyError} If there is no active session, if the userId is missing, or if there is an error updating or verifying the user email.
|
|
1390
|
+
*/
|
|
1391
|
+
this.updateUserEmail = async (params) => {
|
|
1392
|
+
const { verificationToken, email, stampWith } = params;
|
|
1393
|
+
const session = await this.storageManager.getActiveSession();
|
|
1394
|
+
if (!session) {
|
|
1395
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1396
|
+
}
|
|
1397
|
+
const userId = params?.userId || session.userId;
|
|
1398
|
+
try {
|
|
1399
|
+
const existingUser = await this.httpClient.proxyGetAccount({
|
|
1400
|
+
filterType: base.FilterType.Email,
|
|
1401
|
+
filterValue: email,
|
|
1402
|
+
});
|
|
1403
|
+
if (existingUser.organizationId) {
|
|
1404
|
+
throw new sdkTypes.TurnkeyError(`Email ${email} is already associated with another user.`, sdkTypes.TurnkeyErrorCodes.ACCOUNT_ALREADY_EXISTS);
|
|
1405
|
+
}
|
|
1406
|
+
const res = await this.httpClient.updateUserEmail({
|
|
1407
|
+
userId: userId,
|
|
1408
|
+
userEmail: email,
|
|
1409
|
+
...(verificationToken && { verificationToken }),
|
|
1410
|
+
}, stampWith);
|
|
1411
|
+
if (!res || !res.userId) {
|
|
1412
|
+
throw new sdkTypes.TurnkeyError("No user ID found in the update user email response", sdkTypes.TurnkeyErrorCodes.BAD_RESPONSE);
|
|
1413
|
+
}
|
|
1414
|
+
return res.userId;
|
|
1415
|
+
}
|
|
1416
|
+
catch (error) {
|
|
1417
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1418
|
+
throw error;
|
|
1419
|
+
throw new sdkTypes.TurnkeyError(`Failed to update user email`, sdkTypes.TurnkeyErrorCodes.UPDATE_USER_EMAIL_ERROR, error);
|
|
1420
|
+
}
|
|
1421
|
+
};
|
|
1422
|
+
/**
|
|
1423
|
+
* Removes the user's email address.
|
|
1424
|
+
*
|
|
1425
|
+
* - This function removes the user's email address by setting it to an empty string.
|
|
1426
|
+
* - If a userId is provided, it removes the email for that specific user; otherwise, it uses the current session's userId.
|
|
1427
|
+
* - Automatically ensures an active session exists before making the request.
|
|
1428
|
+
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1429
|
+
*
|
|
1430
|
+
* @param params.userId - user ID to remove a specific user's email address (defaults to the current session's userId).
|
|
1431
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1432
|
+
* @returns A promise that resolves to the userId of the user whose email was removed.
|
|
1433
|
+
* @throws {TurnkeyError} If there is no active session, if the userId is missing, or if there is an error removing the user email.
|
|
1434
|
+
*/
|
|
1435
|
+
this.removeUserEmail = async (params) => {
|
|
1436
|
+
const { stampWith } = params || {};
|
|
1437
|
+
const session = await this.storageManager.getActiveSession();
|
|
1438
|
+
if (!session) {
|
|
1439
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1440
|
+
}
|
|
1441
|
+
const userId = params?.userId || session.userId;
|
|
1442
|
+
const res = await this.httpClient.updateUserEmail({
|
|
1443
|
+
userId: userId,
|
|
1444
|
+
userEmail: "",
|
|
1445
|
+
}, stampWith);
|
|
1446
|
+
if (!res || !res.userId) {
|
|
1447
|
+
throw new sdkTypes.TurnkeyError("No user ID found in the remove user email response", sdkTypes.TurnkeyErrorCodes.BAD_RESPONSE);
|
|
1448
|
+
}
|
|
1449
|
+
return res.userId;
|
|
1450
|
+
};
|
|
1451
|
+
/**
|
|
1452
|
+
* Updates the user's phone number.
|
|
1453
|
+
*
|
|
1454
|
+
* - This function updates the user's phone number and, if provided, verifies it using a verification token (from an OTP flow).
|
|
1455
|
+
* - If a userId is provided, it updates the phone number for that specific user; otherwise, it uses the current session's userId.
|
|
1456
|
+
* - If a verificationToken is not provided, the phone number will be updated but will not be marked as verified.
|
|
1457
|
+
* - Automatically ensures an active session exists before making the request.
|
|
1458
|
+
* - Handles session management and error reporting for both update and verification flows.
|
|
1459
|
+
*
|
|
1460
|
+
* @param params.phoneNumber - new phone number to set for the user.
|
|
1461
|
+
* @param params.verificationToken - verification token from OTP phone verification (required if verifying the phone number).
|
|
1462
|
+
* @param params.userId - user ID to update a specific user's phone number (defaults to the current session's userId).
|
|
1463
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1464
|
+
* @returns A promise that resolves to the userId of the updated user.
|
|
1465
|
+
* @throws {TurnkeyError} If there is no active session, if the userId is missing, or if there is an error updating or verifying the user phone number.
|
|
1466
|
+
*/
|
|
1467
|
+
this.updateUserPhoneNumber = async (params) => {
|
|
1468
|
+
const { verificationToken, phoneNumber, stampWith } = params;
|
|
1469
|
+
const session = await this.storageManager.getActiveSession();
|
|
1470
|
+
if (!session) {
|
|
1471
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1472
|
+
}
|
|
1473
|
+
const userId = params?.userId || session.userId;
|
|
1474
|
+
try {
|
|
1475
|
+
const res = await this.httpClient.updateUserPhoneNumber({
|
|
1476
|
+
userId,
|
|
1477
|
+
userPhoneNumber: phoneNumber,
|
|
1478
|
+
...(verificationToken && { verificationToken }),
|
|
1479
|
+
}, stampWith);
|
|
1480
|
+
if (!res || !res.userId) {
|
|
1481
|
+
throw new sdkTypes.TurnkeyError("Failed to update user phone number", sdkTypes.TurnkeyErrorCodes.UPDATE_USER_PHONE_NUMBER_ERROR);
|
|
1482
|
+
}
|
|
1483
|
+
return res.userId;
|
|
1484
|
+
}
|
|
1485
|
+
catch (error) {
|
|
1486
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1487
|
+
throw error;
|
|
1488
|
+
throw new sdkTypes.TurnkeyError(`Failed to update user phone number`, sdkTypes.TurnkeyErrorCodes.UPDATE_USER_PHONE_NUMBER_ERROR, error);
|
|
1489
|
+
}
|
|
1490
|
+
};
|
|
1491
|
+
/**
|
|
1492
|
+
* Removes the user's phone number.
|
|
1493
|
+
*
|
|
1494
|
+
* - This function removes the user's phone number by setting it to an empty string.
|
|
1495
|
+
* - If a userId is provided, it removes the phone number for that specific user; otherwise, it uses the current session's userId.
|
|
1496
|
+
* - Automatically ensures an active session exists before making the request.
|
|
1497
|
+
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1498
|
+
*
|
|
1499
|
+
* @param params.userId - user ID to remove a specific user's phone number (defaults to the current session's userId).
|
|
1500
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1501
|
+
* @returns A promise that resolves to the userId of the user whose phone number was removed.
|
|
1502
|
+
* @throws {TurnkeyError} If there is no active session, if the userId is missing, or if there is an error removing the user phone number.
|
|
1503
|
+
*/
|
|
1504
|
+
this.removeUserPhoneNumber = async (params) => {
|
|
1505
|
+
const { stampWith } = params || {};
|
|
1506
|
+
const session = await this.storageManager.getActiveSession();
|
|
1507
|
+
if (!session) {
|
|
1508
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1509
|
+
}
|
|
1510
|
+
const userId = params?.userId || session.userId;
|
|
1511
|
+
const res = await this.httpClient.updateUserPhoneNumber({
|
|
1512
|
+
userId,
|
|
1513
|
+
userPhoneNumber: "",
|
|
1514
|
+
}, stampWith);
|
|
1515
|
+
if (!res || !res.userId) {
|
|
1516
|
+
throw new sdkTypes.TurnkeyError("Failed to remove user phone number", sdkTypes.TurnkeyErrorCodes.UPDATE_USER_PHONE_NUMBER_ERROR);
|
|
1517
|
+
}
|
|
1518
|
+
return res.userId;
|
|
1519
|
+
};
|
|
1520
|
+
/**
|
|
1521
|
+
* Updates the user's name.
|
|
1522
|
+
*
|
|
1523
|
+
* - This function updates the user's display name.
|
|
1524
|
+
* - If a userId is provided, it updates the name for that specific user; otherwise, it uses the current session's userId.
|
|
1525
|
+
* - Automatically ensures an active session exists before making the request.
|
|
1526
|
+
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1527
|
+
* - Handles session management and error reporting for the update flow.
|
|
1528
|
+
*
|
|
1529
|
+
* @param params.userName - new name to set for the user.
|
|
1530
|
+
* @param params.userId - user ID to update a specific user's name (defaults to the current session's userId).
|
|
1531
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1532
|
+
* @returns A promise that resolves to the userId of the updated user.
|
|
1533
|
+
* @throws {TurnkeyError} If there is no active session, if the userId is missing, or if there is an error updating the user name.
|
|
1534
|
+
*/
|
|
1535
|
+
this.updateUserName = async (params) => {
|
|
1536
|
+
const { userName, stampWith } = params;
|
|
1537
|
+
const session = await this.storageManager.getActiveSession();
|
|
1538
|
+
if (!session) {
|
|
1539
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1540
|
+
}
|
|
1541
|
+
const userId = params?.userId || session.userId;
|
|
1542
|
+
try {
|
|
1543
|
+
const res = await this.httpClient.updateUserName({
|
|
1544
|
+
userId,
|
|
1545
|
+
userName,
|
|
1546
|
+
}, stampWith);
|
|
1547
|
+
if (!res || !res.userId) {
|
|
1548
|
+
throw new sdkTypes.TurnkeyError("No user ID found in the update user name response", sdkTypes.TurnkeyErrorCodes.BAD_RESPONSE);
|
|
1549
|
+
}
|
|
1550
|
+
return res.userId;
|
|
1551
|
+
}
|
|
1552
|
+
catch (error) {
|
|
1553
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1554
|
+
throw error;
|
|
1555
|
+
throw new sdkTypes.TurnkeyError(`Failed to update user name`, sdkTypes.TurnkeyErrorCodes.UPDATE_USER_NAME_ERROR, error);
|
|
1556
|
+
}
|
|
1557
|
+
};
|
|
1558
|
+
/**
|
|
1559
|
+
* Adds an OAuth provider to the user.
|
|
1560
|
+
*
|
|
1561
|
+
* - This function adds an OAuth provider (e.g., Google, Apple) to the user account.
|
|
1562
|
+
* - If a userId is provided, it adds the provider for that specific user; otherwise, it uses the current session's userId.
|
|
1563
|
+
* - Automatically checks if an account already exists for the provided OIDC token and prevents duplicate associations.
|
|
1564
|
+
* - If the user's email is not set or not verified, attempts to update and verify the email using the email from the OIDC token.
|
|
1565
|
+
* - Handles session management and error reporting for the add provider flow.
|
|
1566
|
+
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1567
|
+
*
|
|
1568
|
+
* @param params.providerName - name of the OAuth provider to add (e.g., "Google", "Apple").
|
|
1569
|
+
* @param params.oidcToken - OIDC token for the OAuth provider.
|
|
1570
|
+
* @param params.userId - user ID to add the provider for a specific user (defaults to current session's userId).
|
|
1571
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1572
|
+
* @returns A promise that resolves to an array of provider IDs associated with the user.
|
|
1573
|
+
* @throws {TurnkeyError} If there is no active session, if the account already exists, or if there is an error adding the OAuth provider.
|
|
1574
|
+
*/
|
|
1575
|
+
this.addOauthProvider = async (params) => {
|
|
1576
|
+
const { providerName, oidcToken, stampWith } = params;
|
|
1577
|
+
const session = await this.storageManager.getActiveSession();
|
|
1578
|
+
if (!session) {
|
|
1579
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1580
|
+
}
|
|
1581
|
+
try {
|
|
1582
|
+
const accountRes = await this.httpClient.proxyGetAccount({
|
|
1583
|
+
filterType: "OIDC_TOKEN",
|
|
1584
|
+
filterValue: oidcToken,
|
|
1585
|
+
});
|
|
1586
|
+
if (!accountRes) {
|
|
1587
|
+
throw new sdkTypes.TurnkeyError(`Account fetch failed}`, sdkTypes.TurnkeyErrorCodes.ACCOUNT_FETCH_ERROR);
|
|
1588
|
+
}
|
|
1589
|
+
if (accountRes.organizationId) {
|
|
1590
|
+
throw new sdkTypes.TurnkeyError("Account already exists with this OIDC token", sdkTypes.TurnkeyErrorCodes.ACCOUNT_ALREADY_EXISTS);
|
|
1591
|
+
}
|
|
1592
|
+
const userId = params?.userId || session.userId;
|
|
1593
|
+
const { email: oidcEmail, iss } = jwtDecode.jwtDecode(oidcToken) || {}; // Parse the oidc token so we can get the email. Pass it in to updateUser then call createOauthProviders. This will be verified by Turnkey.
|
|
1594
|
+
if (iss === utils.googleISS) {
|
|
1595
|
+
const verifiedSuborg = await this.httpClient.proxyGetAccount({
|
|
1596
|
+
filterType: "EMAIL",
|
|
1597
|
+
filterValue: oidcEmail,
|
|
1598
|
+
});
|
|
1599
|
+
const isVerified = verifiedSuborg.organizationId === session.organizationId;
|
|
1600
|
+
const user = await this.fetchUser({
|
|
1601
|
+
userId,
|
|
1602
|
+
stampWith,
|
|
1603
|
+
});
|
|
1604
|
+
if (!user?.userEmail && !isVerified) {
|
|
1605
|
+
await this.updateUserEmail({
|
|
1606
|
+
email: oidcEmail,
|
|
1607
|
+
userId,
|
|
1608
|
+
stampWith,
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
const createProviderRes = await this.httpClient.createOauthProviders({
|
|
1613
|
+
userId,
|
|
1614
|
+
oauthProviders: [
|
|
1615
|
+
{
|
|
1616
|
+
providerName,
|
|
1617
|
+
oidcToken,
|
|
1618
|
+
},
|
|
1619
|
+
],
|
|
1620
|
+
}, stampWith);
|
|
1621
|
+
if (!createProviderRes) {
|
|
1622
|
+
throw new sdkTypes.TurnkeyError("Failed to create OAuth provider", sdkTypes.TurnkeyErrorCodes.ADD_OAUTH_PROVIDER_ERROR);
|
|
1623
|
+
}
|
|
1624
|
+
return createProviderRes?.providerIds || [];
|
|
1625
|
+
}
|
|
1626
|
+
catch (error) {
|
|
1627
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1628
|
+
throw error;
|
|
1629
|
+
throw new sdkTypes.TurnkeyError(`Failed to fetch account for OAuth provider`, sdkTypes.TurnkeyErrorCodes.ACCOUNT_FETCH_ERROR, error);
|
|
1630
|
+
}
|
|
1631
|
+
};
|
|
1632
|
+
/**
|
|
1633
|
+
* Removes a list of OAuth providers from the user.
|
|
1634
|
+
*
|
|
1635
|
+
* - This function removes OAuth providers (e.g., Google, Apple) from the user's account.
|
|
1636
|
+
* - If a userId is provided, it removes the providers for that specific user; otherwise, it uses the current session's userId.
|
|
1637
|
+
* - Automatically ensures an active session exists before making the request.
|
|
1638
|
+
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1639
|
+
* - Returns an array of remaining provider IDs associated with the user after removal.
|
|
1640
|
+
*
|
|
1641
|
+
* @param params.providerIds - IDs of the OAuth providers to remove.
|
|
1642
|
+
* @param params.userId - user ID to remove the provider for a specific user (defaults to the current session's userId).
|
|
1643
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1644
|
+
* @returns A promise that resolves to an array of provider IDs that were removed.
|
|
1645
|
+
* @throws {TurnkeyError} If there is no active session, if the userId is missing, or if there is an error removing the OAuth provider.
|
|
1646
|
+
*/
|
|
1647
|
+
this.removeOauthProviders = async (params) => {
|
|
1648
|
+
const { providerIds, stampWith } = params;
|
|
1649
|
+
const session = await this.storageManager.getActiveSession();
|
|
1650
|
+
if (!session) {
|
|
1651
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1652
|
+
}
|
|
1653
|
+
const userId = params?.userId || session.userId;
|
|
1654
|
+
const res = await this.httpClient.deleteOauthProviders({
|
|
1655
|
+
userId,
|
|
1656
|
+
providerIds,
|
|
1657
|
+
}, stampWith);
|
|
1658
|
+
if (!res) {
|
|
1659
|
+
throw new sdkTypes.TurnkeyError("Failed to remove OAuth provider", sdkTypes.TurnkeyErrorCodes.REMOVE_OAUTH_PROVIDER_ERROR);
|
|
1660
|
+
}
|
|
1661
|
+
return res.providerIds;
|
|
1662
|
+
};
|
|
1663
|
+
/**
|
|
1664
|
+
* Adds a new passkey authenticator for the user.
|
|
1665
|
+
*
|
|
1666
|
+
* - This function prompts the user to create a new passkey (WebAuthn/FIDO2) and adds it as an authenticator for the user.
|
|
1667
|
+
* - Handles both web and React Native environments, automatically selecting the appropriate passkey creation flow.
|
|
1668
|
+
* - If a userId is provided, the passkey is added for that specific user; otherwise, it uses the current session's userId.
|
|
1669
|
+
* - The passkey's name and display name can be customized; if not provided, defaults are generated.
|
|
1670
|
+
* - The resulting passkey attestation and challenge are registered with Turnkey as a new authenticator.
|
|
1671
|
+
*
|
|
1672
|
+
* @param params.name - name of the passkey (defaults to "Turnkey Passkey-`timestamp`").
|
|
1673
|
+
* @param params.displayName - display name of the passkey (defaults to the value of `name`).
|
|
1674
|
+
* @param params.userId - user ID to add the passkey for a specific user (defaults to the current session's userId).
|
|
1675
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1676
|
+
* @returns A promise that resolves to an array of authenticator IDs for the newly added passkey(s).
|
|
1677
|
+
* @throws {TurnkeyError} If there is no active session, if passkey creation fails, or if there is an error adding the passkey.
|
|
1678
|
+
*/
|
|
1679
|
+
this.addPasskey = async (params) => {
|
|
1680
|
+
const { stampWith } = params || {};
|
|
1681
|
+
const name = params?.name || `Turnkey Passkey-${Date.now()}`;
|
|
1682
|
+
const displayName = params?.displayName || name;
|
|
1683
|
+
try {
|
|
1684
|
+
const session = await this.storageManager.getActiveSession();
|
|
1685
|
+
if (!session) {
|
|
1686
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1687
|
+
}
|
|
1688
|
+
const userId = params?.userId || session.userId;
|
|
1689
|
+
const { encodedChallenge, attestation } = await this.createPasskey({
|
|
1690
|
+
name,
|
|
1691
|
+
displayName,
|
|
1692
|
+
...(stampWith && { stampWith }),
|
|
1693
|
+
});
|
|
1694
|
+
if (!attestation || !encodedChallenge) {
|
|
1695
|
+
throw new sdkTypes.TurnkeyError("Failed to create passkey challenge and attestation", sdkTypes.TurnkeyErrorCodes.CREATE_PASSKEY_ERROR);
|
|
1696
|
+
}
|
|
1697
|
+
const res = await this.httpClient.createAuthenticators({
|
|
1698
|
+
userId,
|
|
1699
|
+
authenticators: [
|
|
1700
|
+
{
|
|
1701
|
+
authenticatorName: name,
|
|
1702
|
+
challenge: encodedChallenge,
|
|
1703
|
+
attestation,
|
|
1704
|
+
},
|
|
1705
|
+
],
|
|
1706
|
+
}, stampWith);
|
|
1707
|
+
return res?.authenticatorIds || [];
|
|
1708
|
+
}
|
|
1709
|
+
catch (error) {
|
|
1710
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1711
|
+
throw error;
|
|
1712
|
+
throw new sdkTypes.TurnkeyError(`Failed to add passkey`, sdkTypes.TurnkeyErrorCodes.ADD_PASSKEY_ERROR, error);
|
|
1713
|
+
}
|
|
1714
|
+
};
|
|
1715
|
+
/**
|
|
1716
|
+
* Removes passkeys (authenticator) from the user.
|
|
1717
|
+
*
|
|
1718
|
+
* - This function removes passkeys (WebAuthn/FIDO2 authenticators) from the user's account.
|
|
1719
|
+
* - If a userId is provided, it removes the passkeys for that specific user; otherwise, it uses the current session's userId.
|
|
1720
|
+
* - Automatically ensures an active session exists before making the request.
|
|
1721
|
+
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1722
|
+
* - Returns an array of remaining authenticator IDs for the user after removal.
|
|
1723
|
+
*
|
|
1724
|
+
* @param params.authenticatorIds - IDs of the authenticators (passkeys) to remove.
|
|
1725
|
+
* @param params.userId - user ID to remove the passkeys for a specific user (defaults to the current session's userId).
|
|
1726
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1727
|
+
* @returns A promise that resolves to an array of authenticator IDs that were removed.
|
|
1728
|
+
* @throws {TurnkeyError} If there is no active session, if the userId is missing, or if there is an error removing the passkeys.
|
|
1729
|
+
*/
|
|
1730
|
+
this.removePasskeys = async (params) => {
|
|
1731
|
+
const { authenticatorIds, stampWith } = params;
|
|
1732
|
+
const session = await this.storageManager.getActiveSession();
|
|
1733
|
+
if (!session) {
|
|
1734
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1735
|
+
}
|
|
1736
|
+
const userId = params?.userId || session.userId;
|
|
1737
|
+
const res = await this.httpClient.deleteAuthenticators({
|
|
1738
|
+
userId,
|
|
1739
|
+
authenticatorIds,
|
|
1740
|
+
}, stampWith);
|
|
1741
|
+
if (!res) {
|
|
1742
|
+
throw new sdkTypes.TurnkeyError("Failed to remove passkey", sdkTypes.TurnkeyErrorCodes.REMOVE_PASSKEY_ERROR);
|
|
1743
|
+
}
|
|
1744
|
+
return res.authenticatorIds;
|
|
1745
|
+
};
|
|
1746
|
+
/**
|
|
1747
|
+
* Creates a new wallet for sub-organization.
|
|
1748
|
+
*
|
|
1749
|
+
* - This function creates a new wallet for the current sub-organization.
|
|
1750
|
+
* - If an organizationId is provided, the wallet will be created under that specific sub-organization; otherwise, it uses the current session's organizationId.
|
|
1751
|
+
* - If a list of address formats is provided, accounts will be created in the wallet based on those formats (starting from path index 0).
|
|
1752
|
+
* - If a list of account parameters is provided, those accounts will be created in the wallet.
|
|
1753
|
+
* - If no accounts or address formats are provided, default Ethereum and Solana accounts will be created.
|
|
1754
|
+
* - Optionally allows specifying the mnemonic length for the wallet seed phrase (defaults to 12).
|
|
1755
|
+
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1756
|
+
*
|
|
1757
|
+
* @param params.walletName - name of the wallet to create.
|
|
1758
|
+
* @param params.accounts - array of account parameters or address formats to create in the wallet.
|
|
1759
|
+
* @param params.organizationId - organization ID to create the wallet under a specific sub-organization (defaults to the current session's organizationId).
|
|
1760
|
+
* @param params.mnemonicLength - mnemonic length for the wallet seed phrase (defaults to 12).
|
|
1761
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1762
|
+
* @returns A promise that resolves to the ID of the newly created wallet.
|
|
1763
|
+
* @throws {TurnkeyError} If there is no active session or if there is an error creating the wallet.
|
|
1764
|
+
*/
|
|
1765
|
+
this.createWallet = async (params) => {
|
|
1766
|
+
const { walletName, accounts, organizationId, mnemonicLength, stampWith } = params;
|
|
1767
|
+
const session = await this.storageManager.getActiveSession();
|
|
1768
|
+
if (!session) {
|
|
1769
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1770
|
+
}
|
|
1771
|
+
let walletAccounts = [];
|
|
1772
|
+
if (accounts && !utils.isWalletAccountArray(accounts)) {
|
|
1773
|
+
walletAccounts = utils.generateWalletAccountsFromAddressFormat({
|
|
1774
|
+
addresses: accounts,
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
else {
|
|
1778
|
+
walletAccounts = accounts || [
|
|
1779
|
+
...turnkeyHelpers.DEFAULT_ETHEREUM_ACCOUNTS,
|
|
1780
|
+
...turnkeyHelpers.DEFAULT_SOLANA_ACCOUNTS,
|
|
1781
|
+
];
|
|
1782
|
+
}
|
|
1783
|
+
try {
|
|
1784
|
+
const res = await this.httpClient.createWallet({
|
|
1785
|
+
organizationId: organizationId || session.organizationId,
|
|
1786
|
+
walletName,
|
|
1787
|
+
accounts: walletAccounts,
|
|
1788
|
+
mnemonicLength: mnemonicLength || 12,
|
|
1789
|
+
}, stampWith);
|
|
1790
|
+
if (!res || !res.walletId) {
|
|
1791
|
+
throw new sdkTypes.TurnkeyError("No wallet found in the create wallet response", sdkTypes.TurnkeyErrorCodes.BAD_RESPONSE);
|
|
1792
|
+
}
|
|
1793
|
+
return res.walletId;
|
|
1794
|
+
}
|
|
1795
|
+
catch (error) {
|
|
1796
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1797
|
+
throw error;
|
|
1798
|
+
throw new sdkTypes.TurnkeyError(`Failed to create wallet`, sdkTypes.TurnkeyErrorCodes.CREATE_WALLET_ERROR, error);
|
|
1799
|
+
}
|
|
1800
|
+
};
|
|
1801
|
+
/**
|
|
1802
|
+
* Creates new accounts in the specified wallet.
|
|
1803
|
+
*
|
|
1804
|
+
* - This function creates new wallet accounts based on the provided account parameters or address formats.
|
|
1805
|
+
* - If a walletId is provided, it creates the accounts in that specific wallet; otherwise, it uses the current session's wallet.
|
|
1806
|
+
* - If a list of address formats is provided, it will create accounts in the wallet based on those formats, automatically determining the next available path indexes to avoid duplicates with existing accounts.
|
|
1807
|
+
* - If account parameters are provided, they are used directly for account creation.
|
|
1808
|
+
* - Automatically queries existing wallet accounts to prevent duplicate account creation for the same address format and path.
|
|
1809
|
+
* - Supports stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1810
|
+
*
|
|
1811
|
+
* @param params.accounts - An array of account parameters or address formats to create in the wallet.
|
|
1812
|
+
* @param params.walletId - ID of the wallet to create accounts in.
|
|
1813
|
+
* @param params.organizationId - organization ID to create the accounts under a specific organization (walletId must be associated with the sub-organization).
|
|
1814
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1815
|
+
* @returns A promise that resolves to an array of addresses for the newly created accounts.
|
|
1816
|
+
* @throws {TurnkeyError} If there is no active session, if the wallet does not exist, or if there is an error creating the wallet accounts.
|
|
1817
|
+
*/
|
|
1818
|
+
this.createWalletAccounts = async (params) => {
|
|
1819
|
+
const { accounts, walletId, organizationId, stampWith } = params;
|
|
1820
|
+
const session = await this.storageManager.getActiveSession();
|
|
1821
|
+
if (!session) {
|
|
1822
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1823
|
+
}
|
|
1824
|
+
try {
|
|
1825
|
+
let walletAccounts = [];
|
|
1826
|
+
if (accounts && !utils.isWalletAccountArray(accounts)) {
|
|
1827
|
+
// Query existing wallet accounts to avoid duplicates
|
|
1828
|
+
const existingWalletAccounts = await this.httpClient.getWalletAccounts({
|
|
1829
|
+
walletId,
|
|
1830
|
+
organizationId: organizationId || session.organizationId,
|
|
1831
|
+
paginationOptions: { limit: "100" },
|
|
1832
|
+
}, stampWith);
|
|
1833
|
+
walletAccounts = utils.generateWalletAccountsFromAddressFormat({
|
|
1834
|
+
addresses: accounts,
|
|
1835
|
+
existingWalletAccounts: existingWalletAccounts.accounts || [],
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
else {
|
|
1839
|
+
walletAccounts = accounts;
|
|
1840
|
+
}
|
|
1841
|
+
const res = await this.httpClient.createWalletAccounts({
|
|
1842
|
+
organizationId: organizationId || session.organizationId,
|
|
1843
|
+
walletId,
|
|
1844
|
+
accounts: walletAccounts,
|
|
1845
|
+
}, stampWith);
|
|
1846
|
+
if (!res || !res.addresses) {
|
|
1847
|
+
throw new sdkTypes.TurnkeyError("No account found in the create wallet account response", sdkTypes.TurnkeyErrorCodes.BAD_RESPONSE);
|
|
1848
|
+
}
|
|
1849
|
+
return res.addresses;
|
|
1850
|
+
}
|
|
1851
|
+
catch (error) {
|
|
1852
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1853
|
+
throw error;
|
|
1854
|
+
throw new sdkTypes.TurnkeyError(`Failed to create wallet account`, sdkTypes.TurnkeyErrorCodes.CREATE_WALLET_ACCOUNT_ERROR, error);
|
|
1855
|
+
}
|
|
1856
|
+
};
|
|
1857
|
+
/**
|
|
1858
|
+
* Exports a wallet as an encrypted bundle.
|
|
1859
|
+
*
|
|
1860
|
+
* - This function exports the specified wallet and its accounts as an encrypted bundle, suitable for backup or transfer.
|
|
1861
|
+
* - The exported bundle contains the wallet's seed phrase, encrypted to the provided target public key.
|
|
1862
|
+
* - If a targetPublicKey is provided, the bundle will be encrypted to that public key; otherwise, an error will be thrown.
|
|
1863
|
+
* - If an organizationId is provided, the wallet will be exported under that sub-organization; otherwise, the current session's organizationId is used.
|
|
1864
|
+
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1865
|
+
* - The exported bundle can later be imported using the `importWallet` method.
|
|
1866
|
+
*
|
|
1867
|
+
* @param params.walletId - ID of the wallet to export.
|
|
1868
|
+
* @param params.targetPublicKey - public key to encrypt the bundle to (required).
|
|
1869
|
+
* @param params.organizationId - organization ID to export the wallet under a specific sub-organization (walletId must be associated with the sub-organization).
|
|
1870
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1871
|
+
* @returns A promise that resolves to an `ExportBundle` object containing the encrypted wallet seed phrase and metadata.
|
|
1872
|
+
* @throws {TurnkeyError} If there is no active session, if the targetPublicKey is missing, or if there is an error exporting the wallet.
|
|
1873
|
+
*/
|
|
1874
|
+
this.exportWallet = async (params) => {
|
|
1875
|
+
const { walletId, targetPublicKey, stampWith, organizationId } = params;
|
|
1876
|
+
const session = await this.storageManager.getActiveSession();
|
|
1877
|
+
if (!session) {
|
|
1878
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1879
|
+
}
|
|
1880
|
+
try {
|
|
1881
|
+
const res = await this.httpClient.exportWallet({
|
|
1882
|
+
walletId,
|
|
1883
|
+
targetPublicKey,
|
|
1884
|
+
organizationId: organizationId || session.organizationId,
|
|
1885
|
+
}, stampWith);
|
|
1886
|
+
if (!res.exportBundle) {
|
|
1887
|
+
throw new sdkTypes.TurnkeyError("No export bundle found in the response", sdkTypes.TurnkeyErrorCodes.BAD_RESPONSE);
|
|
1888
|
+
}
|
|
1889
|
+
return res.exportBundle;
|
|
1890
|
+
}
|
|
1891
|
+
catch (error) {
|
|
1892
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1893
|
+
throw error;
|
|
1894
|
+
throw new sdkTypes.TurnkeyError(`Failed to export wallet`, sdkTypes.TurnkeyErrorCodes.EXPORT_WALLET_ERROR, error);
|
|
1895
|
+
}
|
|
1896
|
+
};
|
|
1897
|
+
/**
|
|
1898
|
+
* Imports a wallet from an encrypted bundle.
|
|
1899
|
+
*
|
|
1900
|
+
* - This function imports a wallet using the provided encrypted bundle and creates accounts based on the provided parameters.
|
|
1901
|
+
* - If a userId is provided, the wallet will be imported for that specific user; otherwise, it uses the current session's userId.
|
|
1902
|
+
* - If an accounts array is provided, those accounts will be created in the imported wallet; otherwise, default Ethereum and Solana accounts will be created.
|
|
1903
|
+
* - The encrypted bunlde MUST be encrypted to
|
|
1904
|
+
* - Automatically ensures an active session exists before making the request.
|
|
1905
|
+
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1906
|
+
*
|
|
1907
|
+
* @param params.encryptedBundle - encrypted bundle containing the wallet seed phrase and metadata.
|
|
1908
|
+
* @param params.walletName - name of the wallet to create upon import.
|
|
1909
|
+
* @param params.accounts - array of account parameters to create in the imported wallet (defaults to standard Ethereum and Solana accounts).
|
|
1910
|
+
* @param params.userId - user ID to import the wallet for a specific user (defaults to the current session's userId).
|
|
1911
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1912
|
+
* @returns A promise that resolves to the ID of the imported wallet.
|
|
1913
|
+
* @throws {TurnkeyError} If there is no active session, if the encrypted bundle is invalid, or if there is an error importing the wallet.
|
|
1914
|
+
*/
|
|
1915
|
+
this.importWallet = async (params) => {
|
|
1916
|
+
const { encryptedBundle, accounts, walletName, userId, stampWith } = params;
|
|
1917
|
+
const session = await this.storageManager.getActiveSession();
|
|
1918
|
+
if (!session) {
|
|
1919
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1920
|
+
}
|
|
1921
|
+
try {
|
|
1922
|
+
const res = await this.httpClient.importWallet({
|
|
1923
|
+
organizationId: session.organizationId,
|
|
1924
|
+
userId: userId || session.userId,
|
|
1925
|
+
encryptedBundle,
|
|
1926
|
+
walletName,
|
|
1927
|
+
accounts: accounts || [
|
|
1928
|
+
...turnkeyHelpers.DEFAULT_ETHEREUM_ACCOUNTS,
|
|
1929
|
+
...turnkeyHelpers.DEFAULT_SOLANA_ACCOUNTS,
|
|
1930
|
+
],
|
|
1931
|
+
}, stampWith);
|
|
1932
|
+
if (!res || !res.walletId) {
|
|
1933
|
+
throw new sdkTypes.TurnkeyError("No wallet ID found in the import response", sdkTypes.TurnkeyErrorCodes.BAD_RESPONSE);
|
|
1934
|
+
}
|
|
1935
|
+
return res.walletId;
|
|
1936
|
+
}
|
|
1937
|
+
catch (error) {
|
|
1938
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1939
|
+
throw error;
|
|
1940
|
+
throw new sdkTypes.TurnkeyError(`Failed to import wallet`, sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR, error);
|
|
1941
|
+
}
|
|
1942
|
+
};
|
|
1943
|
+
/**
|
|
1944
|
+
* Deletes the current sub-organization (sub-org) for the active session.
|
|
1945
|
+
*
|
|
1946
|
+
* - This function deletes the sub-organization associated with the current active session.
|
|
1947
|
+
* - By default, the deletion will fail if any wallets associated with the sub-organization have not been exported.
|
|
1948
|
+
* - If `deleteWithoutExport` is set to true, the sub-organization will be deleted even if its wallets have not been exported (potentially resulting in loss of access to those wallets).
|
|
1949
|
+
* - Requires an active session; otherwise, an error is thrown.
|
|
1950
|
+
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
1951
|
+
*
|
|
1952
|
+
* @param params.deleteWithoutExport - flag to delete the sub-organization without requiring all wallets to be exported first (defaults to false).
|
|
1953
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper.
|
|
1954
|
+
* @returns A promise that resolves to a `TDeleteSubOrganizationResponse` object containing the result of the deletion.
|
|
1955
|
+
* @throws {TurnkeyError} If there is no active session or if there is an error deleting the sub-organization.
|
|
1956
|
+
*/
|
|
1957
|
+
this.deleteSubOrganization = async (params) => {
|
|
1958
|
+
const { deleteWithoutExport = false, stampWith } = params || {};
|
|
1959
|
+
const session = await this.storageManager.getActiveSession();
|
|
1960
|
+
if (!session) {
|
|
1961
|
+
throw new sdkTypes.TurnkeyError("No active session found. Please log in first.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
1962
|
+
}
|
|
1963
|
+
try {
|
|
1964
|
+
return await this.httpClient.deleteSubOrganization({ deleteWithoutExport }, stampWith);
|
|
1965
|
+
}
|
|
1966
|
+
catch (error) {
|
|
1967
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1968
|
+
throw error;
|
|
1969
|
+
throw new sdkTypes.TurnkeyError(`Failed to delete sub-organization`, sdkTypes.TurnkeyErrorCodes.DELETE_SUB_ORGANIZATION_ERROR, error);
|
|
1970
|
+
}
|
|
1971
|
+
};
|
|
1972
|
+
/**
|
|
1973
|
+
* Stores a session token and updates the session associated with the specified session key, or by default the active session.
|
|
1974
|
+
*
|
|
1975
|
+
* - This function parses and stores a signed JWT session token in local storage, associating it with the given session key.
|
|
1976
|
+
* - If a sessionKey is provided, the session will be stored under that key; otherwise, it will use the default session key.
|
|
1977
|
+
* - If a session already exists for the session key, its associated key pair will be deleted before storing the new session.
|
|
1978
|
+
* - After storing the session, any unused key pairs are automatically cleared from storage.
|
|
1979
|
+
* - Ensures that session management is consistent and prevents orphaned key pairs.
|
|
1980
|
+
*
|
|
1981
|
+
* @param params.sessionToken - JWT session token to store.
|
|
1982
|
+
* @param params.sessionKey - session key to store the session under (defaults to the default session key).
|
|
1983
|
+
* @returns A promise that resolves when the session is successfully stored.
|
|
1984
|
+
* @throws {TurnkeyError} If there is an error storing the session or cleaning up key pairs.
|
|
1985
|
+
*/
|
|
1986
|
+
this.storeSession = async (params) => {
|
|
1987
|
+
const { sessionToken, sessionKey = base.SessionKey.DefaultSessionkey } = params;
|
|
1988
|
+
if (!sessionToken)
|
|
1989
|
+
return;
|
|
1990
|
+
try {
|
|
1991
|
+
const sessionToReplace = await this.storageManager.getSession(sessionKey);
|
|
1992
|
+
await this.storageManager.storeSession(sessionToken, sessionKey);
|
|
1993
|
+
if (sessionToReplace) {
|
|
1994
|
+
await this.apiKeyStamper?.deleteKeyPair(sessionToReplace.publicKey);
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
catch (error) {
|
|
1998
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
1999
|
+
throw error;
|
|
2000
|
+
throw new sdkTypes.TurnkeyError(`Failed to store session`, sdkTypes.TurnkeyErrorCodes.STORE_SESSION_ERROR, error);
|
|
2001
|
+
}
|
|
2002
|
+
finally {
|
|
2003
|
+
await this.clearUnusedKeyPairs();
|
|
2004
|
+
}
|
|
2005
|
+
};
|
|
2006
|
+
/**
|
|
2007
|
+
* Clears the session associated with the specified session key, or the active session by default.
|
|
2008
|
+
*
|
|
2009
|
+
* - This function deletes the session and its associated key pair from storage.
|
|
2010
|
+
* - If a sessionKey is provided, it will clear the session under that key; otherwise, it will clear the default (active) session.
|
|
2011
|
+
* - Removes the session data from local storage and deletes the corresponding API key pair from the key store.
|
|
2012
|
+
* - Throws an error if the session does not exist or if there is an error during the clearing process.
|
|
2013
|
+
*
|
|
2014
|
+
* @param params.sessionKey - session key to clear the session under (defaults to the default session key).
|
|
2015
|
+
* @returns A promise that resolves when the session is successfully cleared.
|
|
2016
|
+
* @throws {TurnkeyError} If the session does not exist or if there is an error clearing the session.
|
|
2017
|
+
*/
|
|
2018
|
+
this.clearSession = async (params) => {
|
|
2019
|
+
const { sessionKey = base.SessionKey.DefaultSessionkey } = params || {};
|
|
2020
|
+
try {
|
|
2021
|
+
const session = await this.storageManager.getSession(sessionKey);
|
|
2022
|
+
if (session) {
|
|
2023
|
+
await this.apiKeyStamper?.deleteKeyPair(session.publicKey);
|
|
2024
|
+
await this.storageManager.clearSession(sessionKey);
|
|
2025
|
+
}
|
|
2026
|
+
else {
|
|
2027
|
+
throw new sdkTypes.TurnkeyError(`No session found with key: ${sessionKey}`, sdkTypes.TurnkeyErrorCodes.NOT_FOUND);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
catch (error) {
|
|
2031
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
2032
|
+
throw error;
|
|
2033
|
+
throw new sdkTypes.TurnkeyError(`Failed to delete session`, sdkTypes.TurnkeyErrorCodes.CLEAR_SESSION_ERROR, error);
|
|
2034
|
+
}
|
|
2035
|
+
};
|
|
2036
|
+
/**
|
|
2037
|
+
* Clears all sessions and resets the active session state.
|
|
2038
|
+
*
|
|
2039
|
+
* - This function removes all session data from the client and persistent storage, including all associated key pairs.
|
|
2040
|
+
* - Iterates through all stored session keys, clearing each session and deleting its corresponding API key pair.
|
|
2041
|
+
* - After clearing, there will be no active session, and all session-related data will be removed from local storage.
|
|
2042
|
+
* - Throws an error if no sessions exist or if there is an error during the clearing process.
|
|
2043
|
+
*
|
|
2044
|
+
* @returns A promise that resolves when all sessions are successfully cleared.
|
|
2045
|
+
* @throws {TurnkeyError} If no sessions exist or if there is an error clearing all sessions.
|
|
2046
|
+
*/
|
|
2047
|
+
this.clearAllSessions = async () => {
|
|
2048
|
+
try {
|
|
2049
|
+
const sessionKeys = await this.storageManager.listSessionKeys();
|
|
2050
|
+
if (sessionKeys.length === 0) {
|
|
2051
|
+
throw new sdkTypes.TurnkeyError("No sessions found to clear.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
2052
|
+
}
|
|
2053
|
+
for (const sessionKey of sessionKeys) {
|
|
2054
|
+
this.clearSession({ sessionKey });
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
catch (error) {
|
|
2058
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
2059
|
+
throw error;
|
|
2060
|
+
throw new sdkTypes.TurnkeyError(`Failed to clear all sessions`, sdkTypes.TurnkeyErrorCodes.CLEAR_ALL_SESSIONS_ERROR, error);
|
|
2061
|
+
}
|
|
2062
|
+
};
|
|
2063
|
+
/**
|
|
2064
|
+
* Refreshes the session associated with the specified session key, or the active session by default.
|
|
2065
|
+
*
|
|
2066
|
+
* - This function refreshes the session and updates the session token and key pair associated with the given session key.
|
|
2067
|
+
* - If a sessionKey is provided, it will refresh the session under that key; otherwise, it will use the current active session key.
|
|
2068
|
+
* - Optionally allows specifying a new expiration time for the session, a custom public key, and whether to invalidate the existing session after refreshing.
|
|
2069
|
+
* - Makes a request to the Turnkey API to stamp a new login and stores the refreshed session token.
|
|
2070
|
+
* - Automatically manages key pair cleanup and session storage to ensure consistency.
|
|
2071
|
+
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
2072
|
+
*
|
|
2073
|
+
* @param params.sessionKey - session key to refresh the session under (defaults to the active session key).
|
|
2074
|
+
* @param params.expirationSeconds - expiration time in seconds for the refreshed session (defaults to the configured default).
|
|
2075
|
+
* @param params.publicKey - public key to use for the refreshed session (if not provided, a new key pair will be generated).
|
|
2076
|
+
* @param params.invalidateExisitng - flag to invalidate the existing session before refreshing (defaults to false).
|
|
2077
|
+
* @param params.stampWith - parameter to stamp the request with a specific stamper.
|
|
2078
|
+
* @returns A promise that resolves to a `TStampLoginResponse` object containing the refreshed session details.
|
|
2079
|
+
* @throws {TurnkeyError} If the session key does not exist, if there is no active session, or if there is an error refreshing the session.
|
|
2080
|
+
*/
|
|
2081
|
+
this.refreshSession = async (params) => {
|
|
2082
|
+
const { sessionKey = await this.storageManager.getActiveSessionKey(), expirationSeconds = base.DEFAULT_SESSION_EXPIRATION_IN_SECONDS, publicKey, invalidateExisitng = false, } = params || {};
|
|
2083
|
+
if (!sessionKey) {
|
|
2084
|
+
throw new sdkTypes.TurnkeyError("No session key provided or active session to refresh session", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
2085
|
+
}
|
|
2086
|
+
const session = await this.getSession({
|
|
2087
|
+
sessionKey: sessionKey,
|
|
2088
|
+
});
|
|
2089
|
+
if (!session) {
|
|
2090
|
+
throw new sdkTypes.TurnkeyError(`No active session found: ${sessionKey}`, sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
2091
|
+
}
|
|
2092
|
+
if (!this.httpClient) {
|
|
2093
|
+
throw new sdkTypes.TurnkeyError("HTTP client is not initialized. Please initialize the client before refreshing the session.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
2094
|
+
}
|
|
2095
|
+
let keyPair;
|
|
2096
|
+
try {
|
|
2097
|
+
keyPair = publicKey ?? (await this.apiKeyStamper?.createKeyPair());
|
|
2098
|
+
if (!keyPair) {
|
|
2099
|
+
throw new sdkTypes.TurnkeyError("Failed to create new key pair.", sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
2100
|
+
}
|
|
2101
|
+
const res = await this.httpClient.stampLogin({
|
|
2102
|
+
publicKey: keyPair,
|
|
2103
|
+
expirationSeconds,
|
|
2104
|
+
invalidateExisting: invalidateExisitng,
|
|
2105
|
+
}, params?.stampWith);
|
|
2106
|
+
if (!res || !res.session) {
|
|
2107
|
+
throw new sdkTypes.TurnkeyError("No session found in the refresh response", sdkTypes.TurnkeyErrorCodes.BAD_RESPONSE);
|
|
2108
|
+
}
|
|
2109
|
+
await this.storeSession({
|
|
2110
|
+
sessionToken: res.session,
|
|
2111
|
+
...(sessionKey && { sessionKey }),
|
|
2112
|
+
});
|
|
2113
|
+
return res;
|
|
2114
|
+
}
|
|
2115
|
+
catch (error) {
|
|
2116
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
2117
|
+
throw error;
|
|
2118
|
+
throw new sdkTypes.TurnkeyError(`Failed to refresh session`, sdkTypes.TurnkeyErrorCodes.REFRESH_SESSION_ERROR, error);
|
|
2119
|
+
}
|
|
2120
|
+
};
|
|
2121
|
+
/**
|
|
2122
|
+
* Retrieves the session associated with the specified session key, or the active session by default.
|
|
2123
|
+
*
|
|
2124
|
+
* - This function retrieves the session object from storage, using the provided session key or, if not specified, the current active session key.
|
|
2125
|
+
* - If no session key is provided and there is no active session, it returns undefined.
|
|
2126
|
+
* - Returns the session details, including public key, organization ID, user ID, and expiration.
|
|
2127
|
+
*
|
|
2128
|
+
* @param params.sessionKey - session key to retrieve a specific session (defaults to the current active session key).
|
|
2129
|
+
* @returns A promise that resolves to a `Session` object containing the session details, or undefined if not found.
|
|
2130
|
+
* @throws {TurnkeyError} If there is an error retrieving the session from storage.
|
|
2131
|
+
*/
|
|
2132
|
+
this.getSession = async (params) => {
|
|
2133
|
+
try {
|
|
2134
|
+
const { sessionKey = await this.storageManager.getActiveSessionKey() } = params || {};
|
|
2135
|
+
return this.storageManager.getSession(sessionKey);
|
|
2136
|
+
}
|
|
2137
|
+
catch (error) {
|
|
2138
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
2139
|
+
throw error;
|
|
2140
|
+
throw new sdkTypes.TurnkeyError(`Failed to get session with key`, sdkTypes.TurnkeyErrorCodes.GET_SESSION_ERROR, error);
|
|
2141
|
+
}
|
|
2142
|
+
};
|
|
2143
|
+
/**
|
|
2144
|
+
* Retrieves all sessions stored in persistent storage.
|
|
2145
|
+
*
|
|
2146
|
+
* - This function fetches all session objects currently stored by the client, including those that are not active.
|
|
2147
|
+
* - Returns a record mapping each session key to its corresponding `Session` object.
|
|
2148
|
+
* - Useful for session management, auditing, or displaying all available sessions to the user.
|
|
2149
|
+
* - Automatically skips any session keys that do not have a valid session object.
|
|
2150
|
+
*
|
|
2151
|
+
* @returns A promise that resolves to a record of session keys and their corresponding `Session` objects, or `undefined` if no sessions exist.
|
|
2152
|
+
* @throws {TurnkeyError} If there is an error retrieving sessions from storage.
|
|
2153
|
+
*/
|
|
2154
|
+
this.getAllSessions = async () => {
|
|
2155
|
+
try {
|
|
2156
|
+
const sessionKeys = await this.storageManager.listSessionKeys();
|
|
2157
|
+
if (!sessionKeys || sessionKeys.length === 0) {
|
|
2158
|
+
return undefined;
|
|
2159
|
+
}
|
|
2160
|
+
const sessions = {};
|
|
2161
|
+
for (const sessionKey of sessionKeys) {
|
|
2162
|
+
const session = await this.storageManager.getSession(sessionKey);
|
|
2163
|
+
if (session) {
|
|
2164
|
+
sessions[sessionKey] = session;
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
return sessions;
|
|
2168
|
+
}
|
|
2169
|
+
catch (error) {
|
|
2170
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
2171
|
+
throw error;
|
|
2172
|
+
throw new sdkTypes.TurnkeyError(`Failed to get all sessions`, sdkTypes.TurnkeyErrorCodes.GET_ALL_SESSIONS_ERROR, error);
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
/**
|
|
2176
|
+
* Sets the active session to the specified session key.
|
|
2177
|
+
*
|
|
2178
|
+
* - This function updates the `activeSessionKey` in persistent storage to the specified session key.
|
|
2179
|
+
* - Ensures that subsequent operations use the session associated with this key as the active session.
|
|
2180
|
+
* - Does not validate whether the session key exists or is valid; it simply updates the pointer.
|
|
2181
|
+
* - Useful for switching between multiple stored sessions or restoring a previous session context.
|
|
2182
|
+
*
|
|
2183
|
+
* @param params.sessionKey - session key to set as the active session.
|
|
2184
|
+
* @returns A promise that resolves when the active session key is successfully set.
|
|
2185
|
+
* @throws {TurnkeyError} If the client is not initialized or if there is an error setting the active session key.
|
|
2186
|
+
*/
|
|
2187
|
+
this.setActiveSession = async (params) => {
|
|
2188
|
+
const { sessionKey } = params;
|
|
2189
|
+
try {
|
|
2190
|
+
await this.storageManager.setActiveSessionKey(sessionKey);
|
|
2191
|
+
}
|
|
2192
|
+
catch (error) {
|
|
2193
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
2194
|
+
throw error;
|
|
2195
|
+
throw new sdkTypes.TurnkeyError(`Failed to set active session`, sdkTypes.TurnkeyErrorCodes.SET_ACTIVE_SESSION_ERROR, error);
|
|
2196
|
+
}
|
|
2197
|
+
};
|
|
2198
|
+
/**
|
|
2199
|
+
* Retrieves the active session key currently set in persistent storage.
|
|
2200
|
+
*
|
|
2201
|
+
* - This function fetches the session key that is currently marked as active in the client's persistent storage.
|
|
2202
|
+
* - The active session key determines which session is used for all session-dependent operations.
|
|
2203
|
+
* - If no active session key is set, returns `undefined`.
|
|
2204
|
+
* - Useful for determining which session is currently in use, especially when managing multiple sessions.
|
|
2205
|
+
*
|
|
2206
|
+
* @returns A promise that resolves to the active session key as a string, or `undefined` if no active session is set.
|
|
2207
|
+
* @throws {TurnkeyError} If there is an error retrieving the active session key from storage.
|
|
2208
|
+
*/
|
|
2209
|
+
this.getActiveSessionKey = async () => {
|
|
2210
|
+
try {
|
|
2211
|
+
return await this.storageManager.getActiveSessionKey();
|
|
2212
|
+
}
|
|
2213
|
+
catch (error) {
|
|
2214
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
2215
|
+
throw error;
|
|
2216
|
+
throw new sdkTypes.TurnkeyError(`Failed to get active session key`, sdkTypes.TurnkeyErrorCodes.GET_ACTIVE_SESSION_KEY_ERROR, error);
|
|
2217
|
+
}
|
|
2218
|
+
};
|
|
2219
|
+
/**
|
|
2220
|
+
* Clears any unused API key pairs from persistent storage.
|
|
2221
|
+
*
|
|
2222
|
+
* - This function scans all API key pairs stored in indexedDB and removes any key pairs that are not associated with a session in persistent storage.
|
|
2223
|
+
* - Ensures that only key pairs referenced by existing sessions are retained, preventing orphaned or stale key pairs from accumulating.
|
|
2224
|
+
* - Iterates through all stored session keys and builds a map of in-use public keys, then deletes any key pairs not present in this map.
|
|
2225
|
+
* - Intended to be called after session changes (e.g., login, logout, session replacement) to keep key storage clean and secure.
|
|
2226
|
+
*
|
|
2227
|
+
* @returns A promise that resolves when all unused key pairs are successfully cleared.
|
|
2228
|
+
* @throws {TurnkeyError} If there is an error listing, checking, or deleting unused key pairs.
|
|
2229
|
+
*/
|
|
2230
|
+
this.clearUnusedKeyPairs = async () => {
|
|
2231
|
+
try {
|
|
2232
|
+
const publicKeys = await this.apiKeyStamper?.listKeyPairs();
|
|
2233
|
+
if (!publicKeys || publicKeys.length === 0) {
|
|
2234
|
+
return;
|
|
2235
|
+
}
|
|
2236
|
+
const sessionKeys = await this.storageManager?.listSessionKeys();
|
|
2237
|
+
const sessionTokensMap = {};
|
|
2238
|
+
for (const sessionKey of sessionKeys) {
|
|
2239
|
+
const session = await this.storageManager.getSession(sessionKey);
|
|
2240
|
+
if (session) {
|
|
2241
|
+
sessionTokensMap[session.publicKey] = sessionKey;
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
for (const publicKey of publicKeys) {
|
|
2245
|
+
if (!sessionTokensMap[publicKey]) {
|
|
2246
|
+
try {
|
|
2247
|
+
await this.apiKeyStamper?.deleteKeyPair(publicKey);
|
|
2248
|
+
}
|
|
2249
|
+
catch (error) {
|
|
2250
|
+
throw new sdkTypes.TurnkeyError(`Failed to delete unused key pair ${publicKey}`, sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR, error);
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
catch (error) {
|
|
2256
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
2257
|
+
throw error;
|
|
2258
|
+
throw new sdkTypes.TurnkeyError(`Failed to clear unused key pairs`, sdkTypes.TurnkeyErrorCodes.CLEAR_UNUSED_KEY_PAIRS_ERROR, error);
|
|
2259
|
+
}
|
|
2260
|
+
};
|
|
2261
|
+
/**
|
|
2262
|
+
* Creates a new API key pair and returns the public key.
|
|
2263
|
+
*
|
|
2264
|
+
* - This function generates a new API key pair and stores it in the underlying key store (IndexedDB).
|
|
2265
|
+
* - If an external key pair is provided, it will use that key pair for creation instead of generating a new one.
|
|
2266
|
+
* - If `storeOverride` is set to true, the generated or provided public key will be set as the override key in the API key stamper, making it the active key for subsequent signing operations.
|
|
2267
|
+
* - Ensures the API key stamper is initialized before proceeding.
|
|
2268
|
+
* - Handles both native CryptoKeyPair objects and raw key material.
|
|
2269
|
+
*
|
|
2270
|
+
* @param params.externalKeyPair - An externally generated key pair (either a CryptoKeyPair or an object with publicKey/privateKey strings) to use instead of generating a new one.
|
|
2271
|
+
* @param params.storeOverride - If true, sets the generated or provided public key as the override key in the API key stamper (defaults to false).
|
|
2272
|
+
* @returnparams.s A promise that resolves to the public key of the created or provided API key pair as a string.
|
|
2273
|
+
* @throws {TurnkeyError} If the API key stamper is not initialized or if there is an error during key pair creation or storage.
|
|
2274
|
+
*/
|
|
2275
|
+
this.createApiKeyPair = async (params) => {
|
|
2276
|
+
if (!this.apiKeyStamper) {
|
|
2277
|
+
throw new sdkTypes.TurnkeyError("API Key Stamper is not initialized.", sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
2278
|
+
}
|
|
2279
|
+
const externalKeyPair = params?.externalKeyPair;
|
|
2280
|
+
const storeOverride = params?.storeOverride ?? false;
|
|
2281
|
+
try {
|
|
2282
|
+
const publicKey = await this.apiKeyStamper.createKeyPair(externalKeyPair ? externalKeyPair : undefined);
|
|
2283
|
+
if (storeOverride && publicKey) {
|
|
2284
|
+
await this.apiKeyStamper.setPublicKeyOverride(publicKey);
|
|
2285
|
+
}
|
|
2286
|
+
return publicKey;
|
|
2287
|
+
}
|
|
2288
|
+
catch (error) {
|
|
2289
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
2290
|
+
throw error;
|
|
2291
|
+
throw new sdkTypes.TurnkeyError(`Failed to create API key pair`, sdkTypes.TurnkeyErrorCodes.CREATE_API_KEY_PAIR_ERROR, error);
|
|
2292
|
+
}
|
|
2293
|
+
};
|
|
2294
|
+
/**
|
|
2295
|
+
* Fetches the WalletKit proxy authentication configuration from the auth proxy.
|
|
2296
|
+
*
|
|
2297
|
+
* - This function makes a request to the Turnkey auth proxy to retrieve the current WalletKit configuration,
|
|
2298
|
+
* including supported authentication methods, OAuth providers, and any custom proxy settings.
|
|
2299
|
+
* - Useful for dynamically configuring the client UI or authentication flows based on the proxy's capabilities.
|
|
2300
|
+
* - Ensures that the client is aware of the latest proxy-side configuration, which may affect available login/signup options.
|
|
2301
|
+
*
|
|
2302
|
+
* @returns A promise that resolves to a `ProxyTGetWalletKitConfigResponse` object containing the proxy authentication configuration.
|
|
2303
|
+
* @throws {TurnkeyError} If there is an error retrieving the proxy authentication configuration from the auth proxy.
|
|
2304
|
+
*/
|
|
2305
|
+
this.getProxyAuthConfig = async () => {
|
|
2306
|
+
try {
|
|
2307
|
+
const res = await this.httpClient.proxyGetWalletKitConfig({});
|
|
2308
|
+
if (!res) {
|
|
2309
|
+
throw new sdkTypes.TurnkeyError(`Failed to fetch auth proxy config`, sdkTypes.TurnkeyErrorCodes.GET_PROXY_AUTH_CONFIG_ERROR);
|
|
2310
|
+
}
|
|
2311
|
+
return res;
|
|
2312
|
+
}
|
|
2313
|
+
catch (error) {
|
|
2314
|
+
if (error instanceof sdkTypes.TurnkeyError)
|
|
2315
|
+
throw error;
|
|
2316
|
+
throw new sdkTypes.TurnkeyError(`Failed to get auth proxy config`, sdkTypes.TurnkeyErrorCodes.GET_PROXY_AUTH_CONFIG_ERROR, error);
|
|
2317
|
+
}
|
|
2318
|
+
};
|
|
2319
|
+
this.config = config;
|
|
2320
|
+
// Just store any explicitly provided stampers
|
|
2321
|
+
this.apiKeyStamper = apiKeyStamper;
|
|
2322
|
+
this.passkeyStamper = passkeyStamper;
|
|
2323
|
+
this.walletManager = walletManager;
|
|
2324
|
+
// Actual initialization will happen in init()
|
|
2325
|
+
}
|
|
2326
|
+
async init() {
|
|
2327
|
+
// Initialize storage manager
|
|
2328
|
+
// TODO (Amir): StorageManager should be a class that extends StorageBase and has an init method
|
|
2329
|
+
this.storageManager = await base$1.createStorageManager();
|
|
2330
|
+
// Initialize the API key stamper
|
|
2331
|
+
this.apiKeyStamper = new base$2.CrossPlatformApiKeyStamper(this.storageManager);
|
|
2332
|
+
await this.apiKeyStamper.init();
|
|
2333
|
+
if (this.config.passkeyConfig) {
|
|
2334
|
+
this.passkeyStamper = new base$3.CrossPlatformPasskeyStamper(this.config.passkeyConfig);
|
|
2335
|
+
await this.passkeyStamper.init();
|
|
2336
|
+
}
|
|
2337
|
+
if (this.config.walletConfig?.features?.auth ||
|
|
2338
|
+
this.config.walletConfig?.features?.connecting) {
|
|
2339
|
+
this.walletManager = await base$4.createWalletManager(this.config.walletConfig);
|
|
2340
|
+
}
|
|
2341
|
+
// We can comfortably default to the prod urls here
|
|
2342
|
+
const apiBaseUrl = this.config.apiBaseUrl || "https://api.turnkey.com";
|
|
2343
|
+
const authProxyUrl = this.config.authProxyUrl || "https://authproxy.turnkey.com";
|
|
2344
|
+
// Initialize the HTTP client with the appropriate stampers
|
|
2345
|
+
this.httpClient = new sdkClientBase.TurnkeySDKClientBase({
|
|
2346
|
+
...this.config,
|
|
2347
|
+
apiBaseUrl,
|
|
2348
|
+
authProxyUrl,
|
|
2349
|
+
apiKeyStamper: this.apiKeyStamper,
|
|
2350
|
+
passkeyStamper: this.passkeyStamper,
|
|
2351
|
+
walletStamper: this.walletManager?.stamper,
|
|
2352
|
+
storageManager: this.storageManager,
|
|
2353
|
+
});
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
exports.TurnkeyClient = TurnkeyClient;
|
|
2358
|
+
//# sourceMappingURL=core.js.map
|