EasyStarter logoEasyStarter
认证服务

阿里云手机号登录(适合中国大陆业务)

配置阿里云 Dypnsapi 短信验证码登录

阿里云手机号登录

EasyStarter 已经内置基于 Better Auth phone-number plugin 的手机号登录。服务端通过阿里云号码认证服务 Dypnsapi 发送和校验短信验证码,客户端继续使用 Better Auth 的 phoneNumber.sendOtp 与 phoneNumber.verify,不需要额外新增自定义认证接口。

如果你的产品主要在国内部署,建议把手机号登录作为首选甚至唯一登录方式。国内用户对手机号验证码登录的接受度最高,GitHub / Google / Apple 这类 OAuth 登录在国内访问稳定性、账号覆盖率和合规配置上都更麻烦;邮箱密码登录也可以保留,但不是必须。也就是说,面向国内场景时,只配置阿里云手机号登录即可,其它登录方式都可以不启用、不申请、不写环境变量。

当前内置流程只支持中国大陆手机号:

项目当前配置
手机号格式+86 E.164 格式,例如 +8613800138000
发送接口SendSmsVerifyCode
校验接口CheckSmsVerifyCode
服务端 Providerapps/server/src/sms/providers/aliyun.ts
Better Auth 配置apps/server/src/lib/auth.ts

所需环境变量

ALIBABA_CLOUD_ACCESS_KEY_ID=
ALIBABA_CLOUD_ACCESS_KEY_SECRET=

这两个值是服务端调用阿里云 OpenAPI 的长期访问凭证。不要提交到 Git,也不要放到前端环境变量中。

如果你只保留手机号登录,GITHUB_CLIENT_ID、GITHUB_CLIENT_SECRET、GOOGLE_CLIENT_ID、GOOGLE_CLIENT_SECRET 等 OAuth 变量可以不配置。生产环境只需要保留 Better Auth 会话所需的基础变量和这里的阿里云 AccessKey。

开通号码认证服务

先确认阿里云账号已经开通号码认证服务,并且账号可调用 Dypnsapi 的短信认证接口。

官方接口文档:SendSmsVerifyCode

阿里云文档中说明,SendSmsVerifyCode 是号码认证服务的短信验证码发送接口。它使用 Dypnsapi 产品下的 2017-05-25 API 版本,并且授权 Action 为 dypns:SendSmsVerifyCode。

创建 RAM 用户并授权

推荐使用 RAM 用户的 AccessKey,不要直接使用阿里云主账号 AccessKey。

  1. 登录 阿里云 RAM 控制台
  2. 进入 身份管理 → 用户
  3. 点击 创建用户
  4. 填写必要的信息
  5. 在访问配置中选择 使用永久 AccessKey 访问
  6. 创建完成后,页面会自动回到用户列表页面

获取 AccessKey ID 和 AccessKey Secret

  1. 在用户列表找到刚创建的 RAM 用户,AccessKey 这一列会显示AccessKey ID、AccessKey Secret,点击复制

AccessKey ID、AccessKey Secret 只会在创建时显示一次,后续无法再次查看。如果丢失,只能禁用旧密钥并重新创建新的 AccessKey。

为 RAM 用户授权调用 Dypnsapi 的权限

  1. 在用户列表找到刚创建的 RAM 用户,点击登录名称 / 显示名称进入用户详情页
  2. 点击 权限管理 → 新增授权
  3. 在 权限策略 步骤,搜索框中搜索 dypns,找到 AliyunDypnsReadOnlyAccess、 AliyunDypnsFullAccess 权限,点击选择

你也可以选择 PowerUserAccess 权限,这种权限 提供对阿里云服务和资源的完全访问权限,包含了短信、OSS等等服务的全部权限。为了最小权限原则,建议只授权 AliyunDypnsReadOnlyAccess、 AliyunDypnsFullAccess,它包含了号码认证服务的全部权限,但不涉及其它服务。

  1. 确认授权

填入本地与生产环境变量

本地开发写入 apps/server/.dev.vars:

apps/server/.dev.vars
ALIBABA_CLOUD_ACCESS_KEY_ID=your-access-key-id
ALIBABA_CLOUD_ACCESS_KEY_SECRET=your-access-key-secret

生产部署写入 apps/server/.env.production:

apps/server/.env.production
ALIBABA_CLOUD_ACCESS_KEY_ID=your-access-key-id
ALIBABA_CLOUD_ACCESS_KEY_SECRET=your-access-key-secret

.env.production 只用于通过 Wrangler 批量推送到 Cloudflare Workers Secrets,不参与前端构建,也不应该提交到仓库。

推送生产 Secrets

部署到 Cloudflare Workers 前,把生产密钥推送到 Workers Secrets:

pnpm -F server secrets:bulk:production

推送成功后,Worker 运行时可以通过 env.ALIBABA_CLOUD_ACCESS_KEY_ID 和 env.ALIBABA_CLOUD_ACCESS_KEY_SECRET 读取密钥。更新密钥时重新执行这条命令即可,不需要因为 Secret 变化而重新部署代码。

保持阿里云验证码参数不变

EasyStarter 内置 Provider 已经按阿里云 SendSmsVerifyCode 文档锁定以下参数:

apps/server/src/sms/providers/aliyun.ts
const ALIYUN_SMS_VERSION = "2017-05-25";
const ALIYUN_SMS_SIGN_NAME = "速通互联验证码";
const ALIYUN_SMS_TEMPLATE_CODE = "100001";

这三个值不要修改:

常量对应阿里云参数为什么不能改
ALIYUN_SMS_VERSIONOpenAPI 版本Dypnsapi 的 SendSmsVerifyCode 接口版本就是 2017-05-25
ALIYUN_SMS_SIGN_NAMESignName文档示例使用号码认证服务赠送签名 速通互联验证码,该接口暂不支持普通自定义签名
ALIYUN_SMS_TEMPLATE_CODETemplateCode赠送签名必须搭配赠送模板,当前模板 Code 为 100001

这里使用的是号码认证服务 Dypnsapi 的短信认证接口,不是普通短信服务 Dysmsapi 的 SendSms。不要把普通短信服务里申请的 SMS_... 模板码替换到这里。

本地验证手机号登录

启动服务端与客户端后,在登录页选择手机号登录:

pnpm dev:server
pnpm dev:web

输入中国大陆手机号后,前端会调用:

POST /api/auth/phone-number/send-otp

提交验证码时会调用:

POST /api/auth/phone-number/verify

服务端会把 +86 号码拆成阿里云需要的 CountryCode=86 和本地手机号,然后由阿里云生成、发送并校验验证码。

常见问题

为什么不自己生成验证码?

当前实现使用 TemplateParam={"code":"##code##","min":"5"},让阿里云生成验证码。这样后续校验可以继续调用 CheckSmsVerifyCode,服务端不需要自己保存验证码。

为什么不能换成自己的短信签名?

SendSmsVerifyCode 属于号码认证服务。阿里云文档说明,赠送签名必须搭配赠送模板使用,并且暂不支持使用自定义签名。当前内置值与官方文档示例保持一致。

AccessKey 泄露怎么办?

立即在 RAM 控制台禁用或删除泄露的 AccessKey,重新创建新的 AccessKey,并重新推送 apps/server/.env.production 到 Workers Secrets。