PromptShop

Email And Password Best Practices

This skill outlines the best practices for implementing email and password authentication with Better Auth

Install

npx promptshop add email-and-password-best-practices

Details

What This Skill Does

This skill outlines the best practices for implementing email and password authentication with Better Auth. It covers enabling email/password authentication, configuring email verification, handling password resets, and implementing client-side validation. This is useful for developers looking to securely manage user authentication with email and passwords.

When to Use

  • Enable email/password authentication.
  • Configure email verification flows.
  • Implement password reset functionality.
  • Require email verification for sign-in.
  • Implement client-side validation.
  • Use absolute URLs for callback URLs.

Key Features

  • Enables email/password authentication with a simple config.
  • Configures email verification with sendVerificationEmail.
  • Provides code examples for email verification.
  • Offers option to require email verification.
  • Suggests client-side validation for better UX.
  • Emphasizes using absolute URLs for callback URLs.

Quick Start

Enable email/password: emailAndPassword: { enabled: true } Configure emailVerification.sendVerificationEmail Add sendResetPassword for password reset flows Run npx @better-auth/cli@latest migrate Verify: attempt sign-up and confirm verification email triggers

Email Verification Setup

Configure emailVerification.sendVerificationEmail to verify user email addresses.

import { betterAuth } from "better-auth"; import { sendEmail } from "./email"; // your email sending function

export const auth = betterAuth({ emailVerification: { sendVerificationEmail: async ({ user, url, token }, request) => { await sendEmail({ to: user.email, subject: "Verify your email address", text: Click the link to verify your email: ${url}, }); }, }, });

Note: The url parameter contains the full verification link. The token is available if you need to build a custom verification URL.

Requiring Email Verification

For stricter security, enable emailAndPassword.requireEmailVerification to block sign-in until the user verifies their email. When enabled, unverified users will receive a new verification email on each sign-in attempt.

export const auth = betterAuth({ emailAndPassword: { requireEmailVerification: true, }, });

Note: This requires sendVerificationEmail to be configured and only applies to email/password sign-ins.

Client Side Validation

Implement client-side validation for immediate user feedback and reduced server load.

Callback URLs

Always use absolute URLs (including the origin) for callback URLs in sign-up and sign-in requests. This prevents Better Auth from needing to infer the origin, which can cause issues when your backend and frontend are on different domains.

const { data, error } = await authClient.signUp.email({ callbackURL: "https://example.com/callback", // absolute URL with origin });

Password Reset Flows

Provide sendResetPassword in the email and password config to enable password resets.

import { betterAuth } from "better-auth"; import { sendEmail } from "./email"; // your email sending function

export const auth = betterAuth({ emailAndPassword: { enabled: true, // Custom email sending function to send reset-password email sendResetPassword: async ({ user, url, token }, request) => { void sendEmail({ to: user.email, subject: "Reset your password", text: Click the link to reset your password: ${url}, }); }, // Optional event hook onPasswordReset: async ({ user }, request) => { // your logic here console.log(Password for user ${user.email} has been reset.); }, }, });

Security Considerations

Built-in protections: background email sending (timing attack prevention), dummy operations on invalid requests, constant response messages regardless of user existence.

On serverless platforms, configure a background task handler:

export const auth = betterAuth({ advanced: { backgroundTasks: { handler: (promise) => { // Use platform-specific methods like waitUntil waitUntil(promise); }, }, }, });

Token Security

Tokens expire after 1 hour by default. Configure with resetPasswordTokenExpiresIn (in seconds):

export const auth = betterAuth({ emailAndPassword: { enabled: true, resetPasswordTokenExpiresIn: 60 * 30, // 30 minutes }, });

Tokens are single-use — deleted immediately after successful reset.

Session Revocation

Enable revokeSessionsOnPasswordReset to invalidate all existing sessions on password reset:

export const auth = betterAuth({ emailAndPassword: { enabled: true, revokeSessionsOnPasswordReset: true, }, });

Password Requirements

Password length limits (configurable):

export const auth = betterAuth({ emailAndPassword: { enabled: true, minPasswordLength: 12, maxPasswordLength: 256, }, });

Sending the Password Reset

Call requestPasswordReset to send the reset link. Triggers the sendResetPassword function from your config.

const data = await auth.api.requestPasswordReset({ body: { email: "john.doe@example.com", // required redirectTo: "https://example.com/reset-password", }, });

Or authClient:

const { data, error } = await authClient.requestPasswordReset({ email: "john.doe@example.com", // required redirectTo: "https://example.com/reset-password", });

Note: While the email is required, we also recommend configuring the redirectTo for a smoother user experience.

Password Hashing

Default: scrypt (Node.js native, no external dependencies).

Custom Hashing Algorithm

To use Argon2id or another algorithm, provide custom hash and verify functions:

import { betterAuth } from "better-auth"; import { hash, verify, type Options } from "@node-rs/argon2";

const argon2Options: Options = { memoryCost: 65536, // 64 MiB timeCost: 3, // 3 iterations parallelism: 4, // 4 parallel lanes outputLen: 32, // 32 byte output algorithm: 2, // Argon2id variant };

export const auth = betterAuth({ emailAndPassword: { enabled: true, password: { hash: (password) => hash(password, argon2Options), verify: ({ password, hash: storedHash }) => verify(storedHash, password, argon2Options), }, }, });

Note: If you switch hashing algorithms on an existing system, users with passwords hashed using the old algorithm won't be able to sign in. Plan a migration strategy if needed.

Quick Start

Enable email/password: emailAndPassword: { enabled: true } Configure emailVerification.sendVerificationEmail Add sendResetPassword for password reset flows Run npx @better-auth/cli@latest migrate Verify: attempt sign-up and confirm verification email triggers

Email Verification Setup

Configure emailVerification.sendVerificationEmail to verify user email addresses.

import { betterAuth } from "better-auth"; import { sendEmail } from "./email"; // your email sending function

export const auth = betterAuth({ emailVerification: { sendVerificationEmail: async ({ user, url, token }, request) => { await sendEmail({ to: user.email, subject: "Verify your email address", text: Click the link to verify your email: ${url}, }); }, }, });

Note: The url parameter contains the full verification link. The token is available if you need to build a custom verification URL.

Requiring Email Verification

For stricter security, enable emailAndPassword.requireEmailVerification to block sign-in until the user verifies their email. When enabled, unverified users will receive a new verification email on each sign-in attempt.

export const auth = betterAuth({ emailAndPassword: { requireEmailVerification: true, }, });

Note: This requires sendVerificationEmail to be configured and only applies to email/password sign-ins.

Client Side Validation

Implement client