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

# Add Passkeys to Stytch Login

> Set up Passkeys in Stytch Login using the Consumer frontend SDKs.

Passkeys let users sign in with device-bound credentials and biometrics instead of passwords. This guide shows how to configure Stytch Login and Passkey registration using the frontend SDK.

## Before you start

In order to complete this guide, you'll need the following:

* A Stytch Consumer Authentication project. If you don't have one already, in the [Dashboard](https://stytch.com/dashboard), click on your projects in the top nav, click **Create Project**, and then select **Consumer Authentication**.
* The public token for the **Test environment** you would like to use. These values can be accessed from the **Project ID & API keys** section of the Project Overview, or from the "Copy Public API Key" in the command bar (⌘-K / Ctrl-K). You'll need to pass the public token into the initialized SDK client.
* The SDK enabled. In the Dashboard, navigate to the [Frontend SDK](https://stytch.com/dashboard/sdk-configuration) page. Then click **Enable SDK in Test**.
* **WebAuthn** enabled in the SDK settings under **Authentication products**.
* A web app that serves HTML pages.

## Passkeys require a primary factor

In order to ensure account recovery, Passkeys can only be used for login and not signup. Stytch Users are required to have a verified email or phone from primary auth factors like [Magic Links](/consumer-auth/authentication/magic-links/adding-magic-links-to-custom-auth-flow), [OTP](/consumer-auth/authentication/otps/api), and [OAuth](/consumer-auth/authentication/oauth/overview) before they can register a Passkey with Stytch's API.

Additionally, Passkey registration requires the User to have authenticated with at least two factors if possible. Please refer to the [MFA SDK resource](/api-reference/consumer/frontend-sdks/vanilla-js/resources/multi-factor-authentication) for more details.

<Steps>
  <Step title="Install the frontend SDK">
    Stytch provides [frontend SDK options](/api-reference/consumer/frontend-sdks/vanilla-js/overview) for vanilla JavaScript, React, and Next.js. The essential steps to set up Passkeys are the same regardless of which SDK implementation you decide to use.

    In this guide, we'll use the vanilla JavaScript SDK.

    Install the [frontend SDK](/api-reference/consumer/frontend-sdks/vanilla-js/overview) locally via npm or yarn.

    ```bash theme={null}
    npm install @stytch/vanilla-js --save
    ```
  </Step>

  <Step title="Create a login.html page and initialize the Stytch Client">
    The next step is to create a login page with two elements:

    1. A `<script>` that initializes the Stytch Client. You'll need to pass in your Project's public token.
    2. A DOM element for the login UI component.

    ```html theme={null}
    <!-- login.html -->
    <script>
    // step 2
    import { StytchUIClient } from '@stytch/vanilla-js';
    const stytch = new StytchUIClient('PUBLIC_TOKEN');
    </script>

    <div id="login-form"></div>
    ```
  </Step>

  <Step title="Configure and mount the Login component">
    After the initializing the Stytch Client, create a config object with the necessary auth settings. Refer to the [UI Config](/api-reference/consumer/frontend-sdks/vanilla-js/prebuilt-ui/login/configuration) SDK reference for all available options and style customizations.

    Since Passkeys cannot be used for signup, include [One-Time Passcodes](/consumer-auth/authentication/otps/api) as an auth factor in the config object. Then mount the Login component by calling the stytch.mountLogin() method and passing in an object that has the config object and the DOM element id from **Step 2**.

    ```html theme={null}
    <!-- login.html -->
    <script>
    // step 2
    import { StytchUIClient } from '@stytch/vanilla-js';
    const stytch = new StytchUIClient('PUBLIC_TOKEN');

    // step 3
    const config = {
      otpOptions: {
        expirationMinutes: 10,
        methods: ['email', 'sms']
      },
      products: [
        'otp',
        'passkeys'
      ],
    };

    stytch.mountLogin({
      elementId: "#login-form",
      config,
    });
    </script>

    <div id="login-form"></div>
    ```
  </Step>

  <Step title="Add callbacks to redirect to the home.html page">
    With the login form working, create a callbacks object that redirects the user upon successful authentication to a logged-in location (like a home page, dashboard, or account page). You can wire these functions to trigger on the `PASSKEY_AUTHENTICATE` and `OTP_AUTHENTICATE` events.

    Refer to the [UI callbacks](/api-reference/consumer/frontend-sdks/react/prebuilt-ui/login/callbacks) SDK reference for a full list of events you can use for asynchronous operations.

    Once defined, pass the callbacks object into `stytch.mountLogin()` method.

    ```html theme={null}
    <!-- login.html -->
    <script>
    // step 2
    import { StytchUIClient } from '@stytch/vanilla-js';
    const stytch = new StytchUIClient('PUBLIC_TOKEN');

    // step 3
    const config = {
      otpOptions: {
        expirationMinutes: 10,
        methods: ['email', 'sms']
      },
      products: [
        'otp',
        'passkeys'
      ],
    };

    // step 4
    const callbacks = {
      onEvent: ({ type, data }) => {
        if (type === 'PASSKEY_AUTHENTICATE') {
          console.log("Passkey authenticated", data);
          window.location.replace("/home");
        } else if (type === 'OTP_AUTHENTICATE') {
          console.log("OTP authenticated", data);
          window.location.replace("/home");
        } else {
          console.log(type, data)
        }
      },
      onError: (err) => {
        console.log(err);
      }
    };

    stytch.mountLogin({
      elementId: "#login-form",
      config,
      callbacks
    });
    </script>

    <div id="login-form"></div>
    ```

    The `login.html` page should render a Login component UI. The email input field should trigger an **autofill** behavior for Passkey credentials when clicked on. Please make sure you're using a [supported browser](/get-started/overview).

    <img src="https://mintcdn.com/stytch-34ca0595/ujkFjtNfcCtlcK2E/images/consumer/from-old-docs/login-form.png?fit=max&auto=format&n=ujkFjtNfcCtlcK2E&q=85&s=a6036fd8399f61ea36c60b70d0be966f" alt="Passkeys diagram" width="580" height="378" data-path="images/consumer/from-old-docs/login-form.png" />

    <img src="https://mintcdn.com/stytch-34ca0595/ujkFjtNfcCtlcK2E/images/consumer/from-old-docs/passkey-autofill.png?fit=max&auto=format&n=ujkFjtNfcCtlcK2E&q=85&s=34f4491d418de484e24b698ab7698ae8" alt="Passkeys diagram" width="547" height="373" data-path="images/consumer/from-old-docs/passkey-autofill.png" />
  </Step>

  <Step title="Create a home.html page and mount the Passkey Registration component">
    With the `login.html` page redirecting to `home.html` upon successful authentication, the next step is to build a page where users can register Passkeys after signing up.

    Create a `home.html` page that initializes the Stytch Client and mounts the Passkey Registration component. The Passkey Registration component accepts all the same props as the Stytch Login component from **Steps 3 and 4**:

    * config object.
    * target DOM element id.
    * callbacks object.

    You'll need to also create a logout mechanism using the `stytch.session.revoke()` method.

    The `home.html` page should have the following structure:

    ```html theme={null}
    <!-- home.html -->
    <script>
    // step 5
    import { StytchUIClient } from '@stytch/vanilla-js';
    const stytch = new StytchUIClient('PUBLIC_TOKEN');

    const logout = () => {
      stytch.session.revoke();
    };

    const config = {
      otpOptions: {
        expirationMinutes: 10,
        methods: ['email', 'sms']
      },
      products: [
        'otp',
        'passkeys'
      ],
    };

    const callbacks = {
      onEvent: ({ type, data }) => {
        if (type === 'PASSKEY_REGISTER') {
          console.log("Passkey registered", data);
          window.location.replace("/home");
        } else {
          console.log(type, data)
        }
      },
      onError: (err) => {
        console.log(err);
      }
    };

    stytch.mountPasskeyRegistration({
      elementId: "#stytch-passkey-form",
      config,
      callbacks
    });

    </script>

    <div id="register-passkey-form"></div>

    <button onclick="logout()">Log out</button>
    ```

    The `home.html` page should render a Passkey Registration component with this UI:

    <img src="https://mintcdn.com/stytch-34ca0595/ujkFjtNfcCtlcK2E/images/consumer/from-old-docs/passkey-register.png?fit=max&auto=format&n=ujkFjtNfcCtlcK2E&q=85&s=ccbf6901b65ec545f8434c7536cf1565" alt="Passkeys diagram" width="1734" height="1124" data-path="images/consumer/from-old-docs/passkey-register.png" />
  </Step>

  <Step title="Sign up with Email OTP">
    At this point, the `login.html` and `home.html` should have fully functional Login and Passkey Registration components. Now we can test the end-to-end flow for signing up and logging in to the application with Passkeys.

    First, start on the `login.html` page and enter an email address to sign up with an Email OTP. You should receive an email with an OTP code. Complete the authentication flow and you should be redirected to `home.html` after receiving a 200 network response with a Session.

    <img src="https://mintcdn.com/stytch-34ca0595/ujkFjtNfcCtlcK2E/images/consumer/from-old-docs/passkey-otp-signup.png?fit=max&auto=format&n=ujkFjtNfcCtlcK2E&q=85&s=9c280e1069baaf2dafcbf50d46a1a482" alt="Passkeys diagram" width="1734" height="1124" data-path="images/consumer/from-old-docs/passkey-otp-signup.png" />
  </Step>

  <Step title="Register a Passkey">
    On the `home.html` page, click **Create a Passkey** on the Passkey Registration component. A browser dialog will appear with a prompt to create a Passkey with multiple options for storage such as:

    * Mobile devices
    * Tablets
    * Laptops
    * Security hardware like Yubikeys
    * iCloud
    * Chrome password manager
    * Windows Hello
    * 1Paswsword

    The available options are determined by the end user's specific setup of devices, operating systems, and browsers. For example, someone who has a MacBook laptop with Chrome browser and 1Password installed may see the following dialogs appear for registering Passkeys:

    <img src="https://mintcdn.com/stytch-34ca0595/ujkFjtNfcCtlcK2E/images/consumer/from-old-docs/passkey-dialog.png?fit=max&auto=format&n=ujkFjtNfcCtlcK2E&q=85&s=c7a3eac9ae77c84a6df5524cb0402b24" alt="Passkeys diagram" width="1734" height="1124" data-path="images/consumer/from-old-docs/passkey-dialog.png" />

    <img src="https://mintcdn.com/stytch-34ca0595/ujkFjtNfcCtlcK2E/images/consumer/from-old-docs/passkey-dialog-1pass.png?fit=max&auto=format&n=ujkFjtNfcCtlcK2E&q=85&s=c4799950e3e8a5a5d3192ff1820aa8a0" alt="Passkeys diagram" width="453" height="213" data-path="images/consumer/from-old-docs/passkey-dialog-1pass.png" />

    Select any option and follow all prompted steps to create a Passkey. The end result should be a 200 network response with a Session and the Passkey Registration component displaying a success message:

    <img src="https://mintcdn.com/stytch-34ca0595/ujkFjtNfcCtlcK2E/images/consumer/from-old-docs/passkey-created.png?fit=max&auto=format&n=ujkFjtNfcCtlcK2E&q=85&s=78450fde112d3f8a896f71b2675cc6c6" alt="Passkeys diagram" width="1734" height="1124" data-path="images/consumer/from-old-docs/passkey-created.png" />
  </Step>

  <Step title="Authenticate with your Passkey">
    Your User record should have a verified email from signing up with OTP in **Step 6** and a registered Passkey / WebAuthn factor from **Step 7**.

    ```js theme={null}
    {
      "user_id": "user-test-c872...",
      "status": "active",
      "created_at": "2023-11-21T17:05:05Z",
      "emails": [
        {
          "email": "example@email.com",
          "email_id": "email-test-8c...",
          "verified": true
        }
      ],
      "webauthn_registrations": [
        {
          "authenticator_type": "",
          "domain": "localhost",
          "name": "WebAuthN Registration 531ca878",
          "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
          "verified": true,
          "webauthn_registration_id": "webauthn-registration-test-a71f6..."
        }
      ],
      "email": "example@email.com",
      "phone_number": "",
      "name": {
        "first_name": "",
        "last_name": "",
        "middle_name": ""
      },
      "password": null,
      "phone_numbers": [],
      "providers": [],
      "totps": [],
      "trusted_metadata": {},
      "untrusted_metadata": {}
    }
    ```

    Log out of the `home.html` page and revoke the session with the `logout()` function created in **Step 5**.

    The final step is to log back in by clicking the email input field which should trigger the autofill and Passkeys browser dialog.

    Select the same Passkey you created in **Step 7** which will successfully authenticate into the application.
  </Step>
</Steps>

## What's next

Clone the [React](https://github.com/stytchauth/stytch-passkey-react-node-example-app) or [Next.js](https://github.com/stytchauth/stytch-passkey-nextjs-example-app) example app to get hands-on with a Passkeys-powered application.
