Skip to content

Commit e0458c8

Browse files
committed
Allow overriding stamper config in core
1 parent d611457 commit e0458c8

File tree

5 files changed

+233
-0
lines changed

5 files changed

+233
-0
lines changed

packages/core/src/__clients__/core.ts

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ import {
106106
type BuildWalletLoginRequestParams,
107107
type VerifyAppProofsParams,
108108
type PollTransactionStatusParams,
109+
type OverrideApiKeyStamperParams,
110+
type OverridePasskeyStamperParams,
111+
type OverrideWalletManagerParams,
109112
} from "../__types__";
110113
import {
111114
buildSignUpBody,
@@ -280,6 +283,104 @@ export class TurnkeyClient {
280283
});
281284
};
282285

286+
/**
287+
* Overrides the API key stamper with a new temporary public key.
288+
*
289+
* - This function sets a temporary public key on the API key stamper or clears it if not provided.
290+
* - Useful for dynamically changing the API key used for signing requests.
291+
*
292+
* @param params.temporaryPublicKey - temporary public key to use for the API key stamper.
293+
* @returns A promise that resolves when the stamper and HTTP client have been updated.
294+
* @throws {TurnkeyError} If there is an error initializing the new stamper.
295+
*/
296+
overrideApiKeyStamper = async (
297+
params?: OverrideApiKeyStamperParams,
298+
): Promise<void> => {
299+
const { temporaryPublicKey } = params || {};
300+
// Note: we can expand on this function in the future if we want
301+
return withTurnkeyErrorHandling(
302+
async () => {
303+
if (temporaryPublicKey) {
304+
this.apiKeyStamper?.setTemporaryPublicKey(temporaryPublicKey);
305+
} else {
306+
this.apiKeyStamper?.clearTemporaryPublicKey();
307+
}
308+
},
309+
{
310+
errorMessage: "Failed to override API key stamper",
311+
errorCode: TurnkeyErrorCodes.INITIALIZE_API_KEY_STAMPER_ERROR,
312+
},
313+
);
314+
};
315+
316+
/**
317+
* Overrides the passkey stamper with a new configuration.
318+
*
319+
* - This function creates a new passkey stamper instance with the provided configuration.
320+
* - Reinitializes the stamper and recreates the HTTP client with the new stamper.
321+
* - Useful for dynamically changing passkey configuration (e.g., allowCredentials, rpId, timeout).
322+
*
323+
* @param params.newConfig - new passkey stamper configuration to use.
324+
* @returns A promise that resolves when the stamper and HTTP client have been updated.
325+
* @throws {TurnkeyError} If there is an error initializing the new stamper.
326+
*/
327+
overridePasskeyStamper = async (
328+
params: OverridePasskeyStamperParams,
329+
): Promise<void> => {
330+
const { newConfig } = params;
331+
332+
return withTurnkeyErrorHandling(
333+
async () => {
334+
// Create a new passkey stamper with the new configuration
335+
const passkeyStamper = new CrossPlatformPasskeyStamper(newConfig);
336+
337+
// Initialize the new stamper
338+
await passkeyStamper.init();
339+
340+
// Set the new stamper
341+
this.passkeyStamper = passkeyStamper;
342+
343+
// Recreate the HTTP client with the new stamper
344+
this.httpClient = this.createHttpClient();
345+
},
346+
{
347+
errorMessage: "Failed to override passkey stamper",
348+
errorCode: TurnkeyErrorCodes.INITIALIZE_PASSKEY_STAMPER_ERROR,
349+
},
350+
);
351+
};
352+
353+
/**
354+
* Overrides the wallet manager with a new configuration.
355+
*
356+
* - This function creates a new wallet manager instance with the provided configuration.
357+
* - Recreates the HTTP client with the new wallet manager's stamper.
358+
* - Useful for dynamically changing wallet configuration (e.g., chains, features, WalletConnect settings).
359+
*
360+
* @param params.newConfig - new wallet manager configuration to use.
361+
* @returns A promise that resolves when the wallet manager and HTTP client have been updated.
362+
* @throws {TurnkeyError} If there is an error creating the new wallet manager.
363+
*/
364+
overrideWalletManager = async (
365+
params: OverrideWalletManagerParams,
366+
): Promise<void> => {
367+
const { newConfig } = params;
368+
369+
return withTurnkeyErrorHandling(
370+
async () => {
371+
// Create a new wallet manager with the new configuration
372+
this.walletManager = await createWalletManager(newConfig);
373+
374+
// Recreate the HTTP client with the new wallet manager
375+
this.httpClient = this.createHttpClient();
376+
},
377+
{
378+
errorMessage: "Failed to override wallet manager",
379+
errorCode: TurnkeyErrorCodes.INITIALIZE_WALLET_MANAGER_ERROR,
380+
},
381+
);
382+
};
383+
283384
/**
284385
* Creates a new passkey authenticator for the user.
285386
*
@@ -421,6 +522,7 @@ export class TurnkeyClient {
421522
* @param params.sessionKey - session key to use for session creation (defaults to the default session key).
422523
* @param params.expirationSeconds - session expiration time in seconds (defaults to the configured default).
423524
* @param params.organizationId - organization ID to target (defaults to the session's organization ID or the parent organization ID).
525+
* @param params.allowCredentials - optional list of allowed credentials for passkey authentication. This allows you to restrict which passkeys can be used for login.
424526
* @returns A promise that resolves to a {@link PasskeyAuthResult}, which includes:
425527
* - `sessionToken`: the signed JWT session token.
426528
* - `credentialId`: an empty string.
@@ -430,6 +532,12 @@ export class TurnkeyClient {
430532
params?: LoginWithPasskeyParams,
431533
): Promise<PasskeyAuthResult> => {
432534
let generatedPublicKey: string | undefined = undefined;
535+
536+
const shouldOverrideConfig =
537+
params?.allowCredentials && this.passkeyStamper;
538+
539+
const currentConfig = this.config.passkeyConfig;
540+
433541
return await withTurnkeyErrorHandling(
434542
async () => {
435543
generatedPublicKey =
@@ -445,6 +553,16 @@ export class TurnkeyClient {
445553
TurnkeyErrorCodes.INTERNAL_ERROR,
446554
);
447555
}
556+
557+
if (shouldOverrideConfig) {
558+
// Override passkey stamper config to include allowCredentials
559+
const mergedConfig = {
560+
...currentConfig,
561+
allowCredentials: params?.allowCredentials!, // Can safely assert non-null due to check above
562+
};
563+
await this.overridePasskeyStamper({ newConfig: mergedConfig });
564+
}
565+
448566
const sessionResponse = await this.httpClient.stampLogin(
449567
{
450568
publicKey: generatedPublicKey,
@@ -494,6 +612,13 @@ export class TurnkeyClient {
494612
);
495613
}
496614
}
615+
616+
if (shouldOverrideConfig) {
617+
// Restore previous stamper after login attempt
618+
await this.overridePasskeyStamper({
619+
newConfig: currentConfig!, // can safely assert non-null since passkeyStamper exists
620+
});
621+
}
497622
},
498623
},
499624
);

packages/core/src/__types__/method-types/shared.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import type {
2222
WalletProvider,
2323
Wallet,
2424
TSignedRequest,
25+
TPasskeyStamperConfig,
26+
TWalletManagerConfig,
2527
} from "../index";
2628

2729
export type CreateHttpClientParams = {
@@ -32,6 +34,18 @@ export type CreateHttpClientParams = {
3234
defaultStamperType?: StamperType | undefined;
3335
};
3436

37+
export type OverrideApiKeyStamperParams = {
38+
temporaryPublicKey?: string | undefined;
39+
};
40+
41+
export type OverridePasskeyStamperParams = {
42+
newConfig: TPasskeyStamperConfig;
43+
};
44+
45+
export type OverrideWalletManagerParams = {
46+
newConfig: TWalletManagerConfig;
47+
};
48+
3549
export type CreatePasskeyParams = {
3650
name?: string;
3751
challenge?: string;
@@ -53,6 +67,7 @@ export type LoginWithPasskeyParams = {
5367
sessionKey?: string;
5468
expirationSeconds?: string;
5569
organizationId?: string;
70+
allowCredentials?: PublicKeyCredentialDescriptor[];
5671
};
5772

5873
export type SignUpWithPasskeyParams = {

packages/react-native-wallet-kit/src/providers/TurnkeyProvider.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ import {
7272
type VerifyAppProofsParams,
7373
type SignAndSendTransactionParams,
7474
type PollTransactionStatusParams,
75+
type OverrideApiKeyStamperParams,
76+
type OverridePasskeyStamperParams,
77+
type OverrideWalletManagerParams,
7578
} from "@turnkey/core";
7679
import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
7780
import { Platform } from "react-native";
@@ -729,6 +732,45 @@ export const TurnkeyProvider: React.FC<TurnkeyProviderProps> = ({
729732
[client],
730733
);
731734

735+
const overrideApiKeyStamper = useCallback(
736+
(params?: OverrideApiKeyStamperParams): Promise<void> => {
737+
if (!client) {
738+
throw new TurnkeyError(
739+
"Client is not initialized.",
740+
TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED,
741+
);
742+
}
743+
return client.overrideApiKeyStamper(params);
744+
},
745+
[client],
746+
);
747+
748+
const overridePasskeyStamper = useCallback(
749+
(params: OverridePasskeyStamperParams): Promise<void> => {
750+
if (!client) {
751+
throw new TurnkeyError(
752+
"Client is not initialized.",
753+
TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED,
754+
);
755+
}
756+
return client.overridePasskeyStamper(params);
757+
},
758+
[client],
759+
);
760+
761+
const overrideWalletManager = useCallback(
762+
(params: OverrideWalletManagerParams): Promise<void> => {
763+
if (!client) {
764+
throw new TurnkeyError(
765+
"Client is not initialized.",
766+
TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED,
767+
);
768+
}
769+
return client.overrideWalletManager(params);
770+
},
771+
[client],
772+
);
773+
732774
const logout: (params?: LogoutParams) => Promise<void> = useCallback(
733775
async (params?: { sessionKey?: string }): Promise<void> => {
734776
if (!client) {
@@ -3325,6 +3367,9 @@ export const TurnkeyProvider: React.FC<TurnkeyProviderProps> = ({
33253367
config: masterConfig,
33263368
httpClient: client?.httpClient,
33273369
createHttpClient,
3370+
overrideApiKeyStamper,
3371+
overridePasskeyStamper,
3372+
overrideWalletManager,
33283373
createPasskey,
33293374
logout,
33303375
loginWithPasskey,

packages/react-wallet-kit/src/providers/client/Provider.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ import {
102102
type PollTransactionStatusParams,
103103
type SignAndSendTransactionParams,
104104
type EthTransaction,
105+
type OverrideApiKeyStamperParams,
106+
type OverridePasskeyStamperParams,
107+
type OverrideWalletManagerParams,
105108
} from "@turnkey/core";
106109
import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
107110
import {
@@ -1252,6 +1255,45 @@ export const ClientProvider: React.FC<ClientProviderProps> = ({
12521255
[client],
12531256
);
12541257

1258+
const overrideApiKeyStamper = useCallback(
1259+
(params?: OverrideApiKeyStamperParams): Promise<void> => {
1260+
if (!client) {
1261+
throw new TurnkeyError(
1262+
"Client is not initialized.",
1263+
TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED,
1264+
);
1265+
}
1266+
return client.overrideApiKeyStamper(params);
1267+
},
1268+
[client],
1269+
);
1270+
1271+
const overridePasskeyStamper = useCallback(
1272+
(params: OverridePasskeyStamperParams): Promise<void> => {
1273+
if (!client) {
1274+
throw new TurnkeyError(
1275+
"Client is not initialized.",
1276+
TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED,
1277+
);
1278+
}
1279+
return client.overridePasskeyStamper(params);
1280+
},
1281+
[client],
1282+
);
1283+
1284+
const overrideWalletManager = useCallback(
1285+
(params: OverrideWalletManagerParams): Promise<void> => {
1286+
if (!client) {
1287+
throw new TurnkeyError(
1288+
"Client is not initialized.",
1289+
TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED,
1290+
);
1291+
}
1292+
return client.overrideWalletManager(params);
1293+
},
1294+
[client],
1295+
);
1296+
12551297
const getActiveSessionKey = useCallback(async (): Promise<
12561298
string | undefined
12571299
> => {
@@ -6055,6 +6097,9 @@ export const ClientProvider: React.FC<ClientProviderProps> = ({
60556097
config: masterConfig,
60566098
httpClient: client?.httpClient,
60576099
createHttpClient,
6100+
overrideApiKeyStamper,
6101+
overridePasskeyStamper,
6102+
overrideWalletManager,
60586103
createPasskey,
60596104
logout,
60606105
loginWithPasskey,

packages/sdk-types/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ export enum TurnkeyErrorCodes {
131131
INITIALIZE_IFRAME_ERROR = "INITIALIZE_IFRAME_ERROR",
132132
PLATFORM_MISMATCH = "PLATFORM_MISMATCH",
133133
UNSUPPORTED_PLATFORM = "UNSUPPORTED_PLATFORM",
134+
INITIALIZE_API_KEY_STAMPER_ERROR = "INITIALIZE_API_KEY_STAMPER_ERROR",
135+
INITIALIZE_PASSKEY_STAMPER_ERROR = "INITIALIZE_PASSKEY_STAMPER_ERROR",
136+
INITIALIZE_WALLET_MANAGER_ERROR = "INITIALIZE_WALLET_MANAGER_ERROR",
134137

135138
USER_CANCELED = "USER_CANCELED",
136139
BAD_RESPONSE = "BAD_RESPONSE",

0 commit comments

Comments
 (0)