背景
#2766(PR #2771)接入了 better-auth phoneNumber 插件,但只开了手机号+密码登录:sendOTP 显式抛 NOT_SUPPORTED,因为平台目前没有任何短信发送能力——NotificationChannel 类型里声明了 'sms'(packages/spec/src/contracts/notification-service.ts:19),但 service-messaging 没有 sms channel 实现,也没有短信 provider 抽象。
无邮箱员工目前的密码找回只能靠管理员 set-user-password。OTP 首登/自助重置需要这块基建。
诉求
- 短信 provider 抽象与对接:参照 email channel 的模式(
packages/services/service-messaging/src/email-channel.ts,在 provider service 存在时注册 channel),定义 sender surface + 至少一个 provider 实现(阿里云短信 / Twilio,配置走 service-settings 模式)。
- messaging
sms channel:注册进 messaging-service-plugin.ts 的可插拔 channel 体系,notify(channels:['sms']) 可用。
- auth 侧接线:
auth-manager.ts 的 phoneNumber({ sendOTP }) 从抛错改为经 sms channel 发送,打开:
POST /phone-number/send-otp + /phone-number/verify(OTP 登录/验证)
POST /phone-number/request-password-reset + /phone-number/reset-password(自助重置)
- 导入联动:
/admin/import-users 的 invite 策略对有手机号的行支持"短信邀请"变体(无邮箱员工也能走首登自设密码路径)。
安全要求(必须与功能同 PR)
- OTP 端点限流 + 按号码冷却(SMS pumping 成本滥用是邮件渠道没有的攻击面);OTP 尝试次数上限(better-auth
allowedAttempts)。
- OTP/验证码绝不落日志。
- 占位邮箱用户(
placeholder.invalid)的短信路径与邮件拦截逻辑对齐。
相关
背景
#2766(PR #2771)接入了 better-auth
phoneNumber插件,但只开了手机号+密码登录:sendOTP显式抛NOT_SUPPORTED,因为平台目前没有任何短信发送能力——NotificationChannel类型里声明了'sms'(packages/spec/src/contracts/notification-service.ts:19),但service-messaging没有 sms channel 实现,也没有短信 provider 抽象。无邮箱员工目前的密码找回只能靠管理员
set-user-password。OTP 首登/自助重置需要这块基建。诉求
packages/services/service-messaging/src/email-channel.ts,在 provider service 存在时注册 channel),定义 sender surface + 至少一个 provider 实现(阿里云短信 / Twilio,配置走 service-settings 模式)。smschannel:注册进messaging-service-plugin.ts的可插拔 channel 体系,notify(channels:['sms'])可用。auth-manager.ts的phoneNumber({ sendOTP })从抛错改为经 sms channel 发送,打开:POST /phone-number/send-otp+/phone-number/verify(OTP 登录/验证)POST /phone-number/request-password-reset+/phone-number/reset-password(自助重置)/admin/import-users的invite策略对有手机号的行支持"短信邀请"变体(无邮箱员工也能走首登自设密码路径)。安全要求(必须与功能同 PR)
allowedAttempts)。placeholder.invalid)的短信路径与邮件拦截逻辑对齐。相关