Skip to content

Commit

Permalink
Add wallet stamper client and update session storage
Browse files Browse the repository at this point in the history
  • Loading branch information
taylorjdawson committed Oct 24, 2024
1 parent c88a8de commit c15023c
Show file tree
Hide file tree
Showing 17 changed files with 408 additions and 277 deletions.
16 changes: 5 additions & 11 deletions examples/with-wallet-stamper/src/components/turnkey-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@ import React, { createContext, useContext, useState, useEffect } from "react";
import { createActivityPoller, type TurnkeyClient } from "@turnkey/http";

import {
SolanaWalletInterface,
TStamper,
WalletInterface,
WalletStamper,
EvmWalletInterface,
} from "@turnkey/wallet-stamper";
import { createWebauthnStamper, Email } from "@/lib/turnkey";
import { createUserSubOrg, getSubOrgByPublicKey } from "@/lib/server";
import { ChainType } from "@/lib/types";
import { useWallet } from "@solana/wallet-adapter-react";

import { useRouter } from "next/navigation";
import { ACCOUNT_CONFIG_SOLANA } from "@/lib/constants";
Expand Down Expand Up @@ -101,13 +98,7 @@ export const TurnkeyProvider: React.FC<TurnkeyProviderProps> = ({
chainType: ChainType = ChainType.SOLANA
) {
setAuthenticating(true);
let publicKey = null;
if (chainType === ChainType.SOLANA) {
const solanaWallet = wallet as SolanaWalletInterface;
publicKey = solanaWallet.recoverPublicKey();
} else if (chainType === ChainType.EVM) {
const evmWallet = wallet as EvmWalletInterface;
}
const publicKey = await wallet?.getPublicKey();

const res = await createUserSubOrg({
email,
Expand Down Expand Up @@ -139,7 +130,10 @@ export const TurnkeyProvider: React.FC<TurnkeyProviderProps> = ({
}

async function signInWithWallet(): Promise<User | null> {
const publicKey = (wallet as SolanaWalletInterface)?.recoverPublicKey();
const publicKey = await wallet?.getPublicKey();
if (!publicKey) {
return null;
}
const { organizationIds } = await getSubOrgByPublicKey(publicKey);
const organizationId = organizationIds[0];

Expand Down
127 changes: 113 additions & 14 deletions packages/sdk-browser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,126 @@ Turnkey API documentation lives here: https://docs.turnkey.com.
$ npm install @turnkey/sdk-browser
```

### Initialize

```typescript
import { Turnkey } from "@turnkey/sdk-browser";

const turnkey = new Turnkey({
apiBaseUrl: "https://api.turnkey.com",
defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID,
// Optional: Your relying party ID - for use with Passkey authentication
rpId: process.env.TURNKEY_RP_ID,
});
```

### Turnkey Clients

#### Passkey

The Passkey client allows for authentication to Turnkey's API using Passkeys.

```typescript
const passkeyClient = turnkey.passkeyClient();

// User will be prompted to login with their passkey
await passkeyClient.login();

// Make authenticated requests to Turnkey API, such as listing user's wallets
const walletsResponse = await passkeyClient.getWallets();
```

#### Iframe

The Iframe client can be initialized to interact with Turnkey's hosted iframes for sensitive operations.
The `iframeContainer` parameter is required, and should be a reference to the DOM element that will host the iframe.
The `iframeUrl` is the URL of the iframe you wish to interact with.

The example below demonstrates how to initialize the Iframe client for use with [Email Auth](https://docs.turnkey.com/embedded-wallets/sub-organization-auth)
by passing in `https://auth.turnkey.com` as the `iframeUrl`.

```typescript
const iframeClient = await turnkey.iframeClient({
// The container element that will host the iframe
iframeContainer: document.getElementById("<iframe container id>"),
iframeUrl: "https://auth.turnkey.com",
});

const response = await iframeClient.injectCredentialBundle(
"<Credential from Email>"
);
if (response) {
await iframeClient.getWallets();
}
```

##### IFrame URLs:

| Flow | URL |
| ------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| [Email Auth](https://docs.turnkey.com/embedded-wallets/sub-organization-auth) | [auth.turnkey.com](https://auth.turnkey.com) |
| [Email Recovery](https://docs.turnkey.com/embedded-wallets/sub-organization-recovery) | [recovery.turnkey.com](https://recovery.turnkey.com) |
| [Import Wallet](https://docs.turnkey.com/features/import-wallets) | [import.turnkey.com](https://import.turnkey.com) |
| [Export Wallet](https://docs.turnkey.com/features/export-wallets) | [export.turnkey.com](https://export.turnkey.com) |

#### Wallet

The Wallet client is designed for using your Solana or EVM wallet to stamp and approve activity requests for Turnkey's API.
This stamping process leverages the wallet's signature to authenticate requests.

The example below showcases how to use an injected Ethereum wallet to stamp requests to Turnkey's API.
The user will be prompted to sign a message containing the activity request payload to be sent to Turnkey.

```typescript
import {
TurnkeyBrowserSDK,
TurnkeySDKBrowserConfig,
TurnkeySDKBrowserClient,
} from "@turnkey/sdk-browser";
createWalletClient,
custom,
recoverPublicKey,
hashMessage,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";

import { WalletStamper } from "@turnkey/wallet-stamper";

// Create a new wallet client with a JSON-RPC account from the injected provider
const walletClient = createWalletClient({
chain: mainnet,
transport: custom(window.ethereum!),
});

// This config contains parameters including base URLs, iframe URLs, org ID, and rp ID (relying party ID for WebAuthn)
import turnkeyConfig from "./turnkey.json";
const signMessage = async (message: string) => {
const account = walletClient.account.address;
return walletClient.signMessage({
account,
message,
});
};

// Use the config to instantiate a Turnkey Client
const turnkeyClient = new TurnkeyBrowserSDK(turnkeyConfig);
const getPublicKey = async () => {
// Required to recover the secp256k1 public key from the signature
const arbitraryMessage = "getPublicKey";
const signature = await signMessage(arbitraryMessage);

// Now you can make authenticated requests!
const response = await turnkeyClient?.passkeySign.login();
const secp256k1PublicKey = recoverPublicKey({
hash: hashMessage(arbitraryMessage),
signature: signature as Hex,
});
return secp256k1PublicKey;
};

const walletClient = turnkey.walletClient({
wallet: {
signMessage,
getPublicKey,
},
});

// Make authenticated requests to Turnkey API, such as listing user's wallets
// User will be prompted to sign a message to authenticate the request
const walletsResponse = await walletClient.getWallets();
```

## Helpers

`@turnkey/sdk-browser` provides `TurnkeySDKBrowserClient`, which offers wrappers around commonly used Turnkey activities, such as creating new wallets and wallet accounts.

// TODO:
// - explain subtypes within sdk-client.ts
// - point to demo wallet
4 changes: 2 additions & 2 deletions packages/sdk-browser/scripts/codegen.js
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ export class TurnkeySDKClientBase {
codeBuffer.push(
`\n\t${methodName} = async (input: SdkApiTypes.${inputType}): Promise<SdkApiTypes.${responseType}> => {
const { organizationId, timestampMs, ...rest } = input;
const currentUser = await getStorageValue(StorageKeys.CurrentUser);
const currentUser = await getStorageValue(StorageKeys.UserSession);
return this.command("${endpointPath}", {
parameters: rest,
organizationId: organizationId ?? (currentUser?.organization?.organizationId ?? this.config.organizationId),
Expand All @@ -438,7 +438,7 @@ export class TurnkeySDKClientBase {
codeBuffer.push(
`\n\t${methodName} = async (input: SdkApiTypes.${inputType}): Promise<SdkApiTypes.${responseType}> => {
const { organizationId, timestampMs, ...rest } = input;
const currentUser = await getStorageValue(StorageKeys.CurrentUser);
const currentUser = await getStorageValue(StorageKeys.UserSession);
return this.activityDecision("${endpointPath}",
{
parameters: rest,
Expand Down
Loading

0 comments on commit c15023c

Please sign in to comment.