Replaces default Payload authentication with Better Auth. A custom auth strategy maps Better Auth sessions to req.user so Payload access control keeps working. The full Better Auth config is passed through, with Payload-styled admin login, forgot and reset views. Supports MongoDB and SQLite, requires Next.js and a Payload email adapter for resets and OTP.
Replace Payload CMS auth with Better Auth — full config passthrough, admin 2FA, and Payload access control via req.user
Installation
npm i @heliconia/payload-better-auth1
Stars
0
Forks
0
Open Issues
1 months ago
Last Updated
0
Weekly Downloads
v
npm Version
Dependencies
Trends appear after 7 days of data. Collecting since Oct 22 — 2 daily snapshots so far.
Trends appear after 7 days of data. Daily snapshots have not started yet.
Each point is npm's rolling 7-day download count.
Health Score
Composite score based on GitHub activity, stars, npm downloads, publish recency, dependency count, and package size.
Badge for your README
[](https://payloaddirectory.dev/plugins/1266960418-root)<a href="https://payloaddirectory.dev/plugins/1266960418-root"><img src="https://payloaddirectory.dev/badge/1266960418-root.svg" alt="Payload Directory"></a>README
Payload CMS 3 plugin that replaces default Payload auth with Better Auth.
plugins, socialProviders, session, hooks, etc.)req.userforceAdmin2fa email OTP gate at /admin/verify-identitypnpm add @heliconia/payload-better-auth better-auth
Peer dependencies: payload, better-auth, next, @payloadcms/next.
PAYLOAD_SECRET=... # 32+ chars recommended
BETTER_AUTH_SECRET=... # or reuse PAYLOAD_SECRET
BETTER_AUTH_URL=http://localhost:3000
DATABASE_URL=...
# Optional
FORCE_ADMIN_2FA=true # admin OTP gate (also exposed to admin UI)
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
import { payloadBetterAuth } from '@heliconia/payload-better-auth'
import { admin } from 'better-auth/plugins'
export default buildConfig({
admin: { user: 'users' },
collections: [Users],
plugins: [
payloadBetterAuth({
usersCollection: 'users',
forceAdmin2fa: false,
rolesField: 'roles',
adminRoles: ['admin'],
disabled: false,
betterAuth: ({ payload }) => ({
emailAndPassword: { enabled: true },
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
plugins: [admin({ defaultRole: 'user', adminRoles: ['admin'] })],
}),
}),
],
})
betterAuth accepts an object or callback. The plugin injects database, secret, baseURL, and trustedOrigins — do not set those manually.
payloadBetterAuth({
disabled: true,
betterAuth: () => ({}),
})
Or set PLUGIN_DISABLED=true in the dev app. Payload local auth is restored. Email/password users who signed up via Better Auth can still log in (Payload hash/salt is synced on sign-up, sign-in, and reset). See docs/migration.md.
// app/api/auth/[...all]/route.ts
import { createAuthRouteHandlers } from '@heliconia/payload-better-auth'
import { getPayload } from 'payload'
import config from '@payload-config'
export const { GET, POST, PATCH, PUT, DELETE } = createAuthRouteHandlers(() =>
getPayload({ config }),
)
Use the bundled client in React/Next.js client components:
'use client'
import { authClient, requestPasswordReset } from '@heliconia/payload-better-auth/client'
const { error } = await authClient.signIn.email({
email: '[email protected]',
password: 'secret',
})
if (error) {
// handle error
}
await authClient.signUp.email({
email: '[email protected]',
password: 'secret',
name: 'Jane',
})
Configure socialProviders.github in betterAuth, then:
await authClient.signIn.social({
provider: 'github',
callbackURL: '/dashboard',
})
Ensure BETTER_AUTH_URL matches your app origin and OAuth redirect URLs are registered with the provider.
await requestPasswordReset({
email: '[email protected]',
redirectTo: `${window.location.origin}/auth/reset-password`,
})
On the reset page, read ?token= from the URL and call:
await authClient.resetPassword({
token,
newPassword: 'new-secret',
})
const { data } = await authClient.getSession()
const user = data?.user
await authClient.signOut()
import { createPayloadAuthClient } from '@heliconia/payload-better-auth/client'
export const authClient = createPayloadAuthClient({
basePath: '/api/auth',
})
access: {
read: ({ req }) => req.user?.roles?.includes('admin') ?? false,
}
req.user is populated by the Better Auth strategy when a valid session cookie is present.
When the plugin is enabled, these admin routes use Better Auth:
| Route | Behavior |
|---|---|
/admin/login | Better Auth email sign-in |
/admin/forgot | Sends Better Auth reset email |
/admin/reset?token=… | Better Auth password reset |
/admin/verify-identity | Email OTP when forceAdmin2fa: true |
Logout calls Better Auth signOut via an afterLogout hook.
forceAdmin2fa)When forceAdmin2fa: true, admin-role users must verify an email OTP at /admin/verify-identity before accessing the dashboard.
The plugin auto-injects emailOTP if missing. Provide sendVerificationOTP in production for real email delivery.
In Next.js, expose the flag to the admin UI:
// next.config.mjs
env: {
NEXT_PUBLIC_FORCE_ADMIN_2FA: process.env.FORCE_ADMIN_2FA ?? 'false',
},
The admin UI also reads config.admin.custom.payloadBetterAuth.forceAdmin2fa from the plugin.
Password reset and admin 2FA require a Payload email adapter (payload.sendEmail). Without one, reset emails are skipped (logged as warning).
The monorepo includes a dev Next.js app:
pnpm dev
| URL | Purpose |
|---|---|
/auth | Dev hub — links to all auth flows |
/auth/login | Frontend email + GitHub login |
/auth/signup | Frontend sign-up |
/auth/account | Session probe |
/auth/reset-password | Frontend reset flow |
/admin/login | Admin login |
/admin/forgot | Admin forgot password |
Copy dev/.env.example to dev/.env. Seeded admin: [email protected] / test-password.
pnpm test:int
pnpm test:e2e
FORCE_ADMIN_2FA=true pnpm test:e2e:2fa
pnpm build
MIT
payload auth
Replace Payload's built-in auth with Better Auth for a shared admin and frontend session
774.7k/wk344v3
payload better auth
Integrate Better Auth into Payload CMS 3 with generated collections, API endpoints and admin login
3474v3
payload better auth
Better Auth adapter and plugins that replace Payload's local auth with Better Auth sessions
861.6k/wk74v3
payload oauth2
Generic OAuth2 sign-in for Payload CMS with support for providers such as Google, Zitadel and Apple
7710.1k/wk198v3
payload authjs
Use Auth.js 5 sign-in providers to authenticate users in Payload CMS 3
721.5k/wk207v3
payload simple social login
Google and Microsoft social login for the Payload admin panel and auth collections
7118/wk3v3