Dev guide手机短信登录 & 绑定功能说明
手机短信登录 & 绑定功能说明
手机短信登录 & 绑定功能说明
功能概述
本系统使用 Better Auth 原生 phoneNumber 插件 实现完整的手机短信验证功能,支持手机号登录和绑定两种场景。经过重构,我们现在采用标准化的实现方式,获得更好的安全性和可维护性。
架构决策
为什么选择 Better Auth 原生实现
Better Auth phoneNumber 插件的优势
- ✅ 原生会话管理:完美兼容 Better Auth 会话系统,无兼容性问题
- ✅ 内置安全特性:防暴力攻击、验证码过期管理、尝试次数限制
- ✅ 支持多种场景:登录、绑定、密码重置等完整流程
- ✅ 标准化实现:遵循 OAuth 和认证标准,减少维护负担
- ✅ 灵活配置:支持自定义验证器、过期时间、回调函数等
支持的复杂业务场景
- ✅ 手机号直接登录:通过
disableSession: false
创建会话 - ✅ 已登录用户绑定手机号:通过
updatePhoneNumber: true
+disableSession: true
- ✅ 微信登录后绑定流程:保持现有会话,更新手机号信息
- ✅ 完整的状态查询:通过 Better Auth session API 获取用户状态
API架构
Better Auth 原生端点
手机号验证功能通过 Better Auth 的标准端点提供:
1. 发送验证码
POST /api/auth/phone-number/send-otp
Content-Type: application/json
{
"phoneNumber": "+8613800138000"
}
2. 验证验证码
POST /api/auth/phone-number/verify
Content-Type: application/json
{
"phoneNumber": "+8613800138000",
"code": "123456",
"updatePhoneNumber": false, // true: 绑定模式, false: 登录模式
"disableSession": false // true: 不创建会话, false: 创建会话
}
统一API客户端
位置:/src/lib/auth/phone-api.ts
这是对 Better Auth 客户端的封装,保持向后兼容:
// 发送验证码 - 兼容原有接口
export async function sendPhoneOTP(
phoneNumber: string,
type: "LOGIN" | "REGISTRATION" = "LOGIN"
): Promise<{ error?: { message: string } }>
// 验证验证码 - 兼容原有接口
export async function verifyPhoneOTP(
phoneNumber: string,
code: string,
type: "LOGIN" | "REGISTRATION" = "LOGIN",
updatePhoneNumber = false
): Promise<{ data?: any; error?: { message: string } }>
// 获取验证状态 - 使用 Better Auth session
export async function getPhoneVerificationStatus()
内部实现映射:
LOGIN
模式 →updatePhoneNumber: false, disableSession: false
REGISTRATION
模式 →updatePhoneNumber: true, disableSession: true
服务端配置
Better Auth 插件配置
在 src/lib/auth/auth.ts
中的配置:
import { phoneNumber } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
// ... 其他插件
phoneNumber({
// SMS 发送集成腾讯云
sendOTP: async ({ phoneNumber, code }, request) => {
const { sendVerificationCodeSMS } = await import("@/lib/sms/tencent-sms");
const result = await sendVerificationCodeSMS(phoneNumber, code);
if (!result.success) {
throw new Error(result.message || "发送验证码失败");
}
},
// 密码重置 OTP 发送
sendPasswordResetOTP: async ({ phoneNumber, code }, request) => {
const { sendVerificationCodeSMS } = await import("@/lib/sms/tencent-sms");
const result = await sendVerificationCodeSMS(phoneNumber, code);
if (!result.success) {
throw new Error(result.message || "发送验证码失败");
}
},
// 支持手机号登录时自动注册
signUpOnVerification: {
getTempEmail: (phoneNumber) => `${phoneNumber.replace("+", "")}@sms.hackathonweekly.com`,
getTempName: (phoneNumber) => `用户${phoneNumber.slice(-4)}`,
},
// 手机号格式验证
phoneNumberValidator: async (phoneNumber) => {
const { validateFullPhoneNumber } = await import("@/lib/utils/phone-validation");
const validation = validateFullPhoneNumber(phoneNumber);
return validation.isValid;
},
// 安全配置
otpLength: 6, // 6位验证码
expiresIn: 300, // 5分钟过期
allowedAttempts: 3, // 最多3次尝试
// 验证成功回调
callbackOnVerification: async ({ phoneNumber, user }, request) => {
logger.info(`Phone number ${phoneNumber} verified for user ${user.id}`);
},
}),
],
});
客户端配置
在 src/lib/auth/client.ts
中:
import { phoneNumberClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [
// ... 其他插件
phoneNumberClient(), // 已经配置
],
});
业务场景支持
1. 手机号注册/登录
// 发送验证码
await sendPhoneOTP("+8613800138000", "LOGIN");
// 验证码验证 - 自动创建用户和会话
await verifyPhoneOTP("+8613800138000", "123456", "LOGIN");
2. 微信登录后绑定手机号
// 用户已通过微信登录,现在绑定手机号
await sendPhoneOTP("+8613800138000", "REGISTRATION");
// 验证成功后绑定到当前用户,不创建新会话
await verifyPhoneOTP("+8613800138000", "123456", "REGISTRATION");
3. 设置页面手机号管理
// 直接使用 Better Auth 客户端
await authClient.phoneNumber.sendOtp({ phoneNumber: "+8613800138000" });
await authClient.phoneNumber.verify({
phoneNumber: "+8613800138000",
code: "123456",
updatePhoneNumber: true,
disableSession: true
});
配置选项
手机号验证配置
在 src/config/index.ts
中提供了完整的手机号验证控制选项:
export const config = {
auth: {
// === 手机号验证设置 ===
// 是否强制用户验证手机号(默认不强制)
// 当为 true 时,通过 OAuth(微信等)登录的用户必须绑定手机号
requirePhoneVerification: false,
// 是否允许用户跳过手机号验证(仅在 requirePhoneVerification 为 true 时生效)
// 当为 true 时,会显示"暂时跳过"选项
allowSkipPhoneVerification: true,
// 是否在设置页面显示手机号绑定选项(即使未强制要求)
// 当为 false 时,将完全隐藏手机号绑定功能
enablePhoneBinding: true,
// 其他认证配置...
}
}
这些配置选项的工作方式与之前相同,但现在通过 Better Auth 的原生机制实现。
数据库模型
Better Auth phoneNumber 插件会自动添加以下字段到 User 表:
model User {
phoneNumber String? @unique
phoneNumberVerified Boolean? @default(false)
// 其他字段由 Better Auth 自动管理...
}
验证码信息存储在 Better Auth 的内部表中,无需手动管理。
安全特性
内置安全保护
- ✅ 防暴力攻击:最多3次验证尝试,超出后需重新获取验证码
- ✅ 验证码管理:6位数字,5分钟过期,使用后自动删除
- ✅ 会话安全:原生 Better Auth 会话管理,支持 CSRF 保护等
- ✅ 速率限制:通过腾讯云 SMS 服务的内置限制
Better Auth 安全优势
相比自定义实现,Better Auth 提供:
- 标准化的会话管理和 Cookie 设置
- 内置的 CSRF 保护
- 安全的密码哈希和会话令牌生成
- 规范的错误处理和状态管理
技术集成
腾讯云SMS服务
SMS 发送功能通过 Better Auth 插件配置集成:
# .env 配置
TENCENT_SMS_SECRET_ID=your_secret_id
TENCENT_SMS_SECRET_KEY=your_secret_key
TENCENT_SMS_REGION=ap-guangzhou
TENCENT_SMS_SDK_APP_ID=your_app_id
TENCENT_SMS_SIGN_NAME=your_sign_name
TENCENT_SMS_TEMPLATE_ID=your_template_id
⚠️ 腾讯云短信服务目前只支持
ap-guangzhou
区域,若使用其他区域会返回 “The action not support this region” 错误。
API 端点
所有手机号相关的 API 现在通过 Better Auth 统一处理:
POST /api/auth/phone-number/send-otp
- 发送验证码POST /api/auth/phone-number/verify
- 验证验证码POST /api/auth/phone-number/request-password-reset
- 请求密码重置POST /api/auth/phone-number/reset-password
- 重置密码
迁移说明
从自定义实现迁移
如果你之前使用的是自定义手机验证实现,迁移步骤:
- 保持前端兼容:
phone-api.ts
保持了相同的接口 - 删除自定义路由:不再需要
/api/auth/phone/*
路由 - 数据库兼容:Better Auth 使用相同的 User 表字段
- 会话自动迁移:Better Auth 会话与原有系统兼容
主要改进
- ❌ 会话创建问题解决:不再有会话兼容性问题
- ❌ 代码量减少:删除了约300行自定义代码
- ✅ 标准化:遵循认证行业标准
- ✅ 维护性提升:利用 Better Auth 的活跃维护
开发指南
添加新的验证场景
// 直接使用 Better Auth 客户端API
await authClient.phoneNumber.sendOtp({ phoneNumber });
await authClient.phoneNumber.verify({
phoneNumber,
code,
updatePhoneNumber: boolean,
disableSession: boolean
});
集成其他SMS服务
在 phoneNumber
插件配置中修改 sendOTP
函数:
phoneNumber({
sendOTP: async ({ phoneNumber, code }, request) => {
// 替换为其他SMS服务
await yourSMSProvider.send(phoneNumber, code);
},
})
测试建议
- 测试 Better Auth 原生端点:
/api/auth/phone-number/*
- 测试会话创建和管理
- 测试不同验证场景的参数组合
- 测试安全特性(尝试次数限制等)
未来优化方向
- 更细粒度的配置:利用 Better Auth 插件的更多配置选项
- 多因素认证:结合 Better Auth 的 twoFactor 插件
- 国际化SMS:扩展支持更多 SMS 服务提供商
- 监控集成:利用 Better Auth 的内置日志和监控