Email and password
Enable email/password sign-in, email verification and password resets.
Email and password authentication is enabled by default. If you set no
emailAndPassword block at all, the plugin turns it on so the admin panel has a way in.
Configuration
export const betterAuthOptions = {
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
autoSignIn: false,
minPasswordLength: 8,
async sendResetPassword({ user, url, token }) {
await sendEmail({
to: user.email,
subject: 'Reset your password',
html: `<a href="${url}">Reset your password</a>`,
})
},
},
} satisfies BetterAuthOptionsTo turn it off — a panel that only accepts social login or passkeys — set
enabled: false explicitly.
Email verification
export const betterAuthOptions = {
emailVerification: {
sendOnSignUp: true,
autoSignInAfterVerification: true,
async sendVerificationEmail({ user, url }) {
await sendEmail({
to: user.email,
subject: 'Verify your email',
html: `<a href="${url}">Verify your email</a>`,
})
},
},
} satisfies BetterAuthOptionsThe verification state lands in the emailVerified checkbox on the users collection, so
you can see and (as an admin) correct it from the panel.
Developing without an email transport
Log the URL instead of sending it and paste it into your browser. See Create your first admin.
Sending real email
Better Auth's callbacks are the place to send mail — they receive the recipient and the signed URL, and it is up to you how to deliver it. If you already configured Payload's email adapter, reuse it:
import { getPayload } from '@/lib/payload'
async function sendVerificationEmail({ user, url }) {
const payload = await getPayload()
await payload.sendEmail({
to: user.email,
subject: 'Verify your email',
html: `<a href="${url}">Verify your email</a>`,
})
}Password resets from the admin panel
/admin/forgot-password and /admin/reset-password are wired to Better Auth's reset flow.
The forgot-password form calls Better Auth, which invokes your
emailAndPassword.sendResetPassword callback. If you have not implemented that callback,
the form appears to succeed but no email is sent.
Changing a password
Users change their own password through the Better Auth client:
await authClient.changePassword({
currentPassword,
newPassword,
revokeOtherSessions: true,
})Through Payload's API instead, send both currentPassword and password — the plugin
verifies the current password before permitting the update. See
Roles and access control.
Related options
| Option | Effect |
|---|---|
requireEmailVerification | Block sign-in until the address is verified |
autoSignIn | Sign the user in immediately after sign-up |
account.accountLinking | Link a social account to an existing email/password user |
requireAdminInviteForSignUp | Require an invitation for all public sign-ups |
For the full set, see the Better Auth options reference.