Rallly 邮件与密码认证实战指南:基于 Better Auth 的安全邮箱验证、密码重置与哈希方案
后端前端【免费下载链接】ralllyRallly is an open-source scheduling and collaboration tool designed to make organizing events and meetings easier.项目地址https://gitcode.com/gh_mirrors/ra/rallly点击查看免费下载导读本文以 Rallly 开源仓库中的.agents/skills/email-and-password-best-practices/SKILL.md为骨架完整讲解基于 Better Auth 实现邮件与密码认证的关键实践邮箱验证Email Verification、密码重置Password Reset、回调 URL、客户端校验与密码哈希。Rallly 的认证实现位于 apps/web/src/lib/auth.ts 与 apps/web/src/lib/auth-client.ts本文将以这份真实的 Better Auth 配置为主线逐项展开配置参数、安全原理与完整可运行的代码示例。读者读完可掌握一套可直接落地的邮箱密码认证安全基线。一、邮箱验证从防滥用到强制校验1.1 为什么需要邮箱验证启用邮箱验证的核心目的有两个防止虚假注册fake sign-ups以及确保用户拥有所注册的邮箱。当攻击者无法控制验证邮件到达的邮箱时垃圾账号、一次性邮箱滥用的门槛会显著提高。Rallly 在 apps/web/src/lib/auth.ts 中启用emailVerification并在 apps/web/src/features/auth/utils.ts 提供了isEmailBlocked与isTemporaryEmail两个辅助函数分别配合ALLOWED_EMAILS环境变量实现域名白名单以及内置临时邮箱域名列表过滤一次性邮箱这正是对防止虚假注册这一目标的仓库级印证。1.2 配置发送验证邮件Better Auth 通过emailVerification.sendVerificationEmail让开发者接管验证邮件的发送。该回调接收{ user, url, token }与request其中url是完整的验证链接token可在需要自定义验证 URL 时使用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参数包含完整的验证链接token仅在需要自行拼接自定义验证 URL 时使用。1.3 强制邮箱验证未验证禁止登录更严格的安全策略是启用emailAndPassword.requireEmailVerification在用户验证邮箱之前阻止其登录启用后每次未验证用户尝试登录时都会收到新的验证邮件export const auth betterAuth({ emailAndPassword: { requireEmailVerification: true, }, });注意该选项要求必须配置sendVerificationEmail且仅作用于 email/password 方式的登录。Rallly 实际将requireEmailVerification设为true见 apps/web/src/lib/auth.ts并且在 OTP 邮件验证插件上启用overrideDefaultEmailVerification: true与autoSignInAfterVerification: true把登录与注册统一收敛到同一条验证流程见 apps/web/src/lib/auth.ts进一步说明验证后自动登录的落地方式。二、客户端校验体验与负载的双重收益Better Auth 在服务端校验输入但客户端校验仍然被强烈推荐理由有二改善用户体验Improved UX用户能在输入不符合要求时立即获得反馈而不是等待一次服务端往返。降低服务器负载Reduced server load无效请求被尽早拦截减少发往认证服务器的无谓网络流量。Rallly 在客户端以 react-hook-form zod 做了同样的校验ForgotPasswordForm用z.email(...)校验邮箱格式apps/web/src/app/[locale]/(auth)/forgot-password/components/forgot-password-form.tsxResetPasswordForm用usePasswordValidationSchema()校验新密码apps/web/src/app/[locale]/(auth)/reset-password/components/reset-password-form.tsx。此外 Rallly 还提供了PasswordStrengthMeter强度指示条apps/web/src/features/auth/components/password-strength-meter.tsx借助 zxcvbn 计算 04 分强度并实时展示apps/web/src/features/auth/utils.ts要求强度达到good3 分才允许提交apps/web/src/features/auth/schema.ts。三、回调 URL始终使用带源站的绝对地址在注册与登录请求中回调 URL必须使用包含 origin 的绝对 URL。这避免 Better Auth 自行推断源站origin而前后端分属不同域名时推断很容易出错const { data, error } await authClient.signUp.email({ callbackURL: https://example.com/callback, // absolute URL with origin });Rallly 的思路与此一脉相承它在 apps/web/src/lib/auth.ts 用absoluteUrl(/api/better-auth)生成baseURL在 apps/web/src/lib/auth-client.ts 中客户端同样使用absoluteUrl(/api/better-auth)作为baseURL并配置trustedOrigins: [absoluteUrl()]apps/web/src/lib/auth.ts把信任的回调来源显式锁定为自身源站从根源上规避开放重定向。四、密码重置流程从回调到安全细节4.1 提供sendResetPassword函数要允许用户重置密码首先需要为 email/password 认证器提供sendResetPassword函数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.); }, }, });Rallly 的落地实现会进一步考虑国际化与品牌化sendResetPassword中按用户邮箱查语言偏好getUserLocaleByEmail再调用sendResetPasswordEmail模板发送含resetLink的邮件apps/web/src/lib/auth.ts邮件模板位于 packages/emails/src/templates/reset-password.tsx导出sendResetPasswordEmail函数。onPasswordReset钩子在 apps/web/src/lib/auth.ts 中被用作埋点事件password_reset的触发点。4.2 时序攻击防护Timing attack preventionBetter Auth 在密码重置流程内置了多项时序攻击防护后台发送邮件Background email sending内部通过runInBackgroundOrAwait发送重置邮件不阻塞响应使攻击者无法通过测量响应时间差来判断邮箱是否存在。无效请求的虚拟操作Dummy operations on invalid requests当用户不存在时Better Auth 仍会执行令牌生成和一次数据库查询使用虚拟值保持响应时间一致。恒定响应消息Constant response message无论用户是否存在API 恒定返回If this email exists in our system, check your email for the reset link。在 Serverless 平台上需要配置后台任务处理器background task handler确保邮件可靠发送export const auth betterAuth({ advanced: { backgroundTasks: { handler: (promise) { // Use platform-specific methods like waitUntil waitUntil(promise); }, }, }, });Rallly 的ForgotPasswordForm提交后无论结果如何都统一展示Check your email / If an account exists with this email address, weve sent a password reset link to your inbox文案apps/web/src/app/[locale]/(auth)/forgot-password/components/forgot-password-form.tsx与恒定响应消息的策略完全一致是时序攻击防护在前端交互层的呼应。4.3 令牌安全Token security密码学随机令牌重置令牌使用generateId(24)生成为 24 位字母数字字符串a-z、A-Z、0-9熵值高。令牌过期默认1 小时后过期可用resetPasswordTokenExpiresIn单位秒配置export const auth betterAuth({ emailAndPassword: { enabled: true, resetPasswordTokenExpiresIn: 60 * 30, // 30 minutes }, });一次性令牌密码重置成功后令牌立即被删除防止重复使用。Rallly 在 apps/web/src/app/[locale]/(auth)/reset-password/components/reset-password-form.tsx 中校验 URL 上的token参数缺失或过期时直接展示The password reset link is invalid or has expired. Please request a new one.的错误提示对应令牌过期与失效的前端处理。4.4 会话吊销Session revocation启用revokeSessionsOnPasswordReset可在密码被重置时吊销所有既有会话确保攻击者即使持有有效会话也会被立即终止export const auth betterAuth({ emailAndPassword: { enabled: true, revokeSessionsOnPasswordReset: true, }, });4.5 重定向 URL 校验Redirect URL validationredirectTo参数会与trustedOrigins配置进行校验防止开放重定向攻击open redirect attacks。恶意重定向 URL 将被拒绝并返回 403 错误。Rallly 在重置密码成功后调用validateRedirectUrl再决定跳转目标apps/web/src/app/[locale]/(auth)/reset-password/components/reset-password-form.tsx是这一安全点在客户端侧的二次保障。4.6 密码长度要求Password requirements重置密码时新密码必须满足长度要求最小长度默认 8 字符可用minPasswordLength配置最大长度默认 128 字符可用maxPasswordLength配置export const auth betterAuth({ emailAndPassword: { enabled: true, minPasswordLength: 12, maxPasswordLength: 256, }, });Rallly 在客户端用.max(128)与服务端保持一致apps/web/src/features/auth/schema.ts并且额外通过 zxcvbn 强度评分要求密码强度达到good级别——即在长度限制之上再叠加一层猜测难度约束。4.7 发起密码重置请求配置完成后即可调用requestPasswordReset向用户发送重置链接若用户存在将触发你配置的sendResetPassword函数const data await auth.api.requestPasswordReset({ body: { email: john.doeexample.com, // required redirectTo: https://example.com/reset-password, }, });或使用 authClientconst { data, error } await authClient.requestPasswordReset({ email: john.doeexample.com, // required redirectTo: https://example.com/reset-password, });注意email为必填同时建议配置redirectTo以获得更顺畅的用户体验。Rallly 的 Forgot Password 页面正是按此模式实现提交邮箱后调用authClient.requestPasswordReset({ email, redirectTo: /reset-password })apps/web/src/app/[locale]/(auth)/forgot-password/components/forgot-password-form.tsx随后由 apps/web/src/app/[locale]/(auth)/reset-password/page.tsx 承载新密码表单页该页面在EMAIL_LOGIN_ENABLED false时直接返回 404见同文件第 1820 行避免禁用邮件登录时残留入口。五、密码哈希默认 scrypt 与自定义算法Better Auth 默认使用scrypt进行密码哈希理由充分设计上速度慢且内存密集使暴力破解成本高昂Node.js 原生支持无需外部依赖在 Argon2id 不可用时OWASP 推荐使用 scrypt。5.1 切换自定义哈希算法如 Argon2id若需改用其他算法可在emailAndPassword.password配置中提供自定义hash与verify函数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), }, }, });注意如果在既有系统上切换哈希算法旧算法哈希的用户将无法登录需要提前规划迁移策略。六、在 Rallly 中的完整实践印证Rallly 的 apps/web/src/lib/auth.ts 是这份技能文档在真实项目中的完整落地范本可逐项对照emailVerification.autoSignInAfterVerification: true配合 OTP 插件统一登录/注册验证流程第 186188、224236 行emailAndPassword.requireEmailVerification: true强制邮箱验证后才可登录第 164 行emailAndPassword.disableSignUp: true账号只通过已验证的 OTP 创建、密码只能从已登录会话中设置避免他人预占邮箱第 165169 行sendResetPassword与onPasswordReset分别接管重置邮件发送与事后埋点第 170184 行trustedOrigins: [absoluteUrl()]与baseURL锁定回调与跳转源站第 62、699700 行会话层还配置了 60 天有效期、1 天滑动续期与 cookie 缓存第 683690 行相关常量定义在 apps/web/src/lib/auth-config.ts。这些配置与本文前述的每条最佳实践一一对应可作为集成 Better Auth 邮箱密码认证时可直接参考的实现蓝本。客户端侧的完整调用链ForgotPasswordForm→authClient.requestPasswordReset→ResetPasswordForm→authClient.resetPassword可从 apps/web/src/app/[locale]/(auth)/forgot-password/components/forgot-password-form.tsx 与 apps/web/src/app/[locale]/(auth)/reset-password/components/reset-password-form.tsx 中完整追溯。结语邮箱验证、客户端校验、绝对回调 URL、带多重安全措施时序攻击防护、一次性令牌、会话吊销、重定向校验的密码重置以及 scrypt/Argon2id 密码哈希共同构成了 Better Auth 邮件密码认证的安全基线。这些配置可直接照搬于任何基于 Better Auth 的 Node.js 应用而在 Rallly 仓库中它们已经过生产级打磨并配有完整的前后端实现可供研读参考。赞分享后端前端【免费下载链接】ralllyRallly is an open-source scheduling and collaboration tool designed to make organizing events and meetings easier.项目地址https://gitcode.com/gh_mirrors/ra/rallly点击查看免费下载相关推荐Wasp 邮箱认证Email Auth完整指南邮箱验证、密码重置与配置 API 详解v0.16Wasp 邮箱认证Email Auth完整指南邮箱验证、密码重置与配置 API 详解v0.16 本文面向使用 Wasp v0.16 构建全栈应用的开发后端前端Web框架编译器开发工具代码生成Wasp 邮件认证完全指南邮箱登录、验证与密码重置的声明式配置实战Wasp 邮件认证完全指南邮箱登录、验证与密码重置的声明式配置实战 本指南以 Wasp 0.18 官方文档 email.md https://link.gi后端前端Web框架编译器开发工具代码生成Wasp 邮箱认证完整实战指南从注册登录到邮件验证与密码重置Wasp 邮箱认证完整实战指南从注册登录到邮件验证与密码重置 本指南以 Wasp 全栈框架当前仓库 waspc https://link.gitcode.c后端前端Web框架编译器开发工具代码生成上一篇WinThumbsPreloader V2终极指南彻底告别Windows缩略图加载等待的完整解决方案下一篇Stride引擎网络功能初探多人游戏开发基础创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

AnyPS5方案:PS5桌面化改造的显示、外设、存储与串流全攻略

AnyPS5方案:PS5桌面化改造的显示、外设、存储与串流全攻略

先说清楚,“AnyPS5”不是什么官方项目,也不是哪个大厂出的工具。它是我过去大半年一直在反复打磨的一套桌面折腾方案,代号就这么起的:给PS5主机接上“任何”想接的东西——任何显示器、任何键鼠、任何声卡、任何方向盘&#xff0c…

2026/10/13 6:13:44 阅读更多 →
Hoppscotch 实战指南:免费开源 API 测试工具,发请求、管环境、团队协作一步到位

Hoppscotch 实战指南:免费开源 API 测试工具,发请求、管环境、团队协作一步到位

Hoppscotch 实战指南:免费开源 API 测试工具,发请求、管环境、团队协作一步到位 【免费下载链接】hoppscotch Open-Source API Development Ecosystem • https://hoppscotch.io • Offline, On-Prem & Cloud • Web, Desktop & CLI • Open-Sou…

2026/10/13 6:13:44 阅读更多 →
如何把 PS5 可执行文件移植到 Linux 和 Windows:AnyPS5 原生移植完整指南

如何把 PS5 可执行文件移植到 Linux 和 Windows:AnyPS5 原生移植完整指南

如何把 PS5 可执行文件移植到 Linux 和 Windows:AnyPS5 原生移植完整指南 【免费下载链接】AnyPS5 Tool for automatic PS5 executables porting to Linux and Windows 项目地址: https://gitcode.com/GitHub_Trending/an/AnyPS5 如果你装过 PS5 模拟器&…

2026/10/13 6:13:44 阅读更多 →

最新新闻

机器学习评估体系:从泛化误差到业务指标的完整实践

机器学习评估体系:从泛化误差到业务指标的完整实践

1. 这不是背书,是建模前的“体检清单”——为什么第2章决定你80%的模型成败“西瓜书”第二章《模型的评估与选择》,表面看是教你怎么算准确率、画个ROC曲线、调个交叉验证的k值,但实操中我带过几十个刚入门的项目组,发现一个扎心事…

2026/10/13 9:41:42 阅读更多 →
矩阵实编码遗传算法在电力系统机组组合优化中的应用

矩阵实编码遗传算法在电力系统机组组合优化中的应用

## 1. 项目背景与核心价值机组组合问题(Unit Commitment, UC)是电力系统调度中的经典优化难题,需要在满足负荷需求的前提下,确定各发电机组的启停状态和出力分配,使总运行成本最小化。传统数学规划方法(如混…

2026/10/13 9:41:42 阅读更多 →
电信机顶盒E900V22D硬件解析与性能优化指南

电信机顶盒E900V22D硬件解析与性能优化指南

1. 电信机顶盒E900V22D硬件解析作为一款主流运营商定制设备,E900V22D机顶盒的硬件架构直接决定了其性能表现和功能边界。拆解实机可见,这款设备采用高度集成化的主板设计,PCB板尺寸仅为128cm,却承载了完整的媒体处理功能模块。1.1…

2026/10/13 9:41:42 阅读更多 →
SQL中AVG()的四大陷阱:NULL、0值、粒度与语义

SQL中AVG()的四大陷阱:NULL、0值、粒度与语义

1. 项目概述:平均值不是“除一下”就完事的——SQL里最常被低估的统计陷阱“怎么算平均值?”刚接触SQL的新手常以为就是AVG()函数一贴,结果跑出来数据和Excel对不上,或者业务方突然质疑:“上个月人均消费3800&#xff…

2026/10/13 9:41:42 阅读更多 →
智慧港口整体解决方案:码头流程再造与自动化落地实战

智慧港口整体解决方案:码头流程再造与自动化落地实战

简介:《智慧港口整体解决方案》是一份面向港口管理部门、航运企业及智慧交通领域从业者的专题方案文档,聚焦如何借助新一代感知、传输与应用技术,实现对码头、船舶、货物、重大危险源及航管航运等要素的全面感知、有效传输与按需定制服务。方…

2026/10/13 9:41:42 阅读更多 →
艾里函数:临界过渡建模的核心数学工具

艾里函数:临界过渡建模的核心数学工具

1. 为什么艾里函数不是“空气里的函数”,而是物理建模的隐形骨架很多人第一次看到“Airy函数”这个名字,下意识会联想到“airy”(空气的、轻盈的),甚至以为是某种和气流或浮力相关的数学工具。我刚接触这个概念时也这么…

2026/10/13 9:40:38 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →