new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Better Auth · all subjects

authentication methods/email-password

39 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

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, }, }); ```

Give your agent this brain