@turnkey/core 1.14.1 → 2.1.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/dist/__clients__/core.d.ts +86 -33
- package/dist/__clients__/core.d.ts.map +1 -1
- package/dist/__clients__/core.js +364 -201
- package/dist/__clients__/core.js.map +1 -1
- package/dist/__clients__/core.mjs +365 -202
- package/dist/__clients__/core.mjs.map +1 -1
- package/dist/__generated__/sdk-client-base.d.ts +40 -3
- package/dist/__generated__/sdk-client-base.d.ts.map +1 -1
- package/dist/__generated__/sdk-client-base.js +961 -101
- package/dist/__generated__/sdk-client-base.js.map +1 -1
- package/dist/__generated__/sdk-client-base.mjs +956 -96
- package/dist/__generated__/sdk-client-base.mjs.map +1 -1
- package/dist/__generated__/version.d.ts +1 -1
- package/dist/__generated__/version.d.ts.map +1 -1
- package/dist/__generated__/version.js +1 -1
- package/dist/__generated__/version.mjs +1 -1
- package/dist/__stampers__/api/base.d.ts +2 -1
- package/dist/__stampers__/api/base.d.ts.map +1 -1
- package/dist/__stampers__/api/mobile/stamper.d.ts +2 -1
- package/dist/__stampers__/api/mobile/stamper.d.ts.map +1 -1
- package/dist/__stampers__/api/mobile/stamper.js.map +1 -1
- package/dist/__stampers__/api/mobile/stamper.mjs.map +1 -1
- package/dist/__stampers__/api/web/stamper.d.ts +2 -1
- package/dist/__stampers__/api/web/stamper.d.ts.map +1 -1
- package/dist/__stampers__/api/web/stamper.js.map +1 -1
- package/dist/__stampers__/api/web/stamper.mjs.map +1 -1
- package/dist/__stampers__/passkey/base.d.ts +3 -1
- package/dist/__stampers__/passkey/base.d.ts.map +1 -1
- package/dist/__stampers__/passkey/base.js +19 -0
- package/dist/__stampers__/passkey/base.js.map +1 -1
- package/dist/__stampers__/passkey/base.mjs +20 -1
- package/dist/__stampers__/passkey/base.mjs.map +1 -1
- package/dist/__tests__/poll-transaction-status-test.d.ts +2 -0
- package/dist/__tests__/poll-transaction-status-test.d.ts.map +1 -0
- package/dist/__tests__/utils-test.d.ts +2 -0
- package/dist/__tests__/utils-test.d.ts.map +1 -0
- package/dist/__types__/auth.d.ts +2 -41
- package/dist/__types__/auth.d.ts.map +1 -1
- package/dist/__types__/auth.js.map +1 -1
- package/dist/__types__/auth.mjs.map +1 -1
- package/dist/__types__/config.d.ts +3 -2
- package/dist/__types__/config.d.ts.map +1 -1
- package/dist/__types__/enums.d.ts +1 -0
- package/dist/__types__/enums.d.ts.map +1 -1
- package/dist/__types__/enums.js +1 -0
- package/dist/__types__/enums.js.map +1 -1
- package/dist/__types__/enums.mjs +1 -0
- package/dist/__types__/enums.mjs.map +1 -1
- package/dist/__types__/http.d.ts +1 -14
- package/dist/__types__/http.d.ts.map +1 -1
- package/dist/__types__/index.d.ts +0 -1
- package/dist/__types__/index.d.ts.map +1 -1
- package/dist/__types__/method-types/shared.d.ts +28 -13
- package/dist/__types__/method-types/shared.d.ts.map +1 -1
- package/dist/__wallet__/stamper.d.ts +2 -1
- package/dist/__wallet__/stamper.d.ts.map +1 -1
- package/dist/__wallet__/stamper.js.map +1 -1
- package/dist/__wallet__/stamper.mjs.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -4
- package/dist/index.mjs.map +1 -1
- package/dist/turnkey-helpers.d.ts +4 -0
- package/dist/turnkey-helpers.d.ts.map +1 -1
- package/dist/turnkey-helpers.js +28 -0
- package/dist/turnkey-helpers.js.map +1 -1
- package/dist/turnkey-helpers.mjs +25 -1
- package/dist/turnkey-helpers.mjs.map +1 -1
- package/dist/utils.d.ts +41 -18
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +126 -57
- package/dist/utils.js.map +1 -1
- package/dist/utils.mjs +100 -38
- package/dist/utils.mjs.map +1 -1
- package/package.json +8 -8
- package/dist/__types__/error.d.ts +0 -13
- package/dist/__types__/error.d.ts.map +0 -1
- package/dist/__types__/error.js +0 -18
- package/dist/__types__/error.js.map +0 -1
- package/dist/__types__/error.mjs +0 -16
- package/dist/__types__/error.mjs.map +0 -1
- package/dist/__types__/http.js +0 -11
- package/dist/__types__/http.js.map +0 -1
- package/dist/__types__/http.mjs +0 -9
- package/dist/__types__/http.mjs.map +0 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { TurnkeySDKClientBase } from '../__generated__/sdk-client-base.mjs';
|
|
2
2
|
import { TurnkeyErrorCodes, TurnkeyError, AuthAction } from '@turnkey/sdk-types';
|
|
3
3
|
import { DEFAULT_SESSION_EXPIRATION_IN_SECONDS } from '../__types__/auth.mjs';
|
|
4
|
-
import { SessionKey, StamperType, WalletSource, FilterType,
|
|
5
|
-
import { withTurnkeyErrorHandling, isValidPasskeyName, isWeb, isReactNative, buildSignUpBody, findWalletProviderFromAddress, getPublicKeyFromStampHeader, addressFromPublicKey, getCurveTypeFromProvider, getClientSignatureMessageForLogin, getClientSignatureMessageForSignup, getAuthenticatorAddresses, fetchAllWalletAccountsWithCursor, mapAccountsToWallet, toExternalTimestamp, isEthereumProvider, isSolanaProvider, getActiveSessionOrThrowIfRequired, getHashFunction, getEncodingType, getEncodedMessage, splitSignature, broadcastTransaction, getPolicySignature, googleISS, isWalletAccountArray, generateWalletAccountsFromAddressFormat } from '../utils.mjs';
|
|
4
|
+
import { SessionKey, StamperType, WalletSource, FilterType, OtpType, OtpTypeToFilterTypeMap, Curve, Chain, SignIntent } from '../__types__/enums.mjs';
|
|
5
|
+
import { withTurnkeyErrorHandling, isValidPasskeyName, isWeb, isReactNative, buildSignUpBody, findWalletProviderFromAddress, getPublicKeyFromStampHeader, addressFromPublicKey, getCurveTypeFromProvider, getClientSignatureMessageForLogin, getClientSignatureMessageForSignup, getAuthenticatorAddresses, fetchAllWalletAccountsWithCursor, mapAccountsToWallet, toExternalTimestamp, isEthereumProvider, isSolanaProvider, getActiveSessionOrThrowIfRequired, getHashFunction, getEncodingType, getEncodedMessage, splitSignature, broadcastTransaction, ERC20_TRANSFER_ABI, getPolicySignature, googleISS, isWalletAccountArray, generateWalletAccountsFromAddressFormat } from '../utils.mjs';
|
|
6
|
+
import { encryptOtpCodeToBundle, verify } from '@turnkey/crypto';
|
|
6
7
|
import { createStorageManager } from '../__storage__/base.mjs';
|
|
7
8
|
import { CrossPlatformApiKeyStamper } from '../__stampers__/api/base.mjs';
|
|
8
9
|
import { CrossPlatformPasskeyStamper } from '../__stampers__/passkey/base.mjs';
|
|
@@ -10,26 +11,68 @@ import { DEFAULT_ETHEREUM_ACCOUNTS, DEFAULT_SOLANA_ACCOUNTS } from '../turnkey-h
|
|
|
10
11
|
import { jwtDecode } from 'jwt-decode';
|
|
11
12
|
import { createWalletManager } from '../__wallet__/base.mjs';
|
|
12
13
|
import { toUtf8Bytes } from 'ethers';
|
|
13
|
-
import { verify } from '@turnkey/crypto';
|
|
14
14
|
import { SignatureFormat } from '@turnkey/api-key-stamper';
|
|
15
15
|
import { encodeFunctionData } from 'viem';
|
|
16
16
|
|
|
17
|
-
const ERC20_TRANSFER_ABI = [
|
|
18
|
-
{
|
|
19
|
-
type: "function",
|
|
20
|
-
name: "transfer",
|
|
21
|
-
stateMutability: "nonpayable",
|
|
22
|
-
inputs: [
|
|
23
|
-
{ name: "to", type: "address" },
|
|
24
|
-
{ name: "amount", type: "uint256" },
|
|
25
|
-
],
|
|
26
|
-
outputs: [{ name: "success", type: "bool" }],
|
|
27
|
-
},
|
|
28
|
-
];
|
|
29
17
|
class TurnkeyClient {
|
|
30
18
|
constructor(config,
|
|
31
19
|
// Users can pass in their own stampers, or we will create them. Should we remove this?
|
|
32
20
|
apiKeyStamper, passkeyStamper, walletManager) {
|
|
21
|
+
/**
|
|
22
|
+
* Overrides the API key stamper configuration.
|
|
23
|
+
*
|
|
24
|
+
* - Set `temporaryPublicKey` to a non-empty string to override the default API key.
|
|
25
|
+
* - Set `temporaryPublicKey` to an empty string ("") to clear the temporary key and restore default behavior.
|
|
26
|
+
* - Omit `temporaryPublicKey` to leave it unchanged.
|
|
27
|
+
* - Useful for dynamically changing the API key used for signing requests.
|
|
28
|
+
*
|
|
29
|
+
* @param params.temporaryPublicKey - temporary public key to set (non-empty string), clear (empty string), or leave unchanged (omit).
|
|
30
|
+
* @returns A promise that resolves when the stamper has been updated.
|
|
31
|
+
* @throws {TurnkeyError} If the API key stamper is not initialized or if there is an error updating it.
|
|
32
|
+
*/
|
|
33
|
+
this.overrideApiKeyStamper = async (params) => {
|
|
34
|
+
return withTurnkeyErrorHandling(async () => {
|
|
35
|
+
if (!this.apiKeyStamper) {
|
|
36
|
+
throw new TurnkeyError("API key stamper is not initialized", TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
37
|
+
}
|
|
38
|
+
const { temporaryPublicKey } = params;
|
|
39
|
+
// Only process if temporaryPublicKey is explicitly passed
|
|
40
|
+
if (temporaryPublicKey !== undefined) {
|
|
41
|
+
if (temporaryPublicKey === "") {
|
|
42
|
+
this.apiKeyStamper.clearTemporaryPublicKey();
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
this.apiKeyStamper.setTemporaryPublicKey(temporaryPublicKey);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}, {
|
|
49
|
+
errorMessage: "Failed to override API key stamper",
|
|
50
|
+
errorCode: TurnkeyErrorCodes.INITIALIZE_API_KEY_STAMPER_ERROR,
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Overrides the passkey stamper with a new configuration.
|
|
55
|
+
*
|
|
56
|
+
* - This function updates the passkey stamper configuration without reinitializing the entire stamper.
|
|
57
|
+
* - Updates the stamper's configuration properties (e.g., allowCredentials, rpId, timeout, userVerification).
|
|
58
|
+
* - Useful for dynamically changing passkey configuration during runtime.
|
|
59
|
+
*
|
|
60
|
+
* @param params.config - new passkey stamper configuration to use.
|
|
61
|
+
* @returns A promise that resolves when the stamper configuration has been updated.
|
|
62
|
+
* @throws {TurnkeyError} If there is an error updating the stamper configuration or if the passkey stamper is not initialized.
|
|
63
|
+
*/
|
|
64
|
+
this.overridePasskeyStamper = async (params) => {
|
|
65
|
+
const { config } = params;
|
|
66
|
+
return withTurnkeyErrorHandling(async () => {
|
|
67
|
+
if (!this.passkeyStamper) {
|
|
68
|
+
throw new TurnkeyError("Passkey stamper is not initialized", TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
69
|
+
}
|
|
70
|
+
this.passkeyStamper.updateConfig(config);
|
|
71
|
+
}, {
|
|
72
|
+
errorMessage: "Failed to override passkey stamper",
|
|
73
|
+
errorCode: TurnkeyErrorCodes.INITIALIZE_PASSKEY_STAMPER_ERROR,
|
|
74
|
+
});
|
|
75
|
+
};
|
|
33
76
|
/**
|
|
34
77
|
* Creates a new TurnkeySDKClientBase instance with the provided configuration.
|
|
35
78
|
* This method is used internally to create the HTTP client for making API requests,
|
|
@@ -147,14 +190,14 @@ class TurnkeyClient {
|
|
|
147
190
|
if (params?.sessionKey) {
|
|
148
191
|
const session = await this.storageManager.getSession(params.sessionKey);
|
|
149
192
|
this.storageManager.clearSession(params.sessionKey);
|
|
150
|
-
this.
|
|
193
|
+
await this.deleteApiKeyPair({ publicKey: session?.publicKey });
|
|
151
194
|
}
|
|
152
195
|
else {
|
|
153
196
|
const sessionKey = await this.storageManager.getActiveSessionKey();
|
|
154
197
|
const session = await this.storageManager.getActiveSession();
|
|
155
198
|
if (sessionKey) {
|
|
156
199
|
this.storageManager.clearSession(sessionKey);
|
|
157
|
-
this.
|
|
200
|
+
await this.deleteApiKeyPair({ publicKey: session?.publicKey });
|
|
158
201
|
}
|
|
159
202
|
else {
|
|
160
203
|
throw new TurnkeyError("No active session found to log out from.", TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
@@ -178,37 +221,52 @@ class TurnkeyClient {
|
|
|
178
221
|
* @param params.sessionKey - session key to use for session creation (defaults to the default session key).
|
|
179
222
|
* @param params.expirationSeconds - session expiration time in seconds (defaults to the configured default).
|
|
180
223
|
* @param params.organizationId - organization ID to target (defaults to the session's organization ID or the parent organization ID).
|
|
224
|
+
* @param params.allowCredentials - optional list of allowed credentials for passkey authentication. This allows you to restrict which passkeys can be used for login.
|
|
181
225
|
* @returns A promise that resolves to a {@link PasskeyAuthResult}, which includes:
|
|
182
226
|
* - `sessionToken`: the signed JWT session token.
|
|
183
227
|
* - `credentialId`: an empty string.
|
|
184
228
|
* @throws {TurnkeyError} If there is an error during the passkey login process or if the user cancels the passkey prompt.
|
|
185
229
|
*/
|
|
186
230
|
this.loginWithPasskey = async (params) => {
|
|
187
|
-
|
|
231
|
+
const shouldOverrideConfig = params?.allowCredentials && this.passkeyStamper;
|
|
232
|
+
const currentConfig = this.config.passkeyConfig;
|
|
233
|
+
const generatedPublicKey = params?.publicKey || (await this.createApiKeyPair());
|
|
188
234
|
return await withTurnkeyErrorHandling(async () => {
|
|
189
|
-
|
|
190
|
-
|
|
235
|
+
if (!this.passkeyStamper) {
|
|
236
|
+
throw new TurnkeyError("Passkey stamper is not initialized", TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
237
|
+
}
|
|
191
238
|
const sessionKey = params?.sessionKey || SessionKey.DefaultSessionkey;
|
|
192
239
|
const expirationSeconds = params?.expirationSeconds || DEFAULT_SESSION_EXPIRATION_IN_SECONDS;
|
|
240
|
+
if (shouldOverrideConfig) {
|
|
241
|
+
// Override passkey stamper config to include allowCredentials
|
|
242
|
+
const mergedConfig = {
|
|
243
|
+
...currentConfig,
|
|
244
|
+
allowCredentials: params?.allowCredentials, // Can safely assert non-null due to check above
|
|
245
|
+
};
|
|
246
|
+
await this.overridePasskeyStamper({ config: mergedConfig });
|
|
247
|
+
}
|
|
193
248
|
if (!generatedPublicKey) {
|
|
194
249
|
throw new TurnkeyError("A publickey could not be found or generated.", TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
195
250
|
}
|
|
196
|
-
const
|
|
251
|
+
const loginPayload = {
|
|
197
252
|
publicKey: generatedPublicKey,
|
|
198
253
|
organizationId: params?.organizationId ?? this.config.organizationId,
|
|
199
254
|
expirationSeconds,
|
|
200
|
-
}
|
|
255
|
+
};
|
|
256
|
+
const passkeyLoginRequest = await this.httpClient.stampStampLogin(loginPayload, StamperType.Passkey);
|
|
257
|
+
if (!passkeyLoginRequest) {
|
|
258
|
+
throw new TurnkeyError("Failed to generate login payload for passkey login.", TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
259
|
+
}
|
|
260
|
+
// For passkey stamps, the stamp header value is JSON that includes the credentialId used to sign. Extract it here so we can return it to the caller.
|
|
261
|
+
const credentialId = JSON.parse(passkeyLoginRequest.stamp.stampHeaderValue).credentialId;
|
|
262
|
+
const sessionResponse = await this.httpClient.sendSignedRequest(passkeyLoginRequest);
|
|
201
263
|
await this.storeSession({
|
|
202
264
|
sessionToken: sessionResponse.session,
|
|
203
265
|
sessionKey,
|
|
204
266
|
});
|
|
205
|
-
generatedPublicKey = undefined; // Key pair was successfully used, set to null to prevent cleanup
|
|
206
267
|
return {
|
|
207
268
|
sessionToken: sessionResponse.session,
|
|
208
|
-
|
|
209
|
-
// from a quick glance this is going to be difficult
|
|
210
|
-
// for now we return an empty string
|
|
211
|
-
credentialId: "",
|
|
269
|
+
credentialId,
|
|
212
270
|
};
|
|
213
271
|
}, {
|
|
214
272
|
errorMessage: "Unable to log in with the provided passkey",
|
|
@@ -219,15 +277,23 @@ class TurnkeyClient {
|
|
|
219
277
|
code: TurnkeyErrorCodes.SELECT_PASSKEY_CANCELLED,
|
|
220
278
|
},
|
|
221
279
|
},
|
|
280
|
+
catchFn: async () => {
|
|
281
|
+
try {
|
|
282
|
+
await this.deleteApiKeyPair({ publicKey: generatedPublicKey });
|
|
283
|
+
}
|
|
284
|
+
catch (cleanupError) {
|
|
285
|
+
throw new TurnkeyError(`Failed to clean up generated key pair`, TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
286
|
+
}
|
|
287
|
+
},
|
|
222
288
|
}, {
|
|
223
289
|
finallyFn: async () => {
|
|
224
|
-
if (
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
290
|
+
if (shouldOverrideConfig) {
|
|
291
|
+
// we restore previous stamper after login attempt
|
|
292
|
+
await this.overridePasskeyStamper({
|
|
293
|
+
// we can assert this safely, since if this statement runs we are certian the passkey stamper
|
|
294
|
+
// is initialized
|
|
295
|
+
config: currentConfig,
|
|
296
|
+
});
|
|
231
297
|
}
|
|
232
298
|
},
|
|
233
299
|
});
|
|
@@ -246,17 +312,18 @@ class TurnkeyClient {
|
|
|
246
312
|
* @param params.expirationSeconds - session expiration time in seconds (defaults to the configured default).
|
|
247
313
|
* @param params.createSubOrgParams - parameters for creating a sub-organization (e.g., authenticators, user metadata).
|
|
248
314
|
* @param params.sessionKey - session key to use for storing the session (defaults to the default session key).
|
|
249
|
-
* @param params.organizationId - organization ID to target (defaults to the session's organization ID or the parent organization ID).
|
|
250
315
|
* @returns A promise that resolves to a {@link PasskeyAuthResult}, which includes:
|
|
251
316
|
* - `sessionToken`: the signed JWT session token.
|
|
252
317
|
* - `credentialId`: the credential ID associated with the passkey created.
|
|
253
318
|
* @throws {TurnkeyError} If there is an error during passkey creation, sub-organization creation, or session storage.
|
|
254
319
|
*/
|
|
255
320
|
this.signUpWithPasskey = async (params) => {
|
|
256
|
-
const { passkeyDisplayName, challenge, expirationSeconds = DEFAULT_SESSION_EXPIRATION_IN_SECONDS, createSubOrgParams, sessionKey = SessionKey.DefaultSessionkey,
|
|
257
|
-
|
|
321
|
+
const { passkeyDisplayName, challenge, expirationSeconds = DEFAULT_SESSION_EXPIRATION_IN_SECONDS, createSubOrgParams, sessionKey = SessionKey.DefaultSessionkey, } = params || {};
|
|
322
|
+
const generatedPublicKey = await this.createApiKeyPair();
|
|
258
323
|
return withTurnkeyErrorHandling(async () => {
|
|
259
|
-
|
|
324
|
+
if (!this.passkeyStamper) {
|
|
325
|
+
throw new TurnkeyError("Passkey stamper is not initialized", TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
326
|
+
}
|
|
260
327
|
const passkeyName = passkeyDisplayName || `passkey-${Date.now()}`;
|
|
261
328
|
// A passkey will be created automatically when you call this function. The name is passed in
|
|
262
329
|
const passkey = await this.createPasskey({
|
|
@@ -288,25 +355,23 @@ class TurnkeyClient {
|
|
|
288
355
|
],
|
|
289
356
|
},
|
|
290
357
|
});
|
|
291
|
-
const res = await this.httpClient.
|
|
358
|
+
const res = await this.httpClient.proxySignupV2(signUpBody);
|
|
292
359
|
if (!res) {
|
|
293
360
|
throw new TurnkeyError(`Sign up failed`, TurnkeyErrorCodes.PASSKEY_SIGNUP_AUTH_ERROR);
|
|
294
361
|
}
|
|
295
|
-
const newGeneratedKeyPair = await this.
|
|
296
|
-
this.
|
|
362
|
+
const newGeneratedKeyPair = await this.createApiKeyPair();
|
|
363
|
+
await this.overrideApiKeyStamper({
|
|
364
|
+
temporaryPublicKey: generatedPublicKey,
|
|
365
|
+
});
|
|
297
366
|
const sessionResponse = await this.httpClient.stampLogin({
|
|
298
367
|
publicKey: newGeneratedKeyPair,
|
|
299
|
-
organizationId:
|
|
368
|
+
organizationId: res.organizationId,
|
|
300
369
|
expirationSeconds,
|
|
301
370
|
});
|
|
302
|
-
await
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
sessionKey,
|
|
307
|
-
}),
|
|
308
|
-
]);
|
|
309
|
-
generatedPublicKey = undefined; // Key pair was successfully used, set to null to prevent cleanup
|
|
371
|
+
await this.storeSession({
|
|
372
|
+
sessionToken: sessionResponse.session,
|
|
373
|
+
sessionKey,
|
|
374
|
+
});
|
|
310
375
|
return {
|
|
311
376
|
sessionToken: sessionResponse.session,
|
|
312
377
|
appProofs: res.appProofs,
|
|
@@ -317,14 +382,12 @@ class TurnkeyClient {
|
|
|
317
382
|
errorMessage: "Failed to sign up with passkey",
|
|
318
383
|
}, {
|
|
319
384
|
finallyFn: async () => {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
throw new TurnkeyError(`Failed to clean up generated key pair`, TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
327
|
-
}
|
|
385
|
+
try {
|
|
386
|
+
await this.overrideApiKeyStamper({ temporaryPublicKey: "" });
|
|
387
|
+
await this.deleteApiKeyPair({ publicKey: generatedPublicKey });
|
|
388
|
+
}
|
|
389
|
+
catch (cleanupError) {
|
|
390
|
+
throw new TurnkeyError(`Failed to clean up generated key pair`, TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
328
391
|
}
|
|
329
392
|
},
|
|
330
393
|
});
|
|
@@ -464,13 +527,11 @@ class TurnkeyClient {
|
|
|
464
527
|
this.buildWalletLoginRequest = async (params) => {
|
|
465
528
|
const { walletProvider, publicKey: providedPublicKey } = params;
|
|
466
529
|
const expirationSeconds = params.expirationSeconds || DEFAULT_SESSION_EXPIRATION_IN_SECONDS;
|
|
467
|
-
|
|
530
|
+
const futureSessionPublicKey = providedPublicKey ?? (await this.createApiKeyPair());
|
|
468
531
|
return withTurnkeyErrorHandling(async () => {
|
|
469
532
|
if (!this.walletManager?.stamper) {
|
|
470
533
|
throw new TurnkeyError("Wallet stamper is not initialized", TurnkeyErrorCodes.WALLET_MANAGER_COMPONENT_NOT_INITIALIZED);
|
|
471
534
|
}
|
|
472
|
-
const futureSessionPublicKey = providedPublicKey ??
|
|
473
|
-
(generatedPublicKey = await this.apiKeyStamper?.createKeyPair());
|
|
474
535
|
if (!futureSessionPublicKey) {
|
|
475
536
|
throw new TurnkeyError("Failed to find or generate a public key for building the wallet login request", TurnkeyErrorCodes.WALLET_BUILD_LOGIN_REQUEST_ERROR);
|
|
476
537
|
}
|
|
@@ -512,9 +573,12 @@ class TurnkeyClient {
|
|
|
512
573
|
errorCode: TurnkeyErrorCodes.WALLET_BUILD_LOGIN_REQUEST_ERROR,
|
|
513
574
|
errorMessage: "Failed to build wallet login request",
|
|
514
575
|
catchFn: async () => {
|
|
515
|
-
if
|
|
576
|
+
// we only clean up the generated key pair if we created it here
|
|
577
|
+
if (!providedPublicKey) {
|
|
516
578
|
try {
|
|
517
|
-
await this.
|
|
579
|
+
await this.deleteApiKeyPair({
|
|
580
|
+
publicKey: futureSessionPublicKey,
|
|
581
|
+
});
|
|
518
582
|
}
|
|
519
583
|
catch (cleanupError) {
|
|
520
584
|
throw new TurnkeyError(`Failed to clean up generated key pair`, TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
@@ -599,7 +663,7 @@ class TurnkeyClient {
|
|
|
599
663
|
],
|
|
600
664
|
},
|
|
601
665
|
});
|
|
602
|
-
const res = await this.httpClient.
|
|
666
|
+
const res = await this.httpClient.proxySignupV2(signUpBody);
|
|
603
667
|
if (!res) {
|
|
604
668
|
throw new TurnkeyError(`Sign up failed`, TurnkeyErrorCodes.WALLET_SIGNUP_AUTH_ERROR);
|
|
605
669
|
}
|
|
@@ -674,7 +738,7 @@ class TurnkeyClient {
|
|
|
674
738
|
],
|
|
675
739
|
},
|
|
676
740
|
});
|
|
677
|
-
signupRes = await this.httpClient.
|
|
741
|
+
signupRes = await this.httpClient.proxySignupV2(signUpBody);
|
|
678
742
|
if (!signupRes) {
|
|
679
743
|
throw new TurnkeyError(`Sign up failed`, TurnkeyErrorCodes.WALLET_SIGNUP_AUTH_ERROR);
|
|
680
744
|
}
|
|
@@ -706,21 +770,29 @@ class TurnkeyClient {
|
|
|
706
770
|
*
|
|
707
771
|
* - 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.
|
|
708
772
|
* - Supports both email and SMS OTP types.
|
|
709
|
-
* - Returns an OTP ID
|
|
773
|
+
* - Returns an OTP ID and an encryption target bundle required for subsequent OTP verification.
|
|
774
|
+
* - The `otpEncryptionTargetBundle` must be used to encrypt the OTP code and a client-generated public key before calling `verifyOtp`.
|
|
710
775
|
*
|
|
711
776
|
* @param params.otpType - type of OTP to initialize (OtpType.Email or OtpType.Sms).
|
|
712
777
|
* @param params.contact - contact information for the user (e.g., email address or phone number).
|
|
713
778
|
* @param params.organizationId - optional organization ID to target (defaults to the session's organization ID or the parent organization ID).
|
|
714
|
-
* @returns A promise that resolves to
|
|
779
|
+
* @returns A promise that resolves to an {@link InitOtpResult}, which includes:
|
|
780
|
+
* - `otpId`: the ID of the initiated OTP.
|
|
781
|
+
* - `otpEncryptionTargetBundle`: the signed encryption target bundle for encrypting the OTP code.
|
|
715
782
|
* @throws {TurnkeyError} If there is an error during the OTP initialization process or if the maximum number of OTPs has been reached.
|
|
716
783
|
*/
|
|
717
784
|
this.initOtp = async (params) => {
|
|
718
785
|
return withTurnkeyErrorHandling(async () => {
|
|
719
|
-
const initOtpRes = await this.httpClient.
|
|
720
|
-
if (!initOtpRes ||
|
|
721
|
-
|
|
786
|
+
const initOtpRes = await this.httpClient.proxyInitOtpV2(params);
|
|
787
|
+
if (!initOtpRes ||
|
|
788
|
+
!initOtpRes.otpId ||
|
|
789
|
+
!initOtpRes.otpEncryptionTargetBundle) {
|
|
790
|
+
throw new TurnkeyError("Failed to initialize OTP: otpId or otpEncryptionTargetBundle is missing", TurnkeyErrorCodes.INIT_OTP_ERROR);
|
|
722
791
|
}
|
|
723
|
-
return
|
|
792
|
+
return {
|
|
793
|
+
otpId: initOtpRes.otpId,
|
|
794
|
+
otpEncryptionTargetBundle: initOtpRes.otpEncryptionTargetBundle,
|
|
795
|
+
};
|
|
724
796
|
}, {
|
|
725
797
|
errorMessage: "Failed to initialize OTP",
|
|
726
798
|
errorCode: TurnkeyErrorCodes.INIT_OTP_ERROR,
|
|
@@ -735,45 +807,38 @@ class TurnkeyClient {
|
|
|
735
807
|
/**
|
|
736
808
|
* Verifies the OTP code sent to the user.
|
|
737
809
|
*
|
|
738
|
-
* -
|
|
739
|
-
* - If verification is successful, it returns
|
|
810
|
+
* - Under the hood, the OTP code and an ephemeral client public key are encrypted to the enclave's target key (from `initOtp`) before being sent for verification.
|
|
811
|
+
* - If verification is successful, it returns a verification token bound to the public key.
|
|
740
812
|
* - The verification token can be used for subsequent login or sign-up flows.
|
|
741
|
-
* - Handles both email and SMS OTP types.
|
|
742
813
|
*
|
|
743
814
|
* @param params.otpId - ID of the OTP to verify (returned from `initOtp`).
|
|
744
|
-
* @param params.otpCode - OTP code entered by the user.
|
|
745
|
-
* @param params.
|
|
746
|
-
* @param params.
|
|
747
|
-
* @param params.publicKey - public key the verification token is bound to for ownership verification (client signature verification during login/signup). This public key is optional; if not provided, a new key pair will be generated.
|
|
815
|
+
* @param params.otpCode - the OTP code entered by the user.
|
|
816
|
+
* @param params.otpEncryptionTargetBundle - the encryption target bundle returned from `initOtp`.
|
|
817
|
+
* @param params.publicKey - optional public key to bind to the verification token. If not provided, a new key pair will be generated via the configured `apiKeyStamper`.
|
|
748
818
|
* @returns A promise that resolves to an object containing:
|
|
749
|
-
* - subOrganizationId: sub-organization ID if the contact is already associated with a sub-organization, or an empty string if not.
|
|
750
819
|
* - verificationToken: verification token to be used for login or sign-up.
|
|
820
|
+
* - publicKey: the public key bound to the verification token (either the one provided or the auto-generated one).
|
|
751
821
|
* @throws {TurnkeyError} If there is an error during the OTP verification process, such as an invalid code or network failure.
|
|
752
822
|
*/
|
|
753
823
|
this.verifyOtp = async (params) => {
|
|
754
|
-
const { otpId, otpCode,
|
|
755
|
-
|
|
824
|
+
const { otpId, otpCode, otpEncryptionTargetBundle } = params;
|
|
825
|
+
// we track auto-generated keys so we can clean them up on errors
|
|
826
|
+
const publicKey = params.publicKey ?? (await this.createApiKeyPair());
|
|
827
|
+
if (!publicKey) {
|
|
828
|
+
throw new TurnkeyError("No public key available. Either pass a publicKey or ensure apiKeyStamper is configured.", TurnkeyErrorCodes.INVALID_REQUEST);
|
|
829
|
+
}
|
|
756
830
|
return withTurnkeyErrorHandling(async () => {
|
|
757
|
-
const
|
|
831
|
+
const encryptedOtpBundle = await encryptOtpCodeToBundle(otpCode, otpEncryptionTargetBundle, publicKey);
|
|
832
|
+
const verifyOtpRes = await this.httpClient.proxyVerifyOtpV2({
|
|
758
833
|
otpId: otpId,
|
|
759
|
-
|
|
760
|
-
publicKey: resolvedPublicKey,
|
|
834
|
+
encryptedOtpBundle: encryptedOtpBundle,
|
|
761
835
|
});
|
|
762
836
|
if (!verifyOtpRes) {
|
|
763
837
|
throw new TurnkeyError(`OTP verification failed`, TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
764
838
|
}
|
|
765
|
-
const accountRes = await this.httpClient.proxyGetAccount({
|
|
766
|
-
filterType: OtpTypeToFilterTypeMap[otpType],
|
|
767
|
-
filterValue: contact,
|
|
768
|
-
verificationToken: verifyOtpRes.verificationToken,
|
|
769
|
-
});
|
|
770
|
-
if (!accountRes) {
|
|
771
|
-
throw new TurnkeyError(`Account fetch failed`, TurnkeyErrorCodes.ACCOUNT_FETCH_ERROR);
|
|
772
|
-
}
|
|
773
|
-
const subOrganizationId = accountRes.organizationId;
|
|
774
839
|
return {
|
|
775
|
-
subOrganizationId: subOrganizationId,
|
|
776
840
|
verificationToken: verifyOtpRes.verificationToken,
|
|
841
|
+
publicKey,
|
|
777
842
|
};
|
|
778
843
|
}, {
|
|
779
844
|
errorMessage: "Failed to verify OTP",
|
|
@@ -784,19 +849,29 @@ class TurnkeyClient {
|
|
|
784
849
|
code: TurnkeyErrorCodes.INVALID_OTP_CODE,
|
|
785
850
|
},
|
|
786
851
|
},
|
|
852
|
+
catchFn: async () => {
|
|
853
|
+
// we only clean up the generated key pair if we created it here
|
|
854
|
+
if (!params.publicKey) {
|
|
855
|
+
try {
|
|
856
|
+
await this.deleteApiKeyPair({ publicKey });
|
|
857
|
+
}
|
|
858
|
+
catch (cleanupError) {
|
|
859
|
+
throw new TurnkeyError(`Failed to clean up generated key pair`, TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
},
|
|
787
863
|
});
|
|
788
864
|
};
|
|
789
865
|
/**
|
|
790
866
|
* Logs in a user using an OTP verification token.
|
|
791
867
|
*
|
|
792
868
|
* - This function logs in a user using the verification token received after OTP verification (from email or SMS).
|
|
793
|
-
* -
|
|
869
|
+
* - The client signature is always produced using the private key bound to the verification token during `verifyOtp`.
|
|
870
|
+
* - The verification token's embedded key becomes the session public key.
|
|
794
871
|
* - Optionally invalidates any existing sessions for the user if `invalidateExisting` is set to true.
|
|
795
872
|
* - Stores the resulting session token under the specified session key, or the default session key if not provided.
|
|
796
|
-
* - Handles cleanup of unused key pairs if login fails.
|
|
797
873
|
*
|
|
798
874
|
* @param params.verificationToken - verification token received after OTP verification.
|
|
799
|
-
* @param params.publicKey - public key to use for authentication. If not provided, a new key pair will be generated.
|
|
800
875
|
* @param params.organizationId - optional organization ID to target (defaults to the verified subOrg ID linked to the verification token contact).
|
|
801
876
|
* @param params.invalidateExisting - flag to invalidate existing session for the user.
|
|
802
877
|
* @param params.sessionKey - session key to use for session creation (defaults to the default session key).
|
|
@@ -805,34 +880,35 @@ class TurnkeyClient {
|
|
|
805
880
|
* @throws {TurnkeyError} If there is an error during the OTP login process or if key pair cleanup fails.
|
|
806
881
|
*/
|
|
807
882
|
this.loginWithOtp = async (params) => {
|
|
808
|
-
const { verificationToken, invalidateExisting = false,
|
|
883
|
+
const { verificationToken, invalidateExisting = false, organizationId, sessionKey = SessionKey.DefaultSessionkey, } = params;
|
|
809
884
|
return withTurnkeyErrorHandling(async () => {
|
|
810
|
-
const { message, publicKey:
|
|
811
|
-
|
|
812
|
-
|
|
885
|
+
const { message, publicKey: verificationPublicKey } = getClientSignatureMessageForLogin({ verificationToken });
|
|
886
|
+
// we sign with the verification token key. This is the key bound during
|
|
887
|
+
// verifyOtp() and is what Turnkey expects to sign the client signature for login
|
|
888
|
+
const signature = await this.signWithApiKey({
|
|
889
|
+
message,
|
|
890
|
+
publicKey: verificationPublicKey,
|
|
813
891
|
});
|
|
814
|
-
this.apiKeyStamper?.setTemporaryPublicKey(publicKey);
|
|
815
|
-
const signature = await this.apiKeyStamper?.sign(message, SignatureFormat.Raw);
|
|
816
892
|
if (!signature) {
|
|
817
893
|
throw new TurnkeyError(`Failed to sign client signature for OTP login`, TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
818
894
|
}
|
|
819
895
|
const clientSignature = {
|
|
820
896
|
message: message,
|
|
821
|
-
publicKey:
|
|
897
|
+
publicKey: verificationPublicKey,
|
|
822
898
|
scheme: "CLIENT_SIGNATURE_SCHEME_API_P256",
|
|
823
899
|
signature: signature,
|
|
824
900
|
};
|
|
825
|
-
|
|
901
|
+
// the verification token's public key is also used as the session public key for login
|
|
902
|
+
const loginRes = await this.httpClient.proxyOtpLoginV2({
|
|
826
903
|
verificationToken,
|
|
827
|
-
publicKey:
|
|
828
|
-
invalidateExisting,
|
|
904
|
+
publicKey: verificationPublicKey,
|
|
829
905
|
clientSignature,
|
|
906
|
+
invalidateExisting,
|
|
830
907
|
...(organizationId && { organizationId }),
|
|
831
908
|
});
|
|
832
|
-
if (!
|
|
909
|
+
if (!loginRes) {
|
|
833
910
|
throw new TurnkeyError(`Auth proxy OTP login failed`, TurnkeyErrorCodes.OTP_LOGIN_ERROR);
|
|
834
911
|
}
|
|
835
|
-
const loginRes = await res;
|
|
836
912
|
if (!loginRes.session) {
|
|
837
913
|
throw new TurnkeyError("No session returned from OTP login", TurnkeyErrorCodes.OTP_LOGIN_ERROR);
|
|
838
914
|
}
|
|
@@ -846,21 +922,6 @@ class TurnkeyClient {
|
|
|
846
922
|
}, {
|
|
847
923
|
errorMessage: "Failed to log in with OTP",
|
|
848
924
|
errorCode: TurnkeyErrorCodes.OTP_LOGIN_ERROR,
|
|
849
|
-
catchFn: async () => {
|
|
850
|
-
// Clean up the generated key pair if it wasn't successfully used
|
|
851
|
-
if (publicKey) {
|
|
852
|
-
try {
|
|
853
|
-
await this.apiKeyStamper?.deleteKeyPair(publicKey);
|
|
854
|
-
}
|
|
855
|
-
catch (cleanupError) {
|
|
856
|
-
throw new TurnkeyError(`Failed to clean up generated key pair`, TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
},
|
|
860
|
-
}, {
|
|
861
|
-
finallyFn: async () => {
|
|
862
|
-
this.apiKeyStamper?.clearTemporaryPublicKey();
|
|
863
|
-
},
|
|
864
925
|
});
|
|
865
926
|
};
|
|
866
927
|
/**
|
|
@@ -868,7 +929,7 @@ class TurnkeyClient {
|
|
|
868
929
|
*
|
|
869
930
|
* - This function signs up a user using the verification token received after OTP verification (from email or SMS).
|
|
870
931
|
* - Creates a new sub-organization for the user with the provided parameters and associates the contact (email or phone) with the sub-organization.
|
|
871
|
-
* -
|
|
932
|
+
* - The verification token's embedded key becomes the session public key.
|
|
872
933
|
* - Stores the resulting session token under the specified session key, or the default session key if not provided.
|
|
873
934
|
* - Handles both email and SMS OTP types, and supports additional sub-organization creation parameters.
|
|
874
935
|
*
|
|
@@ -883,7 +944,7 @@ class TurnkeyClient {
|
|
|
883
944
|
* @throws {TurnkeyError} If there is an error during the OTP sign-up process or session storage.
|
|
884
945
|
*/
|
|
885
946
|
this.signUpWithOtp = async (params) => {
|
|
886
|
-
const { verificationToken, contact, otpType, createSubOrgParams, invalidateExisting, sessionKey,
|
|
947
|
+
const { verificationToken, contact, otpType, createSubOrgParams, invalidateExisting, sessionKey, } = params;
|
|
887
948
|
// build sign up body without client signature first
|
|
888
949
|
const signUpBody = buildSignUpBody({
|
|
889
950
|
createSubOrgParams: {
|
|
@@ -895,7 +956,7 @@ class TurnkeyClient {
|
|
|
895
956
|
},
|
|
896
957
|
});
|
|
897
958
|
return withTurnkeyErrorHandling(async () => {
|
|
898
|
-
const { message, publicKey:
|
|
959
|
+
const { message, publicKey: verificationPublicKey } = getClientSignatureMessageForSignup({
|
|
899
960
|
verificationToken,
|
|
900
961
|
...(signUpBody.userEmail && { email: signUpBody.userEmail }),
|
|
901
962
|
...(signUpBody.userPhoneNumber && {
|
|
@@ -905,18 +966,22 @@ class TurnkeyClient {
|
|
|
905
966
|
authenticators: signUpBody.authenticators,
|
|
906
967
|
oauthProviders: signUpBody.oauthProviders,
|
|
907
968
|
});
|
|
908
|
-
|
|
909
|
-
|
|
969
|
+
// we sign with the verification token key. This is the key bound during
|
|
970
|
+
// verifyOtp() and is what Turnkey expects to sign the client signature for signup
|
|
971
|
+
const signature = await this.signWithApiKey({
|
|
972
|
+
message,
|
|
973
|
+
publicKey: verificationPublicKey,
|
|
974
|
+
});
|
|
910
975
|
if (!signature) {
|
|
911
976
|
throw new TurnkeyError(`Failed to sign client signature for OTP sign up`, TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
912
977
|
}
|
|
913
978
|
const clientSignature = {
|
|
914
979
|
message: message,
|
|
915
|
-
publicKey:
|
|
980
|
+
publicKey: verificationPublicKey,
|
|
916
981
|
scheme: "CLIENT_SIGNATURE_SCHEME_API_P256",
|
|
917
982
|
signature: signature,
|
|
918
983
|
};
|
|
919
|
-
const signupRes = await this.httpClient.
|
|
984
|
+
const signupRes = await this.httpClient.proxySignupV2({
|
|
920
985
|
...signUpBody,
|
|
921
986
|
clientSignature,
|
|
922
987
|
});
|
|
@@ -925,7 +990,6 @@ class TurnkeyClient {
|
|
|
925
990
|
}
|
|
926
991
|
const otpRes = await this.loginWithOtp({
|
|
927
992
|
verificationToken,
|
|
928
|
-
publicKey: publicKey,
|
|
929
993
|
...(invalidateExisting && { invalidateExisting }),
|
|
930
994
|
...(sessionKey && { sessionKey }),
|
|
931
995
|
});
|
|
@@ -934,39 +998,26 @@ class TurnkeyClient {
|
|
|
934
998
|
appProofs: signupRes.appProofs,
|
|
935
999
|
};
|
|
936
1000
|
}, {
|
|
937
|
-
catchFn: async () => {
|
|
938
|
-
// Clean up the generated key pair if it wasn't successfully used
|
|
939
|
-
if (publicKey) {
|
|
940
|
-
try {
|
|
941
|
-
await this.apiKeyStamper?.deleteKeyPair(publicKey);
|
|
942
|
-
}
|
|
943
|
-
catch (cleanupError) {
|
|
944
|
-
throw new TurnkeyError(`Failed to clean up generated key pair`, TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
},
|
|
948
1001
|
errorCode: TurnkeyErrorCodes.OTP_SIGNUP_ERROR,
|
|
949
1002
|
errorMessage: "Failed to sign up with OTP",
|
|
950
|
-
}, {
|
|
951
|
-
finallyFn: async () => {
|
|
952
|
-
this.apiKeyStamper?.clearTemporaryPublicKey();
|
|
953
|
-
},
|
|
954
1003
|
});
|
|
955
1004
|
};
|
|
956
1005
|
/**
|
|
957
|
-
* Completes the OTP authentication flow by verifying the OTP
|
|
1006
|
+
* Completes the OTP authentication flow by verifying the encrypted OTP bundle and then either signing up or logging in the user.
|
|
958
1007
|
*
|
|
959
|
-
* - This function
|
|
1008
|
+
* - This function encrypts the OTP code and the session public key to the enclave's target key, verifies the OTP, and then either signs up or logs in the user.
|
|
960
1009
|
* - 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.
|
|
961
1010
|
* - If the contact is already associated with a sub-organization, it will complete the login flow.
|
|
962
|
-
* -
|
|
1011
|
+
* - The key bound to the verification token during `verifyOtp` is always reused as the session public key.
|
|
1012
|
+
* - Supports passing a custom public key for binding into the verification token, invalidating existing session, specifying a session key, and providing additional sub-organization creation parameters.
|
|
963
1013
|
* - Handles both email and SMS OTP types.
|
|
964
1014
|
*
|
|
965
1015
|
* @param params.otpId - ID of the OTP to complete (returned from `initOtp`).
|
|
966
|
-
* @param params.otpCode - OTP code entered by the user.
|
|
1016
|
+
* @param params.otpCode - the OTP code entered by the user.
|
|
1017
|
+
* @param params.otpEncryptionTargetBundle - the encryption target bundle returned from `initOtp`.
|
|
967
1018
|
* @param params.contact - contact information for the user (e.g., email address or phone number).
|
|
968
1019
|
* @param params.otpType - type of OTP being completed (OtpType.Email or OtpType.Sms).
|
|
969
|
-
* @param params.publicKey - public key to
|
|
1020
|
+
* @param params.publicKey - public key to bind to the verification token via `verifyOtp`. If not provided, a new key pair will be generated. This key becomes the session public key.
|
|
970
1021
|
* @param params.invalidateExisting - flag to invalidate existing sessions for the user.
|
|
971
1022
|
* @param params.sessionKey - session key to use for session creation (defaults to the default session key).
|
|
972
1023
|
* @param params.createSubOrgParams - parameters for sub-organization creation (e.g., authenticators, user metadata).
|
|
@@ -977,18 +1028,30 @@ class TurnkeyClient {
|
|
|
977
1028
|
* @throws {TurnkeyError} If there is an error during OTP verification, sign-up, or login.
|
|
978
1029
|
*/
|
|
979
1030
|
this.completeOtp = async (params) => {
|
|
980
|
-
const { otpId, otpCode, contact, otpType,
|
|
1031
|
+
const { otpId, otpCode, otpEncryptionTargetBundle, contact, otpType, invalidateExisting = false, sessionKey, createSubOrgParams, } = params;
|
|
1032
|
+
const publicKey = params.publicKey ?? (await this.createApiKeyPair());
|
|
1033
|
+
if (!publicKey) {
|
|
1034
|
+
throw new TurnkeyError("No public key available. Either pass a publicKey or ensure apiKeyStamper is configured.", TurnkeyErrorCodes.INVALID_REQUEST);
|
|
1035
|
+
}
|
|
981
1036
|
return withTurnkeyErrorHandling(async () => {
|
|
982
|
-
const {
|
|
983
|
-
otpId
|
|
984
|
-
otpCode
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
publicKey: publicKey,
|
|
1037
|
+
const { verificationToken } = await this.verifyOtp({
|
|
1038
|
+
otpId,
|
|
1039
|
+
otpCode,
|
|
1040
|
+
otpEncryptionTargetBundle,
|
|
1041
|
+
publicKey,
|
|
988
1042
|
});
|
|
989
1043
|
if (!verificationToken) {
|
|
990
1044
|
throw new TurnkeyError("No verification token returned from OTP verification", TurnkeyErrorCodes.VERIFY_OTP_ERROR);
|
|
991
1045
|
}
|
|
1046
|
+
const accountRes = await this.httpClient.proxyGetAccount({
|
|
1047
|
+
filterType: OtpTypeToFilterTypeMap[otpType],
|
|
1048
|
+
filterValue: contact,
|
|
1049
|
+
verificationToken: verificationToken,
|
|
1050
|
+
});
|
|
1051
|
+
if (!accountRes) {
|
|
1052
|
+
throw new TurnkeyError(`Account fetch failed`, TurnkeyErrorCodes.ACCOUNT_FETCH_ERROR);
|
|
1053
|
+
}
|
|
1054
|
+
const subOrganizationId = accountRes.organizationId;
|
|
992
1055
|
if (!subOrganizationId) {
|
|
993
1056
|
const signUpRes = await this.signUpWithOtp({
|
|
994
1057
|
verificationToken,
|
|
@@ -997,7 +1060,6 @@ class TurnkeyClient {
|
|
|
997
1060
|
...(createSubOrgParams && { createSubOrgParams }),
|
|
998
1061
|
...(invalidateExisting && { invalidateExisting }),
|
|
999
1062
|
...(sessionKey && { sessionKey }),
|
|
1000
|
-
publicKey: publicKey,
|
|
1001
1063
|
});
|
|
1002
1064
|
return {
|
|
1003
1065
|
...signUpRes,
|
|
@@ -1008,7 +1070,6 @@ class TurnkeyClient {
|
|
|
1008
1070
|
else {
|
|
1009
1071
|
const loginRes = await this.loginWithOtp({
|
|
1010
1072
|
verificationToken,
|
|
1011
|
-
publicKey: publicKey,
|
|
1012
1073
|
...(invalidateExisting && { invalidateExisting }),
|
|
1013
1074
|
...(sessionKey && { sessionKey }),
|
|
1014
1075
|
});
|
|
@@ -1021,6 +1082,16 @@ class TurnkeyClient {
|
|
|
1021
1082
|
}, {
|
|
1022
1083
|
errorMessage: "Failed to complete OTP process",
|
|
1023
1084
|
errorCode: TurnkeyErrorCodes.OTP_COMPLETION_ERROR,
|
|
1085
|
+
catchFn: async () => {
|
|
1086
|
+
if (!params.publicKey) {
|
|
1087
|
+
try {
|
|
1088
|
+
await this.deleteApiKeyPair({ publicKey });
|
|
1089
|
+
}
|
|
1090
|
+
catch (cleanupError) {
|
|
1091
|
+
throw new TurnkeyError(`Failed to clean up generated key pair`, TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
},
|
|
1024
1095
|
});
|
|
1025
1096
|
};
|
|
1026
1097
|
/**
|
|
@@ -1145,7 +1216,7 @@ class TurnkeyClient {
|
|
|
1145
1216
|
// Clean up the generated key pair if it wasn't successfully used
|
|
1146
1217
|
if (publicKey) {
|
|
1147
1218
|
try {
|
|
1148
|
-
await this.
|
|
1219
|
+
await this.deleteApiKeyPair({ publicKey });
|
|
1149
1220
|
}
|
|
1150
1221
|
catch (cleanupError) {
|
|
1151
1222
|
throw new TurnkeyError(`Failed to clean up generated key pair`, TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
@@ -1182,10 +1253,11 @@ class TurnkeyClient {
|
|
|
1182
1253
|
providerName,
|
|
1183
1254
|
oidcToken,
|
|
1184
1255
|
},
|
|
1256
|
+
...(createSubOrgParams?.oauthProviders ?? []),
|
|
1185
1257
|
],
|
|
1186
1258
|
},
|
|
1187
1259
|
});
|
|
1188
|
-
const signupRes = await this.httpClient.
|
|
1260
|
+
const signupRes = await this.httpClient.proxySignupV2(signUpBody);
|
|
1189
1261
|
if (!signupRes) {
|
|
1190
1262
|
throw new TurnkeyError(`Auth proxy OAuth signup failed`, TurnkeyErrorCodes.OAUTH_SIGNUP_ERROR);
|
|
1191
1263
|
}
|
|
@@ -2347,37 +2419,71 @@ class TurnkeyClient {
|
|
|
2347
2419
|
});
|
|
2348
2420
|
};
|
|
2349
2421
|
/**
|
|
2350
|
-
* Adds
|
|
2422
|
+
* Adds one or more OAuth provider audiences to the user.
|
|
2351
2423
|
*
|
|
2352
|
-
* -
|
|
2353
|
-
* -
|
|
2354
|
-
* -
|
|
2355
|
-
* - If the user's email is not set or not verified, attempts to update and verify the email using the email from the
|
|
2356
|
-
* -
|
|
2424
|
+
* - Accepts either an `oidcToken`, a list of `oidcClaims`, or both. At least one must be provided.
|
|
2425
|
+
* - The issuer (`iss`) of the `oidcToken` and all `oidcClaims` must match, ensuring they belong to the same OAuth provider.
|
|
2426
|
+
* - Checks for an existing account for the `oidcToken` (if provided) and for each `oidcClaims` entry, and throws if any of them are already associated with a different sub-organization.
|
|
2427
|
+
* - If the user's email is not set or not verified and an `oidcToken` is provided, attempts to update and verify the email using the email from the token.
|
|
2428
|
+
* - Submits a single `createOauthProviders` request containing the token (if any) and all additional claims.
|
|
2357
2429
|
* - Optionally allows stamping the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
2358
2430
|
*
|
|
2359
2431
|
* @param params.providerName - name of the OAuth provider to add (e.g., "Google", "Apple").
|
|
2360
|
-
* @param params.oidcToken - OIDC token for the OAuth provider.
|
|
2432
|
+
* @param params.oidcToken - OIDC token for the OAuth provider. Either `oidcToken` or `oidcClaims` (or both) must be provided.
|
|
2433
|
+
* @param params.oidcClaims - additional `iss`/`sub`/`aud` claims to register as additional audiences for the same identity.
|
|
2361
2434
|
* @param params.organizationId - organization ID to specify the sub-organization (defaults to the current session's organizationId).
|
|
2362
2435
|
* @param params.userId - user ID to add the provider for a specific user (defaults to current session's userId).
|
|
2363
2436
|
* @param params.stampWith - parameter to stamp the request with a specific stamper (StamperType.Passkey, StamperType.ApiKey, or StamperType.Wallet).
|
|
2364
2437
|
* @returns A promise that resolves to an array of provider IDs associated with the user.
|
|
2365
|
-
* @throws {TurnkeyError} If there is no active session, if the
|
|
2438
|
+
* @throws {TurnkeyError} If there is no active session, if neither `oidcToken` nor `oidcClaims` was provided, if the issuer claims do not all match, if any of the audiences are already associated with another account, or if there is an error adding the OAuth provider.
|
|
2366
2439
|
*/
|
|
2367
2440
|
this.addOauthProvider = async (params) => {
|
|
2368
|
-
const { providerName, oidcToken, stampWith = this.config.defaultStamperType, } = params;
|
|
2441
|
+
const { providerName, oidcToken, oidcClaims = [], stampWith = this.config.defaultStamperType, } = params;
|
|
2442
|
+
if (!oidcToken && oidcClaims.length === 0) {
|
|
2443
|
+
throw new TurnkeyError("At least one of `oidcToken` or `oidcClaims` must be provided to add an OAuth provider", TurnkeyErrorCodes.INVALID_REQUEST);
|
|
2444
|
+
}
|
|
2369
2445
|
const session = await getActiveSessionOrThrowIfRequired(stampWith, this.storageManager.getActiveSession);
|
|
2370
2446
|
return withTurnkeyErrorHandling(async () => {
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
if (
|
|
2379
|
-
|
|
2380
|
-
|
|
2447
|
+
// Parse the oidc token so we can get the email/iss. The email is used to update the user's email for social linking.
|
|
2448
|
+
// The iss is used to ensure all passed-in claims share the same issuer.
|
|
2449
|
+
const { email: oidcEmail, iss: tokenIss } = oidcToken
|
|
2450
|
+
? jwtDecode(oidcToken) || {}
|
|
2451
|
+
: { email: undefined, iss: undefined };
|
|
2452
|
+
// Verify that all passed-in claims share the same issuer (and match the token's issuer if a token was provided).
|
|
2453
|
+
const issuers = new Set();
|
|
2454
|
+
if (tokenIss)
|
|
2455
|
+
issuers.add(tokenIss);
|
|
2456
|
+
for (const claim of oidcClaims) {
|
|
2457
|
+
issuers.add(claim.iss);
|
|
2458
|
+
}
|
|
2459
|
+
if (issuers.size > 1) {
|
|
2460
|
+
throw new TurnkeyError("All `oidcToken` and `oidcClaims` entries must share the same issuer (`iss`)", TurnkeyErrorCodes.INVALID_REQUEST);
|
|
2461
|
+
}
|
|
2462
|
+
// Check that none of the audiences are already associated with a different sub-organization.
|
|
2463
|
+
const accountChecks = [];
|
|
2464
|
+
if (oidcToken) {
|
|
2465
|
+
// Look up the sub-organization associated with passed in oidc token
|
|
2466
|
+
accountChecks.push(this.httpClient.proxyGetAccount({
|
|
2467
|
+
filterType: FilterType.OidcToken,
|
|
2468
|
+
filterValue: oidcToken,
|
|
2469
|
+
}));
|
|
2470
|
+
}
|
|
2471
|
+
// Also look up the sub-organization for each oidcClaim
|
|
2472
|
+
for (const claim of oidcClaims) {
|
|
2473
|
+
accountChecks.push(this.httpClient.proxyGetAccount({
|
|
2474
|
+
filterType: FilterType.OidcClaims,
|
|
2475
|
+
filterValue: JSON.stringify(claim),
|
|
2476
|
+
}));
|
|
2477
|
+
}
|
|
2478
|
+
const accountResults = await Promise.all(accountChecks);
|
|
2479
|
+
for (const accountRes of accountResults) {
|
|
2480
|
+
if (!accountRes) {
|
|
2481
|
+
throw new TurnkeyError(`Account fetch failed`, TurnkeyErrorCodes.ACCOUNT_FETCH_ERROR);
|
|
2482
|
+
}
|
|
2483
|
+
if (accountRes.organizationId &&
|
|
2484
|
+
accountRes.organizationId !== session?.organizationId) {
|
|
2485
|
+
throw new TurnkeyError("Account already exists with this OIDC token or claims", TurnkeyErrorCodes.ACCOUNT_ALREADY_EXISTS);
|
|
2486
|
+
}
|
|
2381
2487
|
}
|
|
2382
2488
|
const userId = params?.userId || session?.userId;
|
|
2383
2489
|
if (!userId) {
|
|
@@ -2387,9 +2493,7 @@ class TurnkeyClient {
|
|
|
2387
2493
|
if (!organizationId) {
|
|
2388
2494
|
throw new TurnkeyError("Organization ID is required to add OAuth provider", TurnkeyErrorCodes.INVALID_REQUEST);
|
|
2389
2495
|
}
|
|
2390
|
-
|
|
2391
|
-
const { email: oidcEmail, iss } = jwtDecode(oidcToken) || {};
|
|
2392
|
-
if (iss === googleISS) {
|
|
2496
|
+
if (oidcToken && tokenIss === googleISS) {
|
|
2393
2497
|
const verifiedSuborg = await this.httpClient.proxyGetAccount({
|
|
2394
2498
|
filterType: "EMAIL",
|
|
2395
2499
|
filterValue: oidcEmail,
|
|
@@ -2422,14 +2526,13 @@ class TurnkeyClient {
|
|
|
2422
2526
|
}, stampWith);
|
|
2423
2527
|
}
|
|
2424
2528
|
}
|
|
2529
|
+
const oauthProviders = [
|
|
2530
|
+
...(oidcToken ? [{ providerName, oidcToken }] : []),
|
|
2531
|
+
...oidcClaims.map((claim) => ({ providerName, oidcClaims: claim })),
|
|
2532
|
+
];
|
|
2425
2533
|
const createProviderRes = await this.httpClient.createOauthProviders({
|
|
2426
2534
|
userId,
|
|
2427
|
-
oauthProviders
|
|
2428
|
-
{
|
|
2429
|
-
providerName,
|
|
2430
|
-
oidcToken,
|
|
2431
|
-
},
|
|
2432
|
-
],
|
|
2535
|
+
oauthProviders,
|
|
2433
2536
|
}, stampWith);
|
|
2434
2537
|
if (!createProviderRes) {
|
|
2435
2538
|
throw new TurnkeyError("Failed to create OAuth provider", TurnkeyErrorCodes.ADD_OAUTH_PROVIDER_ERROR);
|
|
@@ -2973,7 +3076,7 @@ class TurnkeyClient {
|
|
|
2973
3076
|
const session = await this.storageManager.getSession(sessionKey);
|
|
2974
3077
|
if (session) {
|
|
2975
3078
|
await Promise.all([
|
|
2976
|
-
this.
|
|
3079
|
+
this.deleteApiKeyPair({ publicKey: session.publicKey }),
|
|
2977
3080
|
this.storageManager.clearSession(sessionKey),
|
|
2978
3081
|
]);
|
|
2979
3082
|
}
|
|
@@ -3041,9 +3144,8 @@ class TurnkeyClient {
|
|
|
3041
3144
|
if (!this.httpClient) {
|
|
3042
3145
|
throw new TurnkeyError("HTTP client is not initialized. Please initialize the client before refreshing the session.", TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
3043
3146
|
}
|
|
3044
|
-
|
|
3147
|
+
const keyPair = publicKey ?? (await this.createApiKeyPair());
|
|
3045
3148
|
return withTurnkeyErrorHandling(async () => {
|
|
3046
|
-
keyPair = publicKey ?? (await this.apiKeyStamper?.createKeyPair());
|
|
3047
3149
|
if (!keyPair) {
|
|
3048
3150
|
throw new TurnkeyError("Failed to create new key pair.", TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
3049
3151
|
}
|
|
@@ -3063,6 +3165,17 @@ class TurnkeyClient {
|
|
|
3063
3165
|
}, {
|
|
3064
3166
|
errorMessage: "Failed to refresh session",
|
|
3065
3167
|
errorCode: TurnkeyErrorCodes.REFRESH_SESSION_ERROR,
|
|
3168
|
+
catchFn: async () => {
|
|
3169
|
+
// we only delete the keypair if we generated it here
|
|
3170
|
+
if (!publicKey) {
|
|
3171
|
+
try {
|
|
3172
|
+
await this.deleteApiKeyPair({ publicKey: keyPair });
|
|
3173
|
+
}
|
|
3174
|
+
catch (cleanupError) {
|
|
3175
|
+
throw new TurnkeyError("Failed to clean up generated key pair", TurnkeyErrorCodes.KEY_PAIR_CLEANUP_ERROR, cleanupError);
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
},
|
|
3066
3179
|
});
|
|
3067
3180
|
};
|
|
3068
3181
|
/**
|
|
@@ -3183,7 +3296,7 @@ class TurnkeyClient {
|
|
|
3183
3296
|
for (const publicKey of publicKeys) {
|
|
3184
3297
|
if (!sessionTokensMap[publicKey]) {
|
|
3185
3298
|
try {
|
|
3186
|
-
await this.
|
|
3299
|
+
await this.deleteApiKeyPair({ publicKey });
|
|
3187
3300
|
}
|
|
3188
3301
|
catch (error) {
|
|
3189
3302
|
throw new TurnkeyError(`Failed to delete unused key pair ${publicKey}`, TurnkeyErrorCodes.INTERNAL_ERROR, error);
|
|
@@ -3198,34 +3311,84 @@ class TurnkeyClient {
|
|
|
3198
3311
|
/**
|
|
3199
3312
|
* Creates a new API key pair and returns the public key.
|
|
3200
3313
|
*
|
|
3201
|
-
* - This function generates a new API key pair and stores it in the underlying key store
|
|
3314
|
+
* - This function generates a new API key pair and stores it in the underlying key store.
|
|
3202
3315
|
* - If an external key pair is provided, it will use that key pair for creation instead of generating a new one.
|
|
3203
|
-
* - 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.
|
|
3204
3316
|
* - Ensures the API key stamper is initialized before proceeding.
|
|
3205
3317
|
* - Handles both native CryptoKeyPair objects and raw key material.
|
|
3206
3318
|
*
|
|
3207
3319
|
* @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.
|
|
3208
|
-
* @param params.storeOverride - If true, sets the generated or provided public key as the override key in the API key stamper (defaults to false).
|
|
3209
3320
|
* @return A promise that resolves to the public key of the created or provided API key pair as a string.
|
|
3210
3321
|
* @throws {TurnkeyError} If the API key stamper is not initialized or if there is an error during key pair creation or storage.
|
|
3211
3322
|
*/
|
|
3212
3323
|
this.createApiKeyPair = async (params) => {
|
|
3213
3324
|
return withTurnkeyErrorHandling(async () => {
|
|
3214
3325
|
const externalKeyPair = params?.externalKeyPair;
|
|
3215
|
-
const storeOverride = params?.storeOverride ?? false;
|
|
3216
3326
|
if (!this.apiKeyStamper) {
|
|
3217
3327
|
throw new TurnkeyError("API Key Stamper is not initialized.", TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
3218
3328
|
}
|
|
3219
3329
|
const publicKey = await this.apiKeyStamper.createKeyPair(externalKeyPair ? externalKeyPair : undefined);
|
|
3220
|
-
if (storeOverride && publicKey) {
|
|
3221
|
-
this.apiKeyStamper.setTemporaryPublicKey(publicKey);
|
|
3222
|
-
}
|
|
3223
3330
|
return publicKey;
|
|
3224
3331
|
}, {
|
|
3225
3332
|
errorMessage: "Failed to create API key pair",
|
|
3226
3333
|
errorCode: TurnkeyErrorCodes.CREATE_API_KEY_PAIR_ERROR,
|
|
3227
3334
|
});
|
|
3228
3335
|
};
|
|
3336
|
+
/**
|
|
3337
|
+
* Deletes an API key pair from persistent storage.
|
|
3338
|
+
*
|
|
3339
|
+
* - This function removes a stored API key pair from the underlying key store (IndexedDB) based on its public key.
|
|
3340
|
+
* - Ensures that the API key stamper is initialized before proceeding with deletion.
|
|
3341
|
+
* - Intended for cleaning up key pairs that are not associated with active sessions, such as after failed authentication attempts where a temporary key pair was created but never used.
|
|
3342
|
+
* - Should NOT be used to delete key pairs that are associated with active sessions; use `clearSession` or `clearAllSessions` instead.
|
|
3343
|
+
*
|
|
3344
|
+
* @param params.publicKey - The public key of the API key pair to delete.
|
|
3345
|
+
* @returns A promise that resolves when the key pair is successfully deleted.
|
|
3346
|
+
* @throws {TurnkeyError} If the API key stamper is not initialized or if there is an error during key pair deletion.
|
|
3347
|
+
*/
|
|
3348
|
+
this.deleteApiKeyPair = async (params) => {
|
|
3349
|
+
const { publicKey } = params;
|
|
3350
|
+
return withTurnkeyErrorHandling(async () => {
|
|
3351
|
+
if (!this.apiKeyStamper) {
|
|
3352
|
+
throw new TurnkeyError("API Key Stamper is not initialized.", TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
3353
|
+
}
|
|
3354
|
+
await this.apiKeyStamper.deleteKeyPair(publicKey);
|
|
3355
|
+
}, {
|
|
3356
|
+
errorMessage: "Failed to delete API key pair",
|
|
3357
|
+
errorCode: TurnkeyErrorCodes.DELETE_API_KEY_PAIR_ERROR,
|
|
3358
|
+
});
|
|
3359
|
+
};
|
|
3360
|
+
/**
|
|
3361
|
+
* Signs a message using a key pair stored in the API key stamper.
|
|
3362
|
+
*
|
|
3363
|
+
* - This function signs the provided message string with the private key identified by `publicKey`.
|
|
3364
|
+
* - The message is SHA-256 hashed internally before signing (ECDSA P-256).
|
|
3365
|
+
* - Returns a compact hex signature (r || s) suitable for use as a client signature.
|
|
3366
|
+
* - The key pair must already exist in the key store (e.g., created via `createApiKeyPair`).
|
|
3367
|
+
*
|
|
3368
|
+
* @param params.message - the message string to sign.
|
|
3369
|
+
* @param params.publicKey - the public key identifying which key pair to sign with.
|
|
3370
|
+
* @returns A promise that resolves to the compact hex signature string.
|
|
3371
|
+
* @throws {TurnkeyError} If the API key stamper is not initialized or the key pair is not found.
|
|
3372
|
+
*/
|
|
3373
|
+
this.signWithApiKey = async (params) => {
|
|
3374
|
+
const { message, publicKey } = params;
|
|
3375
|
+
let previousTemporaryKey;
|
|
3376
|
+
return withTurnkeyErrorHandling(async () => {
|
|
3377
|
+
if (!this.apiKeyStamper) {
|
|
3378
|
+
throw new TurnkeyError("API Key Stamper is not initialized.", TurnkeyErrorCodes.INTERNAL_ERROR);
|
|
3379
|
+
}
|
|
3380
|
+
previousTemporaryKey = this.apiKeyStamper.getTemporaryPublicKey();
|
|
3381
|
+
this.apiKeyStamper.setTemporaryPublicKey(publicKey);
|
|
3382
|
+
return await this.apiKeyStamper.sign(message, SignatureFormat.Raw);
|
|
3383
|
+
}, {
|
|
3384
|
+
errorMessage: "Failed to sign with API key",
|
|
3385
|
+
errorCode: TurnkeyErrorCodes.INTERNAL_ERROR,
|
|
3386
|
+
}, {
|
|
3387
|
+
finallyFn: async () => {
|
|
3388
|
+
this.apiKeyStamper?.setTemporaryPublicKey(previousTemporaryKey);
|
|
3389
|
+
},
|
|
3390
|
+
});
|
|
3391
|
+
};
|
|
3229
3392
|
/**
|
|
3230
3393
|
* Fetches the WalletKit proxy authentication configuration from the auth proxy.
|
|
3231
3394
|
*
|