> ## Documentation Index
> Fetch the complete documentation index at: https://docs.turnkey.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Add an additional passkey

> This guide demonstrates how to add a new credential (specifically, a passkey) to an existing wallet using the Turnkey SDK.

> ⚠️ **This example is outdated !**<br />
> Head over to [SDK Reference](https://docs.turnkey.com/generated-docs/formatted/react-wallet-kit/client-context-type-add-passkey) for the updated packages.

### Initialize the Passkey client

Begin by initializing the Turnkey SDK by passing in a config object containing:

* `rpId`: The [Relying Party](https://developer.mozilla.org/en-US/docs/Glossary/Relying_party) Identifier, which is the effective domain of your application.
* `apiBaseUrl`: The base URL of the Turnkey API: `https://api.turnkey.com`
* `defaultOrganizationId`: Your parent organization ID, which you can find in the [Turnkey dashboard](https://app.turnkey.com/dashboard).

<Accordion title="What's the purpose of the rpId?">
  The `rpId` is used in WebAuthn to uniquely identify the server that the passkey is associated with. The `rpId` is typically the effective domain of the web application, which is the domain portion of the URL without any subdomains. For example, if your application is hosted at `app.example.com`, the `rpId` would typically be `example.com`. This ensures that credentials are scoped to the correct domain and cannot be used by other domains, enhancing security.
</Accordion>

<Tabs>
  <Tab title="Next.js">
    First, wrap your application with the `TurnkeyProvider` in your `app/layout.tsx` file:

    ```ts app/layout.tsx theme={"system"}
    import { TurnkeyProvider } from "@turnkey/sdk-react";

    export default function RootLayout({
      children,
    }: {
      children: React.ReactNode;
    }) {
      return (
        <html lang="en">
          <body>
            <TurnkeyProvider
              config={{
                // The domain of your application
                rpId: process.env.NEXT_PUBLIC_TURNKEY_RP_ID,
                apiBaseUrl: process.env.NEXT_PUBLIC_TURNKEY_API_BASE_URL,
                defaultOrganizationId:
                  process.env.NEXT_PUBLIC_ORGANIZATION_ID,
              }}
            >
              {children}
            </TurnkeyProvider>
          </body>
        </html>
      );
    }
    ```

    Then, create a new file `app/add-passkey.tsx` where we'll implement the passkey functionality:

    ```tsx app/add-passkey.tsx theme={"system"}
    "use client";

    import { useState } from "react";
    import { useTurnkey } from "@turnkey/sdk-react";

    export default function AddPasskey() {
      const { passkeyClient } = useTurnkey();

      // We'll add more functionality here in the following steps

      return <div>{/* We'll add UI elements here */}</div>;
    }
    ```
  </Tab>

  <Tab title="TypeScript">
    Create a new file `src/add-passkey.ts`:

    ```ts src/add-passkey.ts theme={"system"}
    import { Turnkey } from "@turnkey/sdk-browser";

    // Initialize the Turnkey SDK with your organization credentials
    const turnkey = new Turnkey({
      rpId: process.env.TURNKEY_RP_ID, // Your relying party ID
      apiBaseUrl: process.env.TURNKEY_API_BASE_URL, // Turnkey API base URL
      defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID, // Your parent organization ID
    });

    // Initialize the Passkey Client
    const passkeyClient = turnkey.passkeyClient();

    // We'll add more functionality here in the following steps
    ```
  </Tab>
</Tabs>

### Authenticate the user

Now that that the Passkey Client is initialized, we'll call the `login` function which will prompt the user to authenticate with their passkey. Additionally, this function will set the current user in local storage upon successful authentication, which will be used later when creating an additional authenticator.

<Info>
  The user object which gets stored in local storage is defined as follows:

  ```
  export interface User {
    userId: string;
    username: string;
    organization: SubOrganization;
    readOnlySession?: ReadOnlySession;
  }
  ```
</Info>

<Tabs>
  <Tab title="Next.js">
    ```tsx app/add-passkey.tsx theme={"system"}
    // ... previous code

    export default function AddPasskey() {
      // We'll need the base Turnkey client to get the current user
      const { passkeyClient, turnkey } = useTurnkey();

      // ... previous code

      const getUser = async () => {
        // Get the current user from local storage,
        // we'll need the `userId` to create the authenticator in the next step
        const user = await turnkey?.getCurrentUser();
        if (user) {
          console.log("User retrieved successfully");
        }
        // return the user to be used in the next step
        return user;
      };

      return (
        <div>
          <button onClick={login}>Login</button>
        </div>
      );
    }
    ```
  </Tab>

  <Tab title="TypeScript">
    ```ts src/add-passkey.ts theme={"system"}
    // ... previous code

    const login = async () => {
      const response = await passkeyClient.login();
      if (response.organizationId) {
        console.log("User authenticated successfully");
      } else {
        console.log("User authentication failed");
      }
    };
    ```
  </Tab>
</Tabs>

### Get the current user

Before creating a new passkey, we'll get the current user. This function will retrieve the user from local storage, which was set after calling the `login` function. We'll need the `userId` to create the authenticator in the final step.

<Tabs>
  <Tab title="Next.js">
    ```ts app/add-passkey.tsx theme={"system"}
    // ... previous code

    export default function AddPasskey() {
      // We'll need the base Turnkey client to get the current user
      const { passkeyClient, turnkey } = useTurnkey();

      // ... previous code

      const getUser = async () => {
        // Get the current user from local storage,
        // we'll need the `userId` to create the authenticator in the next step
        const user = await turnkey?.getCurrentUser();
        if (user) {
          console.log("User retrieved successfully");
        }
        // return the user to be used in the next step
        return user;
      };

      return (
        <div>
          <button onClick={login}>Login</button>
        </div>
      );
    }
    ```
  </Tab>

  <Tab title="TypeScript">
    ```ts src/add-passkey.ts theme={"system"}
    // ... previous code

    const getCurrentUser = async () => {
      // Get the current user from local storage,
      // we'll need the `userId` to create the authenticator in the next step
      const user = await turnkey?.getCurrentUser();
      if (user) {
        console.log("User retrieved successfully");
      }
      // return the user to be used in the next step
      return user;
    };
    ```
  </Tab>
</Tabs>

### Create user passkey

Now that you have authenticated the user, you can call the `createUserPasskey` function to create a new user passkey credential. Calling this method will prompt the user to create a passkey, which will be securely stored by their browser. This credential will be associated with the user's account and used for future authentication. Once the credential is created, we'll use it in the next step to create a new authenticator for the user.

<Note>
  The credential includes an encoded challenge and attestation. The encoded challenge ensures the request is fresh and legitimate, while the attestation verifies the authenticity of the device creating the credential. For more information on how passkeys work, including details on the challenge and attestation objects, you can refer to the [Passkeys Documentation](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API#passkeys).
</Note>

<Tabs>
  <Tab title="Next.js">
    ```tsx app/add-passkey.tsx theme={"system"}
    // ... previous code

    export default function AddPasskey() {
      const { passkeyClient } = useTurnkey();

      // ... previous code

      // We'll pass the user object returned from `getUser` to this function
      const createNewPasskey = async (user: User) => {
        const credential = await passkeyClient?.createUserPasskey({
          publicKey: {
            // This is the name of the passkey that will be displayed to the user
            rp: {
              name: "Wallet Passkey",
            },
            user: {
              // We can use the username as the name and display name
              name: user.username,
              displayName: user.username,
            },
          },
        });

        // we'll use this credential in the next step to create a new authenticator
        return credential;
      };

      return (/* ... */);
    }
    ```
  </Tab>

  <Tab title="TypeScript">
    ```ts src/add-passkey.ts theme={"system"}
    // ... previous code

    // We'll pass the user object returned from `getUser` to this function
    const createNewPasskey = async (user: User) => {
      const credential = await passkeyClient?.createUserPasskey({
        publicKey: {
          // This is the name of the passkey that will be displayed to the user
          rp: {
            name: "Wallet Passkey",
          },
          user: {
            // We can use the username as the name and display name
            name: user.username,
            displayName: user.username,
          },
        },
      });
      // we'll use this credential in the next step to create a new authenticator
      return credential;
    };
    ```
  </Tab>
</Tabs>

### Add the credential to the wallet

Now that you have created a new user passkey credential, we'll use this credential to create a new passkey authenticator for the user. We'll need the userId to create the authenticator, so we'll get the current user first. This value comes from local storage which was set in the previous step when the user successfully authenticated via the `login` function.

<Tabs>
  <Tab title="Next.js">
    ```tsx app/add-passkey.tsx theme={"system"}
    // ... previous code

    export default function AddPasskey() {
      const { passkeyClient, turnkey } = useTurnkey();

      // ... previous code

      const addPasskey = async () => {
        const user = await getUser();
        const credential = await createNewPasskey(user);

        const authenticatorsResponse = await passkeyClient.createAuthenticators({
          authenticators: [
            {
              authenticatorName: "New Passkey Authenticator",
              challenge: credential.encodedChallenge,
              attestation: credential.attestation,
            },
          ],
          userId: user.userId,
        });

        // Check if the authenticator was created successfully
        if (authenticatorsResponse?.activity.id) {
          console.log("Authenticator created successfully");
        }
      };

      return (
        <div>
          {/* Add a button to add the passkey to the wallet */}
          <button onClick={addPasskey}>AddPasskey</button>
          <button onClick={login}>Login</button>
        </div>
      );
    }
    ```

    <Accordion title="Complete  add-passkey.tsx component">
      ```tsx app/add-passkey.tsx theme={"system"}
      "use client";

      import { useState } from "react";
      import { useTurnkey } from "@turnkey/sdk-react";

      export default function AddPasskey() {
      const { passkeyClient, turnkey } = useTurnkey();

      const login = async () => {
        const response = await passkeyClient?.login();
        if (response.organizationId) {
          console.log("User authenticated successfully");
        } else {
          console.log("User authentication failed");
        }
      };

      const getUser = async () => {
        const user = await turnkey?.getCurrentUser();
        if (user) {
          console.log("User retrieved successfully");
        }
        return user;
      };

      const createNewPasskey = async (user: User) => {
        const credential = await passkeyClient?.createUserPasskey({
          publicKey: {
            rp: {
              name: "Wallet Passkey",
            },
            user: {
              name: user.username,
              displayName: user.username,
            },
          },
        });

        return credential;
      };

      const addPasskey = async () => {
        const user = await getUser();
        const credential = await createNewPasskey(user);

        const authenticatorsResponse = await passkeyClient.createAuthenticators({
          authenticators: [
            {
              authenticatorName: "New Passkey Authenticator",
              challenge: credential.encodedChallenge,
              attestation: credential.attestation,
            },
          ],
          userId: user.userId,
        });

        if (authenticatorsResponse?.activity.id) {
          console.log("Authenticator created successfully");
        }
      };

      return (
        <div>
          <button onClick={addPasskey}>AddPasskey</button>
          <button onClick={login}>Login</button>
        </div>
      );
      }
      ```
    </Accordion>
  </Tab>

  <Tab title="TypeScript">
    ```ts src/add-passkey.ts theme={"system"}
    // ... previous code

    const addPasskey = async () => {
      const user = await getUser();
      const credential = await createNewPasskey(user);

      // Check if the credential was created successfully
      if (!credential) {
        console.log("Credential not created");
        return;
      }

      const authenticatorsResponse = await passkeyClient.createAuthenticators({
        authenticators: [
          {
            authenticatorName: "New Passkey Authenticator",
            challenge: credential.encodedChallenge,
            attestation: credential.attestation,
          },
        ],
        userId: user.userId,
      });

      // Check if the authenticator was created successfully
      if (authenticatorsResponse?.activity.id) {
        console.log("Authenticator created successfully");
      }
    };
    ```
  </Tab>
</Tabs>

### Optional: read/write sessions

In some cases, you may want to create a read/write session for the user to reduce the number of passkey prompts. This session can be used instead of the passkey to sign requests to Turnkey's API to improve the user experience.

In the this tutorial we used the passkey to authenticate the request to create a new authenticator. The result is that the user will be prompted 3 times:

1. To login
2. To create the new passkey
3. To authenticate the request to create a new authenticator

By creating a read/write session, we can reduce the number of passkey prompts to 2:

1. To login and create a session
2. To authenticate the request to create a new authenticator

To create a read/write session, we simply replace `passkeyClient.login()` with `passkeyClient.loginWithReadwriteSession()`:

```ts src/add-passkey.ts theme={"system"}
// ... previous code

const login = async () => {
  const response = await passkeyClient.loginWithReadwriteSession();
  // ... previous code
};
```

Assuming the login is successful, a read/write session object will be stored in local storage. We'll use the stored session in conjunction with the iframe client to authenticate the create authenticator request.

<Tabs>
  <Tab title="Next.js">
    We'll use the active client returned from the `useTurnkey` hook which will be initialized with the read/write session. The rest of the code remains the same.

    ```tsx app/add-passkey.tsx theme={"system"}
    // ... previous code

    export default function AddPasskey() {
      const { getActiveClient, turnkey } = useTurnkey();

      // ... previous code

      const addPasskey = async () => {
        const user = await getUser();
        const credential = await createNewPasskey(user);

        // Get the active client which returns the iframe client initialized with the read/write session
        const activeClient = await getActiveClient();

        // Since we're using the read/write session this won't prompt the user
        const authenticatorsResponse = await activeClient.createAuthenticators({
          // ...
        });

        // ... rest of the code remains the same
      };

      return (/* ... */);
    }
    ```
  </Tab>

  <Tab title="TypeScript">
    ##### 1. Initialize the iframe client

    We'll create a new function to initialize the iframe client and inject the read/write session.

    ```ts src/add-passkey.ts theme={"system"}
    // ... previous code

    const getIframeClient = async () => {
      const iframeContainerId = "turnkey-auth-container-id";

      const authIframeClient = await turnkey.iframeClient(
        document.getElementById(iframeContainerId),
      );

      const readWriteSession = await turnkey?.getReadWriteSession();

      if (readWriteSession) {
        const injected = await authIframeClient?.injectCredentialBundle(
          readWriteSession.authBundle,
        );
      }

      return authIframeClient;
    };
    ```

    <Info>
      When using the TypeScript SDK, you'll need to ensure that the HTML element exists somewhere in the rendered DOM.

      ```
      <div id="turnkey-auth-iframe-container-id" />
      ```
    </Info>

    ##### 2. Update the `addPasskey` function

    We'll update the `addPasskey` function to use the iframe client to authenticate the request to create a new authenticator.

    ```ts src/add-passkey.ts theme={"system"}
    // ... previous code

    const addPasskey = async () => {
      // ... previous code

      const iframeClient = await getIframeClient();

      const authenticatorsResponse = await iframeClient.createAuthenticators({
        // ...
      });
      // ... rest of the code remains the same
    };
    ```
  </Tab>
</Tabs>

## Conclusion

In this guide, we've walked through the process of adding a new credential to an existing wallet using the Turnkey SDK. By following these steps, you can improve the usability of your application by allowing users to create multiple authentication methods. This flexibility enables users to add a hardware security device like a Yubikey, or a native passkey via providers like iCloud keychain or 1Password, enhancing their overall experience with your application.

For a complete example, check out our [demo embedded wallet](https://github.com/tkhq/demo-embedded-wallet/blob/main/src/components/add-passkey.tsx).
