EasyStarter logoEasyStarter
认证服务

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

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

阿里云手机号登录

EasyStarter 已经内置基于 Better Auth phone-number plugin 的手机号登录。服务端通过阿里云号码认证服务 Dypnsapi 发送和校验短信验证码,客户端继续使用 Better Auth 的 phoneNumber.sendOtpphoneNumber.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_IDGITHUB_CLIENT_SECRETGOOGLE_CLIENT_IDGOOGLE_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. 在访问方式中选择 OpenAPI 调用访问
  6. 创建完成后,为该 RAM 用户授予号码认证服务所需权限

如果你希望权限更收敛,可以创建自定义策略,只允许调用短信验证码相关接口:

{
  "Version": "1",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "dypns:SendSmsVerifyCode",
        "dypns:CheckSmsVerifyCode"
      ],
      "Resource": "*"
    }
  ]
}

如果你还在调试阶段,也可以先使用阿里云提供的号码认证服务相关系统策略,跑通后再收敛权限。

获取 AccessKey ID 和 AccessKey Secret

官方文档:创建 AccessKey

  1. 登录 阿里云 RAM 控制台
  2. 进入 身份管理用户
  3. 点击上一步创建的 RAM 用户
  4. 打开 认证管理AccessKey 标签
  5. 点击 创建 AccessKey
  6. 根据控制台提示完成安全校验
  7. 创建成功后,立即复制并保存:
    • AccessKey IDALIBABA_CLOUD_ACCESS_KEY_ID
    • AccessKey SecretALIBABA_CLOUD_ACCESS_KEY_SECRET

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

填入本地与生产环境变量

本地开发写入 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_IDenv.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。