payload-better-auth
Better Auth as Payload’s authentication, stored in your Payload database.
Better Auth, stored in Payload.
payload-better-auth makes Better Auth Payload's authentication: auth records live in your Payload collections, and every Payload request is authenticated by a Better Auth session. One database and one admin for your content and your users.
pnpm add @delmaredigital/payload-better-authStart with your existing Payload app.
These files show the integration points. Follow the installation guide for peers and the full documentation for the remaining application setup.
import type { BetterAuthOptions } from 'better-auth'
export const betterAuthOptions: Partial<BetterAuthOptions> = {
user: {
additionalFields: {
role: { type: 'string', defaultValue: 'user', input: false },
},
},
emailAndPassword: { enabled: true },
}import { buildConfig } from 'payload'
import { postgresAdapter } from '@payloadcms/db-postgres'
import { betterAuth } from 'better-auth'
import {
betterAuthCollections, createBetterAuthPlugin, payloadAdapter,
} from '@delmaredigital/payload-better-auth'
import { betterAuthOptions } from './lib/auth/config'
import { Users } from './collections/Users'
const baseURL = process.env.NEXT_PUBLIC_SERVER_URL!
export default buildConfig({
collections: [Users], // Define the Users collection as shown in the docs.
plugins: [
betterAuthCollections({ betterAuthOptions, skipCollections: ['user'] }),
createBetterAuthPlugin({
createAuth: (payload) => betterAuth({
...betterAuthOptions,
database: payloadAdapter({ payloadClient: payload }),
advanced: { database: { generateId: 'serial' } },
baseURL,
secret: process.env.BETTER_AUTH_SECRET,
trustedOrigins: [baseURL],
}),
}),
],
db: postgresAdapter({ pool: { connectionString: process.env.DATABASE_URL } }),
})'use client'
import { createAuthClient } from '@delmaredigital/payload-better-auth/client'
export const authClient = createAuthClient()
export const { useSession, signIn, signUp, signOut } = authClientWhat it does
- Collections from your Better Auth setup
- betterAuthCollections builds Payload collections from the Better Auth schema you have installed, including every Better Auth plugin's models.
- A login that matches your config
- The admin login shows password, magic link, email code, social, OIDC, passkey and two-factor sign-in, depending on what you turn on in Better Auth.
- Safe single-use tokens
- On Postgres, verification tokens and counters are consumed with guarded SQL, so a token can't be used twice under concurrent requests (atomicWrites).
- Secrets stay secret
- Secret fields are locked out of REST, GraphQL and the admin by default (secureSecretFields).
- Access rules you can read
- Role and API-key-scope helpers for Payload access control, with a configurable role field.
- Sessions that stay fresh
- Sliding sessions: the refreshed cookie is forwarded on Payload REST and GraphQL requests.
When to use it
- Your frontend members and Payload editors should share an authentication system.
- You want Better Auth’s client and plugin ecosystem backed by your Payload database.
- You need explicit Payload collection access rules around user and account data.
Compatibility
| Payload / admin UI / Next adapter | >=3.90.0 <4 |
|---|---|
| Better Auth | >=1.7.7 <2 |
| Next.js | >=16.4.1 <17 |
| React | >=19.2.1 <20 |
| Node.js | >=22.23.4 |
| Payload 4 beta | Separate @next release; see the docs for its requirements. |
| Databases | Postgres, SQLite and MongoDB |
| Tested against upcoming releases | Daily CI runs against Better Auth canaries and Payload's Drizzle pin, and opens an issue on drift. |
Questions before you install
Do I still define a Users collection?
Yes. The documented setup supplies a Users collection, disables the local strategy, adds betterAuthStrategy(), and skips the generated user collection. Each additional Better Auth field needs a matching Payload field.
Does a server component refresh the session?
No. getServerSession() and server-component reads do not refresh the cookie. REST and GraphQL requests can forward refreshed session cookies.
Can I change the API route?
Yes. Better Auth’s basePath must match Payload’s routes.api plus the plugin’s authBasePath. The admin components pick up routes.api automatically.
Can I try Payload 4?
The @next npm tag supports Payload 4 beta. Use the separate requirements and setup in the documentation; the default release targets Payload 3.