> ## Documentation Index
> Fetch the complete documentation index at: https://auth0-feat-docs-5540.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# User Passkey Management

> Render a card-based UI that lets users enroll and revoke their own WebAuthn passkeys using the Auth0 My Account API.

export const ReleaseStageNotice = ({feature, stage, plans, contact, terms}) => {
  const stageTextMap = {
    "beta": "Beta",
    "ea": "Early Access"
  };
  const stageText = stageTextMap[stage] || "a product release stage";
  const prsLink = "/docs/troubleshoot/product-lifecycle/product-release-stages";
  const linkify = (text, url) => {
    return <a href={url} target="_blank" rel="noreferrer" class="link">{text}</a>;
  };
  const includeDetails = (plans, contact, terms) => {
    const hasDetails = terms || plans || contact;
    if (!hasDetails) return null;
    return <span data-as="p">
            {plans && <>This feature is available for {linkify(`${plans} plans`, "https://auth0.com/pricing")}. </>}
            {contact && "To participate, contact " + contact + ". "}
            {terms && <>By using this feature, you agree to the applicable Free Trial terms in Okta's {linkify("Master Subscription Agreement", "https://www.okta.com/legal")}.</>}
        </span>;
  };
  return <Warning>
            <span data-as="p">
                <strong>The {feature} feature is in {linkify(stageText, prsLink)}.</strong>
            </span>

            {includeDetails(plans, contact, terms)}
        </Warning>;
};

export const ComponentLoader = props => {
  const themePref = window?.localStorage?.getItem?.("isDarkMode");
  const theme = themePref === "dark" || themePref === "light" ? themePref : window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
  const lang = {
    i18n: {
      currentLanguage: props.lang || "en-US"
    }
  };
  return <div style={{
    minHeight: "400px",
    marginTop: "40px",
    background: theme === "light" ? "rgb(var(--gray-950)/.03)" : "rgb(255 255 255/.1)",
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    position: "relative",
    backgroundSize: "16px 16px",
    borderRadius: "10px",
    boxShadow: "0 1px 4px 0 rgba(16,30,54,0.04)",
    display: "flex",
    flexDirection: "column"
  }}>
      <div style={{
    minWidth: "320px",
    width: "96.5%",
    maxWidth: "1200px",
    margin: "12px 12px 0",
    background: theme === "light" ? "#ffffff" : "#101011",
    borderRadius: "10px",
    boxShadow: "0 2px 8px 0 rgba(16,30,54,0.04)",
    padding: "24px",
    minHeight: "400px"
  }} data-uc-component={props.componentSelector} data-uc-props={JSON.stringify(lang)}>
        <Spinner size={40} color="#8A94A6" style={{
    position: "absolute",
    top: "50%",
    left: "50%",
    transform: "translate(-50%, -50%)",
    zIndex: 1
  }} />
      </div>
      <div style={{
    width: "100%",
    textAlign: "center",
    color: theme === "light" ? "#6B7280" : "ffffff",
    fontSize: "12px",
    marginTop: "8px",
    marginBottom: "8px",
    letterSpacing: "0.01em",
    fontWeight: 400
  }}>
        {props.componentPreviewText}
      </div>
    </div>;
};

<ReleaseStageNotice feature="Auth0 Universal Components" stage="beta" terms="true" contact="Auth0 Support" />

The `UserPasskeyMgmt` component lets users enroll and revoke [passkeys](/docs/authenticate/database-connections/passkeys) in a single card-based interface using the [My Account API](/docs/manage-users/my-account-api) and requires no props to render.
The component renders a list of enrolled passkeys, a button to add a new passkey, and a revoke option.

## Prerequisites

To enable passkey support:

* **Configure a custom domain on your Auth0 tenant**. Passkeys require a [custom domain](/docs/customize/custom-domains).

* **Enable passkeys on your Auth0 database connection**. To learn how to enable passkeys in your Auth0 tenant, read [Configure Passkeys](/docs/authenticate/database-connections/passkeys/configure-passkey-policy#configure-passkeys).

* **Match origin**. The relying party id must equal your application’s domain or be a registrable parent of it. To learn more, read [Relying party ID for Passkeys](/docs/authenticate/database-connections/passkeys#relying-party-id-for-passkeys)

* **Ensure your application uses HTTPS**. WebAuthn requires that your application is served over HTTPS.

* **Install and configure universal components in your application**. To install and configure universal components, read [Build Account Security UI](/docs/get-started/universal-components/web/components/my-account-overview#prerequisites).

## Configure your application

Select your framework to configure environment variables and universal components.

<Tabs>
  <Tab title="React (SPA)">
    ## Install the component

    <CodeGroup>
      ```bash pnpm wrap lines theme={null}
      pnpm add @auth0/universal-components-react
      ```

      ```bash npm wrap lines theme={null}
      npm install @auth0/universal-components-react
      ```
    </CodeGroup>

    <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
      The install command also adds the `@auth0/universal-components-core` dependency for shared utilities and Auth0 integration.
    </Callout>

    ## Get started

    ```tsx wrap lines theme={null}
    import { UserPasskeyMgmt } from "@auth0/universal-components-react";

    export function SecurityPage() {
      return <UserPasskeyMgmt />;
    }
    ```

    <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
      * Components are always imported from the root entry `@auth0/universal-components-react`, regardless of framework.
      * Only the `Auth0ComponentProvider` component uses a framework-specific subpath: `/spa` for React applications, `/rwa` for Next.js applications.
    </Callout>

    <Accordion title="Full integration example">
      ```tsx wrap lines theme={null}
      import React from "react";
      import { UserPasskeyMgmt } from "@auth0/universal-components-react";
      import { Auth0Provider } from "@auth0/auth0-react";
      import { Auth0ComponentProvider } from "@auth0/universal-components-react/spa";
      import { analytics } from "./lib/analytics";

      function SecurityPage() {
        return (
          <div className="max-w-3xl mx-auto p-6">
            <UserPasskeyMgmt
              addAction={{
                onAfter: () => {
                  analytics.track("Passkey Enrolled");
                },
              }}
              revokeAction={{
                onBefore: async (passkey) =>
                  confirmDialog(`Remove passkey "${passkey.name}"?`),
                onAfter: (passkey) => {
                  analytics.track("Passkey Revoked", { passkeyId: passkey.id });
                },
              }}
              onErrorAction={(error, action) => {
                console.error(`Passkey ${action} failed:`, error.message);
              }}
              customMessages={{
                header: {
                  title: "Passkeys",
                  description: "Sign in faster and more securely without a password.",
                },
              }}
              styling={{
                variables: {
                  light: { "--color-primary": "#4f46e5" },
                  dark: { "--color-primary": "#818cf8" },
                },
              }}
            />
          </div>
        );
      }

      export default function App() {
        const domain = "YOUR_TENANT.auth0.com";
        const clientId = "YOUR_CLIENT_ID";

        return (
          <Auth0Provider
            domain={domain}
            clientId={clientId}
            authorizationParams={{ redirect_uri: window.location.origin }}
            interactiveErrorHandler="popup"
          >
            <Auth0ComponentProvider domain={domain}>
              <SecurityPage />
            </Auth0ComponentProvider>
          </Auth0Provider>
        );
      }
      ```
    </Accordion>
  </Tab>

  <Tab title="Next.js (RWA)">
    ## Install the component

    <CodeGroup>
      ```bash pnpm wrap lines theme={null}
      pnpm add @auth0/universal-components-react
      ```

      ```bash npm wrap lines theme={null}
      npm install @auth0/universal-components-react
      ```
    </CodeGroup>

    <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
      The install command also adds the `@auth0/universal-components-core` dependency for shared utilities and Auth0 integration.
    </Callout>

    ## Get started

    ```tsx app/security/passkeys/page.tsx wrap lines theme={null}
    "use client";

    import { UserPasskeyMgmt } from "@auth0/universal-components-react";

    export default function PasskeysPage() {
      return <UserPasskeyMgmt />;
    }
    ```

    <Accordion title="Full integration example">
      ```tsx app/security/passkeys/page.tsx wrap lines theme={null}
      "use client";

      import { UserPasskeyMgmt } from "@auth0/universal-components-react";
      import { analytics } from "@/lib/analytics";

      export default function PasskeysPage() {
        return (
          <div className="max-w-3xl mx-auto p-6">
            <UserPasskeyMgmt
              addAction={{
                onAfter: () => {
                  analytics.track("Passkey Enrolled");
                },
              }}
              revokeAction={{
                onAfter: (passkey) => {
                  analytics.track("Passkey Revoked", { passkeyId: passkey.id });
                },
              }}
              onErrorAction={(error, action) => {
                console.error(`Passkey ${action} failed:`, error.message);
              }}
              customMessages={{
                header: {
                  title: "Passkeys",
                  description: "Sign in faster and more securely without a password.",
                },
              }}
              styling={{
                variables: {
                  light: { "--color-primary": "#4f46e5" },
                  dark: { "--color-primary": "#818cf8" },
                },
              }}
            />
          </div>
        );
      }
      ```
    </Accordion>
  </Tab>

  <Tab title="shadcn">
    ## Install the component

    ```bash wrap lines theme={null}
    npx shadcn@latest add https://auth0-universal-components.vercel.app/r/my-account/user-passkey-management.json
    ```

    <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
      The install command adds the component source into `src/components/auth0/my-account/` along with all UI dependencies and the `@auth0/universal-components-core` dependency for shared utilities and Auth0 integration.
    </Callout>

    ## Get started

    ```tsx wrap lines theme={null}
    import { UserPasskeyMgmt } from "@/components/auth0/my-account/user-passkey-management";

    export function SecurityPage() {
      return <UserPasskeyMgmt />;
    }
    ```

    <Accordion title="Full integration example">
      ```tsx wrap lines theme={null}
      import React from "react";
      import { UserPasskeyMgmt } from "@/components/auth0/my-account/user-passkey-management";
      import { Auth0Provider } from "@auth0/auth0-react";
      import { Auth0ComponentProvider } from "@auth0/universal-components-react/spa";
      import { analytics } from "./lib/analytics";

      function SecurityPage() {
        return (
          <div className="max-w-3xl mx-auto p-6">
            <UserPasskeyMgmt
              addAction={{
                onAfter: () => {
                  analytics.track("Passkey Enrolled");
                },
              }}
              revokeAction={{
                onBefore: async (passkey) =>
                  confirmDialog(`Remove passkey "${passkey.name}"?`),
                onAfter: (passkey) => {
                  analytics.track("Passkey Revoked", { passkeyId: passkey.id });
                },
              }}
              onErrorAction={(error, action) => {
                console.error(`Passkey ${action} failed:`, error.message);
              }}
              customMessages={{
                header: {
                  title: "Passkeys",
                  description: "Sign in faster and more securely without a password.",
                },
              }}
              styling={{
                variables: {
                  light: { "--color-primary": "#4f46e5" },
                  dark: { "--color-primary": "#818cf8" },
                },
              }}
            />
          </div>
        );
      }

      export default function App() {
        const domain = "YOUR_TENANT.auth0.com";
        const clientId = "YOUR_CLIENT_ID";

        return (
          <Auth0Provider
            domain={domain}
            clientId={clientId}
            authorizationParams={{ redirect_uri: window.location.origin }}
            interactiveErrorHandler="popup"
          >
            <Auth0ComponentProvider domain={domain}>
              <SecurityPage />
            </Auth0ComponentProvider>
          </Auth0Provider>
        );
      }
      ```
    </Accordion>
  </Tab>
</Tabs>

## Props

### Display props

Display props control how the component renders without affecting its behavior.

<table class="table">
  <thead>
    <tr>
      <th>Prop</th>
      <th>Type</th>
      <th>Default</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td><code>hideHeader</code></td>
      <td><code>boolean</code></td>
      <td><code>false</code></td>
      <td>Hide the page-level header (title and description). The section card with the passkey list is always shown.</td>
    </tr>
  </tbody>
</table>

### Action props

Action props let you hook into the component’s lifecycle events and trigger or cancel operations.

<table class="table">
  <thead>
    <tr>
      <th>Prop</th>
      <th>Type</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td><code>addAction</code></td>
      <td><code>ComponentAction\<void></code></td>
      <td>Lifecycle hooks for the add-passkey flow. Set <code>disabled: true</code> to hide the add button.</td>
    </tr>

    <tr>
      <td><code>revokeAction</code></td>
      <td><code>ComponentAction\<Passkey></code></td>
      <td>Lifecycle hooks for the revoke-passkey flow. Set <code>disabled: true</code> to hide the revoke option.</td>
    </tr>

    <tr>
      <td><code>onFetch</code></td>
      <td><code>() => void</code></td>
      <td>Triggered after the passkey list is successfully loaded.</td>
    </tr>

    <tr>
      <td><code>onErrorAction</code></td>
      <td><code>(error: Error, <br />action: 'add' | 'revoke') => void</code></td>
      <td>Triggered when an add or revoke action fails.</td>
    </tr>
  </tbody>
</table>

**addAction**

Controls the add a passkey flow. `onBefore` triggers before the browser WebAuthn prompt is shown; return `false` to cancel (for example, to enforce a passkey limit). `onAfter` triggers after the new passkey is saved.

* `disabled` hide the "Add passkey" button.
* `onBefore()` runs before the WebAuthn enrollment ceremony. Return `false` to cancel.
* `onAfter()` runs after the passkey is successfully registered. Use this to refresh session state or send analytics.

```tsx wrap lines theme={null}
<UserPasskeyMgmt
  addAction={{
    onBefore: async () => {
      if (passkeys.length >= 5) {
        toast.error("You can register a maximum of 5 passkeys.");
        return false;
      }
      return true;
    },
    onAfter: () => {
      analytics.track("Passkey Enrolled");
    },
  }}
/>
```

**revokeAction**

Controls the revoke a passkey flow. The built-in confirmation modal is shown; `onBefore` runs after the user confirms the modal but before the API call, so you can still cancel at that point. `onAfter` triggers after the passkey is deleted from the account.

* `disabled` hides the revoke option from the passkey actions menu.
* `onBefore(passkey)` runs before the revoke API call. Receives the `Passkey` object. Return `false` to cancel.
* `onAfter(passkey)` runs after the passkey is successfully revoked. Receives the revoked `Passkey` object.

```tsx wrap lines theme={null}
<UserPasskeyMgmt
  revokeAction={{
    onAfter: (passkey) => {
      auditLog.record({ action: "passkey_revoked", passkeyId: passkey.id });
    },
  }}
/>
```

**onFetch**

Triggers after the passkey list is successfully loaded on mount. Use this to show or hide adjacent UI that depends on whether the user has any registered passkeys.

```tsx wrap lines theme={null}
<UserPasskeyMgmt
  onFetch={() => {
    setPasskeysLoaded(true);
  }}
/>
```

**onErrorAction**

Triggers when an add or revoke action fails. The `action` parameter is `'add'` or `'revoke'`. Use this to surface errors in your own toast system or error logging service.

```tsx wrap lines theme={null}
<UserPasskeyMgmt
  onErrorAction={(error, action) => {
    console.error(`Passkey ${action} failed:`, error.message);
    toast.error(`Something went wrong while trying to ${action} your passkey.`);
  }}
/>
```

To render the list read-only, set `disabled: true` on both actions:

```tsx wrap lines theme={null}
<UserPasskeyMgmt
  addAction={{ disabled: true }}
  revokeAction={{ disabled: true }}
/>
```

### Customize props

Customization props let you adapt copy and styling without modifying source code.

<table class="table">
  <thead>
    <tr>
      <th>Prop</th>
      <th>Type</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td><code>customMessages</code></td>
      <td><code>Partial\<PasskeyMessages></code></td>
      <td>Override default UI text and translations.</td>
    </tr>

    <tr>
      <td><code>styling</code></td>
      <td><code>ComponentStyling\<UserPasskeyMgmtClasses></code></td>
      <td>CSS variables and class overrides.</td>
    </tr>
  </tbody>
</table>

**customMessages**

Customize all text and translations. Every field is optional.

<Accordion title="Available Messages">
  * **header**—`title`, `description` (page-level header; hidden when `hideHeader` is `true`)
  * **Top-level card**—`section_title`, `enabled` (badge shown when passkeys are enrolled), `no_passkeys` (empty state message), `add_passkey` (add button label)
  * **List items**—`created_at` (use `${date}` as placeholder), `last_used` (use `${date}` as placeholder)
  * **Actions**—`actions.revoke` (label in the per-passkey actions menu)
  * **Success toasts**—`success.add`, `success.revoke`
  * **Revoke modal**—`modals.revoke.title`, `modals.revoke.consent` (use `<bold>${name}</bold>` to bold the passkey name), `modals.revoke.cancel`, `modals.revoke.confirm`
</Accordion>

```tsx wrap lines theme={null}
<UserPasskeyMgmt
  customMessages={{
    header: {
      title: "Passkeys",
      description: "Sign in faster and more securely without a password.",
    },
    section_title: "Your passkeys",
    no_passkeys: "No passkeys registered yet.",
    add_passkey: "Add a passkey",
    created_at: "Added ${date}",
    last_used: "Last used ${date}",
    success: {
      add: "Passkey registered successfully.",
      revoke: "Passkey removed.",
    },
    modals: {
      revoke: {
        title: "Remove passkey?",
        consent: "This will permanently remove <bold>${name}</bold>.",
        cancel: "Cancel",
        confirm: "Remove",
      },
    },
  }}
/>
```

**Customize style**

Customize appearance with CSS variables and class overrides. Supports light/dark themes.

<Accordion title="Available Styling Options">
  **Variables**—CSS custom properties

  * `common` Applied to both themes
  * `light` Light mode only
  * `dark` Dark mode only

  **Class overrides**

  * `UserPasskeyMgmt-root` the outer card container wrapping the passkey list
  * `UserPasskeyMgmt-item` each individual passkey row card
  * `PasskeyActionModal-modalContent` the revoke confirmation modal content area
</Accordion>

```tsx wrap lines theme={null}
<UserPasskeyMgmt
  styling={{
    variables: {
      light: { "--color-primary": "#4f46e5" },
      dark: { "--color-primary": "#818cf8" },
    },
    classes: {
      "UserPasskeyMgmt-root": "rounded-2xl shadow-md",
      "UserPasskeyMgmt-item": "rounded-xl border border-gray-200",
      "PasskeyActionModal-modalContent": "max-w-sm",
    },
  }}
/>
```

## TypeScript definitions

```typescript wrap lines theme={null}
interface Passkey {
  id: string;
  name?: string;
  createdAt?: string;
  lastUsedAt?: string;
  deviceInfo?: string;
}

interface UserPasskeyMgmtClasses {
  "UserPasskeyMgmt-root"?: string;
  "UserPasskeyMgmt-item"?: string;
  "PasskeyActionModal-modalContent"?: string;
}

// ComponentAction provides before/after hooks and a disabled flag.
// Both hooks receive the same data type T.
interface ComponentAction<T, U = undefined> {
  disabled?: boolean;
  onBefore?: (data: T, extra?: U) => boolean | Promise<boolean>;
  onAfter?: (data: T, extra?: U) => void | Promise<void>;
}

interface UserPasskeyMgmtProps {
  hideHeader?: boolean;
  customMessages?: Partial<PasskeyMessages>;
  styling?: ComponentStyling<UserPasskeyMgmtClasses>;
  addAction?: ComponentAction<void>;
  revokeAction?: ComponentAction<Passkey>;
  onFetch?: () => void;
  onErrorAction?: (error: Error, action: "add" | "revoke") => void;
}
```

## Advanced customization

The `UserPasskeyMgmt` component is composed of a stateless view component and a hook. Import them individually to build custom workflows.

### Available subcomponents

For advanced use cases, you can import individual subcomponents to build custom interfaces.

<table class="table">
  <thead>
    <tr>
      <th>Subcomponent</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        <code>UserPasskeyMgmtView</code>
      </td>

      <td>Stateless view layer; bring your own data and handlers via `useUserPasskey`.</td>
    </tr>

    <tr>
      <td>
        <code>PasskeyActionModal</code>
      </td>

      <td>The revoke confirmation modal. Can be rendered standalone if you need a custom revoke trigger separate from the passkey list.</td>
    </tr>
  </tbody>
</table>

### Available hooks

<table class="table">
  <thead>
    <tr>
      <th>Hook</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        <code>useUserPasskey</code>
      </td>

      <td>Full data and interaction layer: passkey list query, enroll mutation, revoke mutation, modal state, and all event handlers.</td>
    </tr>
  </tbody>
</table>

## Learn more

<CardGroup cols={2}>
  <Card title="UserMFAMgmt" icon="shield" href="/docs/get-started/universal-components/web/components/user-mfa-management">
    Manage MFA factors (TOTP, SMS, email OTP, push, recovery codes) alongside passkeys.
  </Card>

  <Card title="Build a Self-Service Account Security Interface" icon="key" href="/docs/get-started/universal-components/web/components/my-account-overview">
    Overview, prerequisites, and framework setup for all My Account components.
  </Card>
</CardGroup>
