---
title: Use passkeys with Payload CMS through payload-passkey
url: https://roboin.io/article/en/2026/09/16/payload-passkey-brings-passkey-authentication-to-payload-cms/
description: I released payload-passkey, a plugin that lets you sign in to
  Payload CMS without a password by using a passkey. In this article, I cover
  the plugin’s features and setup process.
publishedDate: 2026-09-16T15:05:40.420Z
modifiedDate: 2026-09-16T15:05:40.420Z
thumbnail: https://roboin.io/api/og/en/2026/09/16/payload-passkey-brings-passkey-authentication-to-payload-cms/
thumbnailAlt: 記事のサムネイル画像
thumbnailCaption: N/A
---

# Use passkeys with Payload CMS through payload-passkey

I released [payload-passkey](https://github.com/Robot-Inventor/payload-passkey/), a plugin that lets you sign in to Payload CMS without a password by using a passkey. In this article, I cover the plugin’s features and setup process.

[https://github.com/Robot-Inventor/payload-passkey/](https://github.com/Robot-Inventor/payload-passkey/)

## Why I built it

Payload CMS is an excellent CMS, but its built-in authentication supports email addresses and passwords. If you want two-factor authentication or passkeys, you need a third-party plugin.

I had been using [Payload TOTP](https://github.com/GeorgeHulpoi/payload-totp), a plugin that adds TOTP-based two-factor authentication. After you enter your email address and password on the standard Payload CMS login screen, Payload TOTP takes you to another screen where you enter a one-time password.

Payload TOTP worked well for me, and I used it for some time. Over time, though, I grew tired of opening an authenticator app and entering a one-time password each time I logged in.

I started looking into passkeys. I could not find a Payload CMS plugin focused on passkeys, so Better Auth was the main option.

I had the following requirements:

- Keep Payload CMS’s existing authentication strategy for compatibility and visual consistency
- Work alongside Payload TOTP
- Use Payload CMS’s default authentication strategy for passwords, Payload TOTP for TOTP, and Better Auth for passkeys
- Allow login with either (email address AND password AND TOTP) OR a passkey

Several plugins let developers use Better Auth with Payload CMS, but none matched my use case:

- [@delmaredigital/payload-better-auth](https://github.com/delmaredigital/payload-better-auth/): Replaces the entire login screen
- [payload-auth](https://github.com/payload-auth/payload-auth): Had no documentation at the time
- [payload-auth-plugin](https://github.com/UPDOT/payload-auth-plugin): Appeared to support passkeys as an experimental feature, but [the relevant code was commented out](https://github.com/UPDOT/payload-auth-plugin/blob/3fd277251479bfaa5aa2140634d02d53b0b4f04c/src/client/signin.ts#L12), so the plugin did not support them

None of these plugins met my requirements.

I then found that @delmaredigital/payload-better-auth exports its Better Auth database adapter as a separate module. That gave me a way to wrap the adapter and build a plugin that met my requirements. I built payload-passkey around that approach.

## payload-passkey features

With payload-passkey, you can add a passkey login button to Payload CMS’s default login screen and manage passkeys from the account management screen. The plugin includes the following features:

- A passkey login button on Payload CMS’s default login screen
- Passkey management from the account management screen
- Automatic token refresh when you enable it in Payload CMS
- Compatibility with Payload TOTP
- [Passkey autofill](https://web.dev/articles/passkey-form-autofill) support
- i18n support
- A design that uses Payload CMS UI components and fits into the dashboard

After you enable payload-passkey, you will see a passkey login button on the login screen, as shown below.

![Screenshot showing a \[Login with a passkey\] button added to the bottom of the Payload CMS login screen](https://roboin.io/api/media/file/payload-passkey-login-screen.png)

You will also see a section for managing passkeys on the account management screen.

![Screenshot showing a passkey management section added to the Payload CMS account management screen](https://roboin.io/api/media/file/passkey-management-screen.png)

## How to use payload-passkey

First, install the package.

```bash
pnpm add payload-passkey
```

Next, set the `BETTER_AUTH_SECRET` environment variable to a random string with at least 32 characters.

```http
BETTER_AUTH_SECRET=your-secret-key-longer-than-32-characters
```

Add payload-passkey as a plugin in `payload.config.ts`. Set `userCollection` to the slug of the collection that stores your user data. Set `modelName` to the value of `userCollection` with the trailing `s` removed.

```typescript
import { buildConfig } from "payload";
import { payloadPasskey } from "payload-passkey";

const config = buildConfig({
    collections: [
        {
            slug: "users",
            auth: true,
            fields: [...],
        },
    ],
    plugins: [
        payloadPasskey({
            rpID: "your-domain.example.com",
            rpName: "Example App",
            origin: "https://your-domain.example.com",
            modelName: "user",
            userCollection: "users",
            baseURL: "https://your-domain.example.com",
            secret: process.env.BETTER_AUTH_SECRET,
            trustedOrigins: ["https://your-domain.example.com"],
            generateId: "serial",
            firstUserAdmin: true,
            enablePasskeyAutofill: true
        })
    ]
});

export default config;
```

If you want to use payload-passkey with Payload TOTP, configure the plugins as follows.

```typescript
import { buildConfig } from "payload";
import { payloadPasskey } from "payload-passkey";

const config = buildConfig({
    collections: [
        {
            slug: "users",
            auth: true,
            fields: [...],
        },
    ],
    plugins: [
        payloadPasskey({
            enableTotpCompatibility: true,
            rpID: "your-domain.example.com",
            rpName: "Example App",
            origin: "https://your-domain.example.com",
            modelName: "user",
            userCollection: "users",
            baseURL: "https://your-domain.example.com",
            secret: process.env.BETTER_AUTH_SECRET,
            trustedOrigins: ["https://your-domain.example.com"],
            generateId: "serial",
            firstUserAdmin: true,
            enablePasskeyAutofill: true
        }),
        // `payloadTotp()` must be called after `payloadPasskey()`;
        // otherwise, the collection generated by payload-passkey won't be protected by Payload TOTP.
        payloadTotp({
            collection: "users",
            totp: {
                issuer: "Example App"
            }
        })
    ]
});

export default config;
```

Run the database migrations, and you can start using passkeys with Payload CMS.

```bash
npm run payload migrate:create
npm run payload migrate
```

## Conclusion

I released payload-passkey so you can add passkey support while keeping Payload CMS’s default authentication strategy and compatibility with Payload TOTP.

You can find the source code on GitHub. If you find a bug, please open an issue or send a PR.

[https://github.com/Robot-Inventor/payload-passkey/](https://github.com/Robot-Inventor/payload-passkey/)