Email password sign up method
Call authClient.signUp.email with an object containing email, password (minimum 8 characters by default), name, optional image URL, and optional callbackURL. The method accepts callback options: onRequest, onSuccess, and onError.
Auto sign-in after email registration
By default, users are automatically signed in after successful sign up. This can be disabled by setting emailAndPassword.autoSignIn to false.
Email password sign in method
Call authClient.signIn.email with an object containing email, password, optional callbackURL to redirect after email verification, and rememberMe (boolean, defaults to true) to persist session after browser close.
Server-side email sign in method
To authenticate a user on the server, use auth.api.signInEmail with an object containing body (with email and password) and optional asResponse flag to return a response object instead of data.
Better Auth uses scrypt password hashing by default
Better Auth uses the scrypt algorithm to hash passwords by default. Since Auth0 uses bcrypt, you must configure Better Auth to use bcrypt for password verification if migrating bcrypt hashes. This requires installing the bcrypt package and overriding the hash and verify functions in the emailAndPassword password configuration.
Configure bcrypt hash and verify for Auth0 migration
To verify Auth0 bcrypt passwords in Better Auth, configure emailAndPassword with custom password hash and verify functions. Example: password: { hash: async (password) => { return await bcrypt.hash(password, 10); }, verify: async ({ hash, password }) => { return await bcrypt.compare(password, hash); } }
Enable emailAndPassword in Better Auth for Auth0 migration
To enable email and password authentication during Auth0 migration, set emailAndPassword: { enabled: true } in betterAuth config. Implement custom emailVerification.sendVerificationEmail with your own email sending logic.
Custom password hash and verify configuration for bcrypt
To configure Better Auth to use bcrypt for password verification during migration from Clerk, install bcrypt with `pnpm add bcrypt` and `pnpm add -D @types/bcrypt`, then configure the emailAndPassword option with custom password.hash and password.verify functions. The hash function calls `bcrypt.hash(password, 10)` and the verify function calls `bcrypt.compare(password, hash)` with parameters {hash, password}.
Email and password authentication options
For email and password authentication, configure emailAndPassword with enabled set to true, requireEmailVerification set to true, and minPasswordLength set to 10. Provide sendResetPassword and sendVerificationEmail callbacks. The emailVerification option separately requires a sendVerificationEmail callback.
Integration with emailAndPassword authentication
The emailAndPassword authentication method accepts a sendResetPassword callback that receives user and url objects. This callback can use sendEmail to send reset password emails by calling sendEmail with template "reset-password" and appropriate variables.
Integration with emailVerification
The emailVerification option has a sendVerificationEmail callback that receives user and url objects. Set sendOnSignUp to true to enable. This callback can use sendEmail to send verification emails by calling sendEmail with template "verify-email" and appropriate variables.
Replace Supabase Auth signIn with Better Auth
Replace Supabase Auth sign in call with Better Auth: Supabase's await supabase.auth.signInWithPassword({ email, password }) becomes await authClient.signIn.email({ email, password }) in Better Auth.
Enable email and password authentication
To enable email and password authentication, set the `emailAndPassword.enabled` option to `true` in the `auth` configuration.
Email and password sign-in endpoint
The `/sign-in/email` POST endpoint accepts: email (string, default "john.doe@example.com"), password (string, default "password1234", must be at least 8 characters long and max 128 by default), rememberMe (optional boolean, default true; if false, the user will be signed out when the browser is closed), callbackURL (optional string, default "https://example.com/callback"). Requires session.
Sign-out endpoint
The `/sign-out` POST endpoint takes no parameters and requires a session. Returns no result.
Email verification configuration
To enable email verification, pass a `sendVerificationEmail` function to the `emailVerification` configuration. The function receives a data object with properties: user (the user object), url (the URL to send to the user which contains the token), token (a verification token used to complete the email verification), and a request object as the second parameter. Avoid awaiting the email sending to prevent timing attacks; on serverless platforms use `waitUntil` or similar to ensure the email is sent.
On existing user sign-up callback
The `onExistingUserSignUp` callback is triggered when someone tries to register with an already-registered email address. It receives a data object with the user property and a request object as the second parameter. This callback allows you to notify the existing user of the sign-up attempt.
Email enumeration protection
When `requireEmailVerification` is enabled or `autoSignIn` is set to `false`, the sign-up endpoint prevents email enumeration by returning the same `200` response whether the email is already registered or not. This protection is only active when the sign-up response does not include a session token. The `/change-email` endpoint similarly always returns a success response without revealing whether the target email is already registered.
Custom synthetic user for email enumeration protection
When using plugins that add fields to the user table (such as admin, two-factor, or phone-number plugins), use the `customSyntheticUser` option to build the complete user object for email enumeration protection. The callback receives three building blocks: coreFields (name, email, emailVerified, image, createdAt, updatedAt), additionalFields (your user.additionalFields with defaults applied), and id (a generated user ID). Assemble them in the same order as your database schema: core fields → plugin fields → additional fields → id.
Manually trigger email verification
Call the `sendVerificationEmail` function on the client to manually trigger email verification. It takes an object with properties: email (the user's email), callbackURL (the redirect URL after verification).
Request password reset endpoint
The `/request-password-reset` POST endpoint accepts: email (string, default "john.doe@example.com"), redirectTo (optional string, default "https://example.com/reset-password"). If the token isn't valid or expired, the user is redirected with query parameter `?error=INVALID_TOKEN`. If the token is valid, the user is redirected with query parameter `?token=VALID_TOKEN`.
On password reset callback
The `onPasswordReset` callback is triggered after a user's password has been successfully reset. It receives a data object with the user property and a request object as the second parameter, allowing you to execute logic following a password reset.
Reset password endpoint
The `/reset-password` POST endpoint accepts: newPassword (string, default "password1234"), token (string, the token to reset the password). The token is obtained from the URL query parameter after the user clicks the reset link.
Revoke sessions on password reset
By default, other active sessions are not revoked when a user resets their password. To revoke all user sessions on password reset, set `emailAndPassword.revokeSessionsOnPasswordReset` to `true`.
Change password endpoint
The `/change-password` POST endpoint accepts: newPassword (string, default "newpassword1234"), currentPassword (string, default "oldpassword1234"), revokeOtherSessions (optional boolean, default true; when set to true, all other active sessions for this user will be invalidated). Requires session.
Password hashing algorithm
Better Auth uses `scrypt` to hash passwords by default. The `scrypt` algorithm is designed to be slow and memory-intensive to make it difficult for attackers to brute force passwords. OWASP recommends using `scrypt` if `argon2id` is not available. Scrypt is used because it is natively supported by Node.js.
Custom password hashing configuration
You can pass a custom password hashing algorithm by setting the `password` option in the `emailAndPassword` configuration. The `password` object accepts two functions: `hash` (custom password hashing function) and `verify` (custom password verification function).
Email and password configuration options reference
The emailAndPassword configuration accepts the following options: enabled (boolean, default false), disableSignUp (boolean, default false), minPasswordLength (number, default 8), maxPasswordLength (number, default 128), sendResetPassword (function), onPasswordReset (function), onExistingUserSignUp (function, default undefined), customSyntheticUser (function), autoSignIn (boolean, default true), requireEmailVerification (boolean, default false), revokeSessionsOnPasswordReset (boolean, default false), resetPasswordTokenExpiresIn (number, default 3600), password (object with hash and verify function properties).
Custom password hashing example with Argon2
```ts
import { hash, type Options, verify } from "@node-rs/argon2";
const opts: Options = {
memoryCost: 65536, // 64 MiB
timeCost: 3, // 3 iterations
parallelism: 4, // 4 lanes
outputLen: 32, // 32 bytes
algorithm: 2, // Argon2id
};
export async function hashPassword(password: string) {
const result = await hash(password, opts);
return result;
}
export async function verifyPassword(data: { password: string; hash: string }) {
const { password, hash } = data;
const result = await verify(hash, password, opts);
return result;
}
```
Then configure it in auth.ts:
```ts
import { betterAuth } from "better-auth";
import { hashPassword, verifyPassword } from "./password";
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
password: {
hash: hashPassword,
verify: verifyPassword,
},
},
});
```
Password storage location
A user's password is not stored in the user table. Instead, it is stored in the account table with `providerId` set to `credential`.
Sign out with redirect example
```ts
import { authClient } from "@/lib/auth-client"
await authClient.signOut({
fetchOptions: {
onSuccess: () => {
router.push("/login"); // redirect to login page
},
},
});
```
Require email verification example
```ts
export const auth = betterAuth({
emailAndPassword: {
requireEmailVerification: true,
},
});
```
On existing user sign-up callback example
```ts
import { betterAuth } from "better-auth";
import { sendEmail } from "./email"; // your email sending function
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
onExistingUserSignUp: async ({ user }, request) => {
void sendEmail({
to: user.email,
subject: "Sign-up attempt with your email",
text: "Someone tried to create an account using your email address. If this was you, try signing in instead. If not, you can safely ignore this email.",
});
},
},
});
```
Handle email verification error example
```ts
import { authClient } from "@/lib/auth-client"
await authClient.signIn.email(
{
email: "email@example.com",
password: "password",
},
{
onError: (ctx) => {
// Handle the error
if (ctx.error.status === 403) {
alert("Please verify your email address");
}
//you can also show the original error message
alert(ctx.error.message);
},
}
);
```
Custom synthetic user example with admin plugin
```ts
import { betterAuth } from "better-auth";
import { admin } from "better-auth/plugins";
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
customSyntheticUser: ({ coreFields, additionalFields, id }) => ({
...coreFields,
// Admin plugin fields (in schema order)
role: "user",
banned: false,
banReason: null,
banExpires: null,
// Your additional fields
...additionalFields,
// ID must be last to match database output order
id,
}),
},
plugins: [admin()],
});
```
Send verification email client example
```ts
import { authClient } from "@/lib/auth-client"
await authClient.sendVerificationEmail({
email: "user@email.com",
callbackURL: "/", // The redirect URL after verification
});
```
Reset password client example
```ts
import { authClient } from "@/lib/auth-client"
const { data, error } = await authClient.resetPassword({
newPassword: "password1234",
token,
});
```
Revoke sessions on password reset configuration
```ts
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
revokeSessionsOnPasswordReset: true,
sendResetPassword: async ({ user, url, token }, request) => {
// your email sending logic
},
},
});
```
Enable email and password configuration example
```ts
import { betterAuth } from "better-auth";
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
},
});
```