# Passkey Signup Authentication

> How to implement Passkey Signup authentication across different frameworks

> For the complete documentation index, see [llms.txt](/docs/llms.txt).

Passkeys provide a secure, passwordless authentication method that can be used to create wallets for your users without going through email verification flows. You can implement passkey signup with or without an associated email address.

<Warning>
  If you create a passkey without an email associated with the user, you risk your users losing access to their wallets if they lose their device.

  **Recommended security practice:** Proxy authentication requests to your backend server to enforce additional security measures:

  * When a user attempts to sign up with both passkey and email, you can first require email verification before allowing the passkey to be created
  * Alternatively, you can restrict initial signup to email-based methods only (which inherently verify email ownership), then allow users to add passkeys after their account is established
  * This approach gives you greater control over the authentication flow and helps prevent account recovery issues

  By implementing server-side verification, you ensure that passkeys are only created for verified identities, reducing the risk of permanent access loss.
</Warning>


<Tabs>
  <Tab title="React" language="react">
    ## Implementation Options

    You can implement Passkey Signup authentication in two ways:

    * [Pre-built UI Components](#pre-built-ui-components) - Quick implementation with minimal code
    * [Custom UI](#custom-ui) - Complete control over the user experience

    ## Pre-built UI Components

    Wallet APIs provides pre-built UI components that handle the entire Passkey Signup authentication flow with minimal code.

    ### Step 1: Add Authentication Components to Your Page

    Before configuring your authentication, first add one of the pre-built components to your application:

    ## Using Modal Authentication

To add authentication in a modal popup:

```tsx twoslash
import React from "react";
import { useAuthModal } from "@account-kit/react";

export default function MyPage() {
  const { openAuthModal } = useAuthModal();

  return <button onClick={openAuthModal}>Sign in</button>;
}
```

For more details on modal configuration, see the [Modal Authentication](/docs/wallets/react/ui-components#modal-auth) documentation.


    Or:

    ## Using Embedded Authentication

To embed authentication directly in your page:

```tsx twoslash
import React from "react";
import { AuthCard } from "@account-kit/react";

export default function MyLoginPage() {
  return (
    <div className="flex flex-row p-4 bg-white border border-gray-200 rounded-lg">
      <AuthCard />
    </div>
  );
}
```

For more details on embedded authentication, see the [Embedded Authentication](/docs/wallets/react/ui-components#embedded-auth) documentation.


    ### Step 2: Configure Passkey Signup in UI Components

    After adding the components, configure the Passkey Signup authentication in your application config:

    ```tsx twoslash
    import { AlchemyAccountsUIConfig, createConfig } from "@account-kit/react";
    import { sepolia, alchemy } from "@account-kit/infra";

    const uiConfig: AlchemyAccountsUIConfig = {
      auth: {
        sections: [
          [
            // Include passkey in a section
            { type: "passkey" },

            // You can combine with other authentication methods
            { type: "email" },
          ],
        ],
        // Enable automatic passkey creation after signup
        addPasskeyOnSignup: true,
      },
    };

    export const config = createConfig(
      {
        transport: alchemy({ apiKey: "your-api-key" }),
        chain: sepolia,
      },
      uiConfig,
    );
    ```

    ## Custom UI

    If you need complete control over the user experience, you can implement your own custom UI for Passkey Signup authentication using Wallet APIs hooks.

    ### Option 1: Passkey Signup with Email (Recommended)

    This approach associates an email with the passkey, allowing users to recover their account if they lose access to their device.

    ```tsx twoslash
    import { useAuthenticate } from "@account-kit/react";

    // Inside your component
    const { authenticate } = useAuthenticate();

    // When the user submits their email and wants to create a passkey
    const handlePasskeySignup = (email: string) => {
      // Important: Validate the email before proceeding
      if (!isValidEmail(email)) {
        // Handle validation error
        return;
      }

      authenticate(
        {
          type: "passkey",
          email,
        },
        {
          onSuccess: () => {
            // Success - passkey created and user authenticated
          },
          onError: (error) => {
            // Handle error
          },
        },
      );
    };

    // Simple email validation function
    const isValidEmail = (email: string) => {
      return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
    };
    ```

    <Error>
      It's important that you validate the email before creating an account for the user. This is to prevent users from losing access to their wallets if they lose their device.
    </Error>

    ### Option 2: Passkey Signup without Email

    This approach creates a passkey without an associated email. Use this only if you have another recovery mechanism in place.

    ```tsx twoslash
    import { useAuthenticate } from "@account-kit/react";

    // Inside your component
    const { authenticate } = useAuthenticate();

    // When the user wants to create a passkey without email
    const handlePasskeyOnlySignup = (username: string) => {
      authenticate(
        {
          type: "passkey",
          createNew: true,
          username, // A unique identifier for the passkey
        },
        {
          onSuccess: () => {
            // Success - passkey created and user authenticated
          },
          onError: (error) => {
            // Handle error
          },
        },
      );
    };
    ```

    ### Step 3: Track Authentication Status

    Use the `useSignerStatus` hook to determine if the user is authenticated:

    ```tsx twoslash
    import { useSignerStatus } from "@account-kit/react";

    // Inside your component
    const { isConnected } = useSignerStatus();

    // You can use isConnected to conditionally render UI
    ```
  </Tab>

  <Tab title="React Native">
    <Info>
      This guide assumes you have already followed the [Setup
      Guide](/docs/wallets/react-native/signer/setup-guide) and have set up the
      Account Provider using this
      [guide](/docs/wallets/react-native/signer/authenticating-users/setting-up-the-accounts-provider).
      Refer to the guides above for more information on how to set up
      your project.
    </Info>

    <Tip>
      For a complete example of how to set up a project and use the various
      available authentication methods, refer to the [quickstart
      example](https://github.com/alchemyplatform/account-kit-expo-quickstart).
    </Tip>

    Authenticate a user using the `useAuthenticate()` hook from the `@account-kit/react-native` package. Before using that, configure your application to associate it with a domain you control.

    ## Step 1: Set an rpId in `createConfig`

    The rpId ("relaying party ID") specifies the domain on which passkeys are allowed to function. While passkeys on web applications are automatically associated with the website's domain, mobile applications must be registered with a domain to prove that they are associated.

    In your call to `createConfig`, pass an `rpId` parameter set to a domain you control. Note that the scheme is always assumed to be "https://" and should be omitted.

    ```typescript
    const config = createConfig({
      // ... other config
      rpId: "your-domain.com",
    });
    ```

    ## Step 2: Host a Site Association JSON

    While passkeys on web applications are automatically associated with the website's domain, mobile applications must be registered on that domain to prove that they are associated. To do so, you will need to host a JSON file referencing your app on your domain. The details of doing so differ on iOS and Android.

    ### iOS configuration

    [More information in Apple docs](https://developer.apple.com/documentation/xcode/supporting-associated-domains)

    On your webserver, set up the route

    ```json
    GET https://<yourdomain>/.well-known/apple-app-site-association
    ```

    This route should serve a static JSON object containing your team id and identifier. You should replace `<team-identifier>` and `<bundle-id>` in the below snippet, so it might appear as e.g. `H123456789.com.yourapp.passkeyExample`.

    ```JSON
    {
      "applinks": {},
      "webcredentials": {
        "apps": ["<team-identifier>.<bundle-id>"]
      },
      "appclips": {}
    }
    ```

    Next, in XCode under "Signing & Capabilities", add a new capability of type "Associated Domains". Now add the following, replacing `<yourdomain>` with the domain on which you hosted the JSON (e.g. `your-domain.com`):

    ```json
    webcredentials:<yourdomain>
    ```

    ### Android configuration

    [More information in Android docs](https://developer.android.com/identity/sign-in/credential-manager#add-support-dal)

    On your webserver, set up the route

    ```json
    GET https://<yourdomain>/.well-known/assetlinks.json
    ```

    This route should serve a static JSON object containing the following information:

    ```json
    [
      {
        "relation": ["delegate_permission/common.get_login_creds"],
        "target": {
          "namespace": "android_app",
          "package_name": "<your-package-name>",
          "sha256_cert_fingerprints": ["<sha-hex-value>"]
        }
      }
    ]
    ```

    You should replace `<your-package-name>` with the package name, e.g. `com.yourapp.passkeyExample`, and `"<sha-hex-value>"` with the SHA256 fingerprints of your app's \[signing certificate], e.g. `"FA:C6:17:45:DC:09:03:78:6F:B9:ED:E6:2A:96:2B:39:9F:73:48:F0:BB:6F:89:9B:83:32:12:75:91:03:3B:9C"`.

    ## Step 3: Add a Passkey

    You have the option of creating an account with passkey and email or with passkey alone.

    <Warning>
  If you create a passkey without an email associated with the user, you risk your users losing access to their wallets if they lose their device.

  **Recommended security practice:** Proxy authentication requests to your backend server to enforce additional security measures:

  * When a user attempts to sign up with both passkey and email, you can first require email verification before allowing the passkey to be created
  * Alternatively, you can restrict initial signup to email-based methods only (which inherently verify email ownership), then allow users to add passkeys after their account is established
  * This approach gives you greater control over the authentication flow and helps prevent account recovery issues

  By implementing server-side verification, you ensure that passkeys are only created for verified identities, reducing the risk of permanent access loss.
</Warning>


    ### Option 1: Creating an account with passkey and email (recommended)

    ```tsx twoslash create-passkey-and-email.tsx
    import { useAuthenticate } from "@account-kit/react-native";
    import React, { useState } from "react";
    import { Alert, View, Text, TextInput, Button, Pressable } from "react-native";

    function CreatePasskeyAndEmail() {
      const { authenticate } = useAuthenticate();
      const [email, setEmail] = useState("");

      const handleCreatePasskeyAndEmail = () => {
        // Important: Validate the email before proceeding
        if (!isValidEmail(email)) {
          // Handle validation error
          return;
        }
        try {
          authenticate({
            type: "passkey",
            email,
          });

          // Prompt the user to create a passkey, and create an account once they do.
        } catch (e) {
          Alert.alert("Error creating passkey. Check logs for more details.");

          console.log("Error creating passkey: ", e);
        }
      };

      return (
        <View>
          <Text>Enter Your Email to Create Account</Text>
          <View>
            <TextInput
              value={email}
              onChangeText={(val) => setEmail(val.toLowerCase())}
              placeholder="john@doe.com"
            />
            <Pressable onPress={handleCreatePasskeyAndEmail}>
              {({ pressed }) => (
                <View
                  style={[
                    {
                      opacity: pressed ? 0.5 : 1,
                      transform: [
                        {
                          scale: pressed ? 0.98 : 1,
                        },
                      ],
                    },
                  ]}
                >
                  <Text>Sign In</Text>
                </View>
              )}
            </Pressable>
          </View>
        </View>
      );
    }

    // Simple email validation function
    const isValidEmail = (email: string) => {
      return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
    };
    ```

    <Error>
      It's important that you validate the email before creating an account for the
      user. This is to prevent users from losing access to their wallets if they
      lose their device.
    </Error>

    ### Option 2: Creating a New Account

    To create an account with a passkey, use the `authenticate()` function, with the `type` set to `"passkey"` and `createNew` set to `true`.

    ```tsx twoslash create-passkey.tsx
    import { useAuthenticate } from "@account-kit/react-native";
    import React, { useState } from "react";
    import { Alert, View, Text, TextInput, Button, Pressable } from "react-native";

    function CreatePasskey() {
      const { authenticate } = useAuthenticate();

      const handleCreatePasskey = () => {
        try {
          authenticate({
            type: "passkey",
            createNew: true,
            // This will be the name of the saved passkey on the user's device.
            username: "Your App user",
          });
        } catch (e) {
          Alert.alert("Error creating passkey. Check logs for more details.");

          console.log("Error creating passkey: ", e);
        }
      };

      return (
        <View>
          <Text>Create an account with a passkey</Text>
          <View>
            <Pressable onPress={handleCreatePasskey}>
              {({ pressed }) => (
                <View
                  style={[
                    {
                      opacity: pressed ? 0.5 : 1,
                      transform: [
                        {
                          scale: pressed ? 0.98 : 1,
                        },
                      ],
                    },
                  ]}
                >
                  <Text>Create account</Text>
                </View>
              )}
            </Pressable>
          </View>
        </View>
      );
    }
    ```

    ## Step 4: Sign In with a Passkey

    To sign in with an existing passkey, use the `authenticate()` function, with the `type` set to `"passkey"`.

    ```tsx twoslash create-passkey.tsx
    import { useAuthenticate } from "@account-kit/react-native";
    import React, { useState } from "react";
    import { Alert, View, Text, TextInput, Button, Pressable } from "react-native";

    function SignInWithPasskey() {
      const { authenticate } = useAuthenticate();

      const handleSignIn = () => {
        try {
          authenticate({
            type: "passkey",
            createNew: false,
          });
        } catch (e) {
          Alert.alert(
            "Error signing in with passkey. Check logs for more details.",
          );
          console.log("Error signing in with passkey: ", e);
        }
      };

      return (
        <View>
          <Text>Sign in with passkey</Text>
          <View>
            <Pressable onPress={handleSignIn}>
              {({ pressed }) => (
                <View
                  style={[
                    {
                      opacity: pressed ? 0.5 : 1,
                      transform: [
                        {
                          scale: pressed ? 0.98 : 1,
                        },
                      ],
                    },
                  ]}
                >
                  <Text>Sign In</Text>
                </View>
              )}
            </Pressable>
          </View>
        </View>
      );
    }
    ```
  </Tab>

  <Tab title="JavaScript" language="typescript">
    ## Other JavaScript frameworks

    Create wallets for users using a passkey. This is useful if you don't want to go through the email OTP or magic link flow.

    ## Authenticate a user with email and passkey

    If you want to allow sign-up and login with a passkey, you can ask the user for an email to associate with their passkey. This way, they can log in with their email or passkey in the future. Under the hood, the email is also used to check if an account exists already so you can have a unified sign-up and login flow.

    <Error>
      It's important that you validate this email before creating an account for the user. This is to prevent users from losing access to their wallets if they lose their device.
    </Error>

    ```ts twoslash
    import { signer } from "./signer";

    const result = await signer.authenticate({
      type: "passkey",
      email: "user@mail.com",
    });
    ```

    <Tip>
      For setting up an account config, see the [authentication quickstart](/docs/wallets/signer/quickstart).
    </Tip>
  </Tab>
</Tabs>