email-and-password-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseEmail Verification Setup
邮箱验证设置
When enabling email/password authentication, configure to verify user email addresses. This helps prevent fake sign-ups and ensures users have access to the email they registered with.
emailVerification.sendVerificationEmailts
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 parameter contains the full verification link. The is available if you need to build a custom verification URL.
urltoken启用邮箱/密码认证时,请配置以验证用户邮箱地址。这有助于防止虚假注册,并确保用户能够访问其注册时使用的邮箱。
emailVerification.sendVerificationEmailts
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}`,
});
},
},
});注意:参数包含完整的验证链接。如果您需要构建自定义验证URL,可以使用参数。
urltokenRequiring Email Verification
强制邮箱验证
For stricter security, enable 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.
emailAndPassword.requireEmailVerificationts
export const auth = betterAuth({
emailAndPassword: {
requireEmailVerification: true,
},
});Note: This requires to be configured and only applies to email/password sign-ins.
sendVerificationEmail为提升安全性,可启用以阻止未验证邮箱的用户登录。启用后,未验证用户每次尝试登录时都会收到一封新的验证邮件。
emailAndPassword.requireEmailVerificationts
export const auth = betterAuth({
emailAndPassword: {
requireEmailVerification: true,
},
});注意:此配置要求已设置,且仅适用于邮箱/密码登录方式。
sendVerificationEmailClient side validation
客户端验证
While Better Auth validates inputs server-side, implementing client-side validation is still recommended for two key reasons:
- Improved UX: Users receive immediate feedback when inputs don't meet requirements, rather than waiting for a server round-trip.
- Reduced server load: Invalid requests are caught early, minimizing unnecessary network traffic to your auth server.
尽管Better Auth会在服务端验证输入,但仍建议实现客户端验证,主要有两个原因:
- 提升用户体验:当输入不符合要求时,用户可立即收到反馈,无需等待服务器往返响应。
- 降低服务器负载:提前拦截无效请求,减少发送至认证服务器的不必要网络流量。
Callback URLs
回调URL
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.
ts
const { data, error } = await authClient.signUp.email({
callbackURL: "https://example.com/callback", // absolute URL with origin
});在注册和登录请求中,回调URL请始终使用绝对URL(包含源地址)。这可避免Better Auth需要推断源地址,防止后端与前端位于不同域名时出现问题。
ts
const { data, error } = await authClient.signUp.email({
callbackURL: "https://example.com/callback", // absolute URL with origin
});Password Reset Flows
密码重置流程
Password reset flows are essential to any email/password system, we recommend setting this up.
To allow users to reset a password first you need to provide function to the email and password authenticator.
sendResetPasswordts
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.`);
},
},
});密码重置流程是任何邮箱/密码系统的必备功能,我们建议配置该功能。
要允许用户重置密码,首先需要为邮箱密码认证器提供函数。
sendResetPasswordts
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
安全注意事项
Better Auth implements several security measures in the password reset flow:
Better Auth在密码重置流程中实现了多项安全措施:
Timing attack prevention
防止时序攻击
- Background email sending: Better Auth uses internally to send reset emails without blocking the response. This prevents attackers from measuring response times to determine if an email exists.
runInBackgroundOrAwait - Dummy operations on invalid requests: When a user is not found, Better Auth still performs token generation and a database lookup (with a dummy value) to maintain consistent response times.
- Constant response message: The API always returns regardless of whether the user exists.
"If this email exists in our system, check your email for the reset link"
On serverless platforms, configure a background task handler to ensure emails are sent reliably:
ts
export const auth = betterAuth({
advanced: {
backgroundTasks: {
handler: (promise) => {
// Use platform-specific methods like waitUntil
waitUntil(promise);
},
},
},
});- 后台发送邮件:Better Auth内部使用发送重置邮件,不会阻塞响应。这可防止攻击者通过测量响应时间判断邮箱是否存在。
runInBackgroundOrAwait - 无效请求的虚拟操作:当未找到用户时,Better Auth仍会执行令牌生成和数据库查询(使用虚拟值),以保持一致的响应时间。
- 统一响应消息:无论用户是否存在,API始终返回。
"If this email exists in our system, check your email for the reset link"
在无服务器平台上,请配置后台任务处理程序以确保邮件可靠发送:
ts
export const auth = betterAuth({
advanced: {
backgroundTasks: {
handler: (promise) => {
// Use platform-specific methods like waitUntil
waitUntil(promise);
},
},
},
});Token security
令牌安全
- Cryptographically random tokens: Reset tokens are generated using , producing a 24-character alphanumeric string (a-z, A-Z, 0-9) with high entropy.
generateId(24) - Token expiration: Tokens expire after 1 hour by default. Configure with (in seconds):
resetPasswordTokenExpiresIn
ts
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
resetPasswordTokenExpiresIn: 60 * 30, // 30 minutes
},
});- Single-use tokens: Tokens are deleted immediately after successful password reset, preventing reuse.
- 加密随机令牌:重置令牌使用生成,产生一个24字符的字母数字字符串(a-z, A-Z, 0-9),具有高熵值。
generateId(24) - 令牌过期:令牌默认1小时后过期。可通过(单位:秒)配置:
resetPasswordTokenExpiresIn
ts
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
resetPasswordTokenExpiresIn: 60 * 30, // 30 minutes
},
});- 一次性令牌:成功重置密码后,令牌会立即删除,防止重复使用。
Session revocation
会话吊销
Enable to invalidate all existing sessions when a password is reset. This ensures that if an attacker has an active session, it will be terminated:
revokeSessionsOnPasswordResetts
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
revokeSessionsOnPasswordReset: true,
},
});启用可在密码重置时使所有现有会话失效。这确保如果攻击者拥有活跃会话,该会话将被终止:
revokeSessionsOnPasswordResetts
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
revokeSessionsOnPasswordReset: true,
},
});Redirect URL validation
重定向URL验证
The parameter is validated against your configuration to prevent open redirect attacks. Malicious redirect URLs will be rejected with a 403 error.
redirectTotrustedOriginsredirectTotrustedOriginsPassword requirements
密码要求
During password reset, the new password must meet length requirements:
- Minimum: 8 characters (default), configurable via
minPasswordLength - Maximum: 128 characters (default), configurable via
maxPasswordLength
ts
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
minPasswordLength: 12,
maxPasswordLength: 256,
},
});重置密码时,新密码必须满足长度要求:
- 最小值:8个字符(默认),可通过配置
minPasswordLength - 最大值:128个字符(默认),可通过配置
maxPasswordLength
ts
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
minPasswordLength: 12,
maxPasswordLength: 256,
},
});Sending the password reset
发送密码重置请求
Once the password reset configurations are set-up, you can now call the function to send reset password link to user. If the user exists, it will trigger the function you provided in the auth config.
requestPasswordResetsendResetPasswordts
const data = await auth.api.requestPasswordReset({
body: {
email: "john.doe@example.com", // required
redirectTo: "https://example.com/reset-password",
},
});Or authClient:
ts
const { data, error } = await authClient.requestPasswordReset({
email: "john.doe@example.com", // required
redirectTo: "https://example.com/reset-password",
});Note: While the is required, we also recommend configuring the for a smooother user experience.
emailredirectTo完成密码重置配置后,您可以调用函数向用户发送密码重置链接。如果用户存在,将触发您在认证配置中提供的函数。
requestPasswordResetsendResetPasswordts
const data = await auth.api.requestPasswordReset({
body: {
email: "john.doe@example.com", // required
redirectTo: "https://example.com/reset-password",
},
});或使用authClient:
ts
const { data, error } = await authClient.requestPasswordReset({
email: "john.doe@example.com", // required
redirectTo: "https://example.com/reset-password",
});注意:为必填参数,我们还建议配置以获得更流畅的用户体验。
emailredirectToPassword Hashing
密码哈希
Better Auth uses by default for password hashing. This is a solid choice because:
scrypt- It's designed to be slow and memory-intensive, making brute-force attacks costly
- It's natively supported by Node.js (no external dependencies)
- OWASP recommends it when Argon2id isn't available
Better Auth默认使用进行密码哈希。这是一个可靠的选择,原因如下:
scrypt- 设计为慢且占用大量内存的算法,使暴力破解攻击成本高昂
- 原生支持Node.js(无需外部依赖)
- 当Argon2id不可用时,OWASP推荐使用它
Custom Hashing Algorithm
自定义哈希算法
To use a different algorithm (e.g., Argon2id), provide custom and functions in the configuration:
hashverifyemailAndPassword.passwordts
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.
要使用其他算法(如Argon2id),请在配置中提供自定义的和函数:
emailAndPassword.passwordhashverifyts
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),
},
},
});注意:如果在现有系统中切换哈希算法,使用旧算法哈希密码的用户将无法登录。如有需要,请制定迁移策略。