Skip to content

Commit

Permalink
Adds wallet stamper client
Browse files Browse the repository at this point in the history
  • Loading branch information
taylorjdawson committed Oct 23, 2024
1 parent 0d8d28b commit c88a8de
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 49 deletions.
1 change: 1 addition & 0 deletions packages/sdk-browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@turnkey/encoding": "workspace:*",
"@turnkey/http": "workspace:*",
"@turnkey/iframe-stamper": "workspace:*",
"@turnkey/wallet-stamper": "workspace:^",
"@turnkey/webauthn-stamper": "workspace:*",
"bs58check": "^3.0.1",
"buffer": "^6.0.3",
Expand Down
9 changes: 7 additions & 2 deletions packages/sdk-browser/src/__types__/base.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { TActivityId, TActivityStatus } from "@turnkey/http";
import type { TActivityId, TActivityStatus } from '@turnkey/http';
import type { WalletInterface } from '@turnkey/wallet-stamper';

export type GrpcStatus = {
message: string;
Expand Down Expand Up @@ -38,7 +39,7 @@ export class TurnkeyRequestError extends Error {

super(turnkeyErrorMessage);

this.name = "TurnkeyRequestError";
this.name = 'TurnkeyRequestError';
this.details = input.details ?? null;
this.code = input.code;
}
Expand Down Expand Up @@ -105,3 +106,7 @@ export interface IframeClientParams {
iframeUrl: string;
iframeElementId?: string;
}

export interface TurnkeyWalletClientConfig extends SDKClientConfigWithStamper {
wallet: WalletInterface;
}
20 changes: 11 additions & 9 deletions packages/sdk-browser/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,49 @@ import {
type TSignedRequest,
type TActivity,
type TurnkeyApiTypes,
} from "@turnkey/http";
} from '@turnkey/http';

import {
ApiKeyStamper,
signWithApiKey,
TApiKeyStamperConfig,
} from "@turnkey/api-key-stamper";
} from '@turnkey/api-key-stamper';

import {
IframeEventType,
IframeStamper,
TIframeStamperConfig,
} from "@turnkey/iframe-stamper";
} from '@turnkey/iframe-stamper';

import {
TWebauthnStamperConfig,
WebauthnStamper,
} from "@turnkey/webauthn-stamper";
} from '@turnkey/webauthn-stamper';

import {
TurnkeyBrowserSDK,
TurnkeyBrowserClient,
TurnkeyIframeClient,
TurnkeyPasskeyClient,
} from "./sdk-client";
} from './sdk-client';

export { getStorageValue, setStorageValue, StorageKeys } from "./storage";
export { TurnkeyWalletClient } from './wallet-client';

export { getStorageValue, setStorageValue, StorageKeys } from './storage';

import {
defaultEthereumAccountAtIndex,
DEFAULT_ETHEREUM_ACCOUNTS,
defaultSolanaAccountAtIndex,
DEFAULT_SOLANA_ACCOUNTS,
} from "./turnkey-helpers";
} from './turnkey-helpers';

import type {
TurnkeySDKClientConfig,
TurnkeySDKBrowserConfig,
} from "./__types__/base";
} from './__types__/base';

import type * as TurnkeySDKApiTypes from "./__generated__/sdk_api_types";
import type * as TurnkeySDKApiTypes from './__generated__/sdk_api_types';

// Classes
export {
Expand Down
73 changes: 45 additions & 28 deletions packages/sdk-browser/src/sdk-client.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,43 @@
import { WebauthnStamper } from "@turnkey/webauthn-stamper";
import { IframeStamper, KeyFormat } from "@turnkey/iframe-stamper";
import { getWebAuthnAttestation } from "@turnkey/http";
import { WebauthnStamper } from '@turnkey/webauthn-stamper';
import { IframeStamper, KeyFormat } from '@turnkey/iframe-stamper';
import { getWebAuthnAttestation } from '@turnkey/http';

import { VERSION } from "./__generated__/version";
import WindowWrapper from "./__polyfills__/window";
import { VERSION } from './__generated__/version';
import WindowWrapper from './__polyfills__/window';

import type {
GrpcStatus,
TurnkeySDKClientConfig,
TurnkeySDKBrowserConfig,
IframeClientParams,
} from "./__types__/base";
} from './__types__/base';

import { TurnkeyRequestError } from "./__types__/base";
import { TurnkeyRequestError } from './__types__/base';

import { TurnkeySDKClientBase } from "./__generated__/sdk-client-base";
import type * as SdkApiTypes from "./__generated__/sdk_api_types";
import { TurnkeySDKClientBase } from './__generated__/sdk-client-base';
import type * as SdkApiTypes from './__generated__/sdk_api_types';

import type {
User,
SubOrganization,
ReadWriteSession,
Passkey,
} from "./models";
} from './models';
import {
StorageKeys,
getStorageValue,
removeStorageValue,
setStorageValue,
} from "./storage";
} from './storage';
import {
generateRandomBuffer,
base64UrlEncode,
createEmbeddedAPIKey,
} from "./utils";
} from './utils';
import { type WalletInterface } from '@turnkey/wallet-stamper';
import { type TurnkeyWalletClient } from './wallet-client';

const DEFAULT_SESSION_EXPIRATION = "900"; // default to 15 minutes
const DEFAULT_SESSION_EXPIRATION = '900'; // default to 15 minutes

export class TurnkeyBrowserSDK {
config: TurnkeySDKBrowserConfig;
Expand All @@ -50,7 +52,7 @@ export class TurnkeyBrowserSDK {

if (!targetRpId) {
throw new Error(
"Tried to initialize a passkey client with no rpId defined"
'Tried to initialize a passkey client with no rpId defined'
);
}

Expand All @@ -70,12 +72,12 @@ export class TurnkeyBrowserSDK {
): Promise<TurnkeyIframeClient> => {
if (!params.iframeUrl) {
throw new Error(
"Tried to initialize iframeClient with no iframeUrl defined"
'Tried to initialize iframeClient with no iframeUrl defined'
);
}

const TurnkeyIframeElementId =
params.iframeElementId ?? "turnkey-default-iframe-element-id";
params.iframeElementId ?? 'turnkey-default-iframe-element-id';

const iframeStamper = new IframeStamper({
iframeContainer: params.iframeContainer,
Expand All @@ -92,6 +94,21 @@ export class TurnkeyBrowserSDK {
});
};

walletClient = async (
wallet: WalletInterface
): Promise<TurnkeyWalletClient> => {
const { WalletStamper, TurnkeyWalletClient } = await import(
'./wallet-client'
);

return new TurnkeyWalletClient({
stamper: new WalletStamper(wallet),
wallet,
apiBaseUrl: this.config.apiBaseUrl,
organizationId: this.config.defaultOrganizationId,
});
};

serverSign = async <TResponseType>(
methodName: string,
params: any[],
Expand All @@ -100,7 +117,7 @@ export class TurnkeyBrowserSDK {
const targetServerSignUrl = serverSignUrl ?? this.config.serverSignUrl;

if (!targetServerSignUrl) {
throw new Error("Tried to call serverSign with no serverSignUrl defined");
throw new Error('Tried to call serverSign with no serverSignUrl defined');
}

const stringifiedBody = JSON.stringify({
Expand All @@ -109,13 +126,13 @@ export class TurnkeyBrowserSDK {
});

const response = await fetch(targetServerSignUrl, {
method: "POST",
method: 'POST',
headers: {
"Content-Type": "application/json",
"X-Client-Version": VERSION,
'Content-Type': 'application/json',
'X-Client-Version': VERSION,
},
body: stringifiedBody,
redirect: "follow",
redirect: 'follow',
});

if (!response.ok) {
Expand Down Expand Up @@ -316,19 +333,19 @@ export class TurnkeyPasskeyClient extends TurnkeyBrowserClient {
publicKey: {
rp: {
id: config.publicKey?.rp?.id ?? this.rpId,
name: config.publicKey?.rp?.name ?? "",
name: config.publicKey?.rp?.name ?? '',
},
challenge: config.publicKey?.challenge ?? challenge,
pubKeyCredParams: config.publicKey?.pubKeyCredParams ?? [
{
type: "public-key",
type: 'public-key',
alg: -7,
},
],
user: {
id: config.publicKey?.user?.id ?? authenticatorUserId,
name: config.publicKey?.user?.name ?? "Default User",
displayName: config.publicKey?.user?.displayName ?? "Default User",
name: config.publicKey?.user?.name ?? 'Default User',
displayName: config.publicKey?.user?.displayName ?? 'Default User',
},
authenticatorSelection: {
authenticatorAttachment:
Expand All @@ -338,10 +355,10 @@ export class TurnkeyPasskeyClient extends TurnkeyBrowserClient {
config.publicKey?.authenticatorSelection?.requireResidentKey ??
true,
residentKey:
config.publicKey?.authenticatorSelection?.residentKey ?? "required",
config.publicKey?.authenticatorSelection?.residentKey ?? 'required',
userVerification:
config.publicKey?.authenticatorSelection?.userVerification ??
"preferred",
'preferred',
},
},
};
Expand Down Expand Up @@ -387,7 +404,7 @@ export class TurnkeyPasskeyClient extends TurnkeyBrowserClient {
apiKeyName: `Session Key ${String(Date.now())}`,
publicKey,
expirationSeconds,
curveType: "API_KEY_CURVE_P256",
curveType: 'API_KEY_CURVE_P256',
},
],
});
Expand Down
10 changes: 10 additions & 0 deletions packages/sdk-browser/src/wallet-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { TurnkeyWalletClientConfig } from './__types__/base';
import { TurnkeyBrowserClient } from './sdk-client';

export { WalletStamper } from '@turnkey/wallet-stamper';

export class TurnkeyWalletClient extends TurnkeyBrowserClient {
constructor(config: TurnkeyWalletClientConfig) {
super(config);
}
}
3 changes: 2 additions & 1 deletion packages/sdk-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"typecheck": "tsc -p tsconfig.typecheck.json"
},
"dependencies": {
"@turnkey/sdk-browser": "workspace:*"
"@turnkey/sdk-browser": "workspace:*",
"@turnkey/wallet-stamper": "workspace:^"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
Expand Down
25 changes: 18 additions & 7 deletions packages/sdk-react/src/contexts/TurnkeyContext.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,37 @@
import { ReactNode, createContext, useState, useEffect, useRef } from "react";
import { ReactNode, createContext, useState, useEffect, useRef } from 'react';
import {
Turnkey,
TurnkeyIframeClient,
TurnkeyPasskeyClient,
TurnkeySDKBrowserConfig,
TurnkeyBrowserClient,
} from "@turnkey/sdk-browser";
TurnkeyWalletClient,
} from '@turnkey/sdk-browser';
import type { WalletInterface } from '@turnkey/wallet-stamper';

export interface TurnkeyClientType {
turnkey: Turnkey | undefined;
authIframeClient: TurnkeyIframeClient | undefined;
passkeyClient: TurnkeyPasskeyClient | undefined;
walletClient: TurnkeyWalletClient | undefined;
getActiveClient: () => Promise<TurnkeyBrowserClient | undefined>;
}

export const TurnkeyContext = createContext<TurnkeyClientType>({
turnkey: undefined,
passkeyClient: undefined,
authIframeClient: undefined,
walletClient: undefined,
getActiveClient: async () => {
return undefined;
},
});

interface TurnkeyProviderProps {
children: ReactNode;
config: TurnkeySDKBrowserConfig;
config: TurnkeySDKBrowserConfig & {
wallet: WalletInterface;
};
}

export const TurnkeyProvider: React.FC<TurnkeyProviderProps> = ({
Expand All @@ -36,13 +42,16 @@ export const TurnkeyProvider: React.FC<TurnkeyProviderProps> = ({
const [passkeyClient, setPasskeyClient] = useState<
TurnkeyPasskeyClient | undefined
>(undefined);
const [walletClient, setWalletClient] = useState<
TurnkeyWalletClient | undefined
>(undefined);
const [authIframeClient, setAuthIframeClient] = useState<
TurnkeyIframeClient | undefined
>(undefined);
const iframeInit = useRef<boolean>(false);

const TurnkeyAuthIframeContainerId = "turnkey-auth-iframe-container-id";
const TurnkeyAuthIframeElementId = "turnkey-auth-iframe-element-id";
const TurnkeyAuthIframeContainerId = 'turnkey-auth-iframe-container-id';
const TurnkeyAuthIframeElementId = 'turnkey-auth-iframe-element-id';

const getActiveClient = async () => {
let currentClient: TurnkeyBrowserClient | undefined = passkeyClient;
Expand Down Expand Up @@ -90,12 +99,13 @@ export const TurnkeyProvider: React.FC<TurnkeyProviderProps> = ({
const newTurnkey = new Turnkey(config);
setTurnkey(newTurnkey);
setPasskeyClient(newTurnkey.passkeyClient());
setWalletClient(await newTurnkey.walletClient(config.wallet));

const newAuthIframeClient = await newTurnkey.iframeClient({
iframeContainer: document.getElementById(
TurnkeyAuthIframeContainerId
),
iframeUrl: "https://auth.turnkey.com",
iframeUrl: 'https://auth.turnkey.com',
iframeElementId: TurnkeyAuthIframeElementId,
});
setAuthIframeClient(newAuthIframeClient);
Expand All @@ -109,14 +119,15 @@ export const TurnkeyProvider: React.FC<TurnkeyProviderProps> = ({
turnkey,
passkeyClient,
authIframeClient,
walletClient,
getActiveClient,
}}
>
{children}
<div
className=""
id={TurnkeyAuthIframeContainerId}
style={{ display: "none" }}
style={{ display: 'none' }}
/>
</TurnkeyContext.Provider>
);
Expand Down
10 changes: 10 additions & 0 deletions packages/sdk-react/src/hooks/use-turnkey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useContext } from "react";
import { TurnkeyContext } from "../contexts/TurnkeyContext";

export const useTurnkey = () => {
const context = useContext(TurnkeyContext);
if (!context) {
throw new Error("useTurnkey must be used within a TurnkeyProvider");
}
return context;
};
4 changes: 2 additions & 2 deletions packages/sdk-react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TurnkeyContext, TurnkeyProvider } from "./contexts/TurnkeyContext";
import { useTurnkey } from "./hooks/useTurnkey";
import { TurnkeyContext, TurnkeyProvider } from './contexts/TurnkeyContext';
import { useTurnkey } from './hooks/use-turnkey';

export { TurnkeyContext, TurnkeyProvider, useTurnkey };
Loading

0 comments on commit c88a8de

Please sign in to comment.