5 Commits

Author SHA1 Message Date
iye
9d003a3b6f fix(auth): persist generated OTP so real codes can verify (with in-memory fallback)
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
之前 send-otp 生成的码只在 Redis 可用时才存. dev 没配 Redis → 码生成即丢, 用户拿到
真实 SMS 验证码也登不进去 (除了万能码 123456). 这是上次"修任意 6 位绕过"留下的回归.

新增 src/lib/otp-store.ts:
- storeOtp / consumeOtp 双方法, 内部按 Redis 可用性自动路由
- Redis 可用 → 走 Redis (生产)
- Redis 缺失 → 走进程内 Map (dev / 联调), 通过 globalThis 抗 HMR
- consumeOtp 校验通过即 del, 防重放

send-otp 与 verifyOtp 改走 otp-store, 不再直接读写 Redis 句柄。

E2E (curl + NextAuth callback):
  发码 → dev 日志拿 code=209988
  错码 000000 → 拒绝, session=null
  真码 209988 → 通过, session=粉丝_0099
  重放 209988 → 拒绝 (一次性消费)

并在 NODE_ENV !== production 时把生成的 code 打到 dev 终端, 方便 QA。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 15:21:00 +08:00
iye
8597957af4 fix(auth): reject wrong OTP codes when Redis is missing (security)
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 5m8s
Bug: verifyOtp 里 dev 态 Redis 未配置时, 写了 /^\d{6}$/.test(code) 作为联调 fallback,
导致任意 6 位数字都能登录(包括恶意构造). 实际表现: 用户输入错误验证码也能直接登录。

修复:
- Redis 未配置时无论 dev/prod 一律拒绝, 不再做"任意 6 位"放行
- dev 联调若需要绕过短信, 用万能码 123456 (已保留, 仅 NODE_ENV !== production)

E2E 验证 (curl + NextAuth credentials callback):
  错误码 999999 → /login?error=CredentialsSignin , session=null ✓
  万能码 123456 → callbackUrl=/, session 有用户 ✓

新增 tools/test-verify-otp.mjs 作为该 bug 的回归测试。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 15:08:03 +08:00
iye
0a7c1ec130 feat(auth): wire Aliyun SMS provider for phone OTP login
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 4m33s
接入流程:
- src/lib/sms.ts: 封装 sendOtpSms(phone, code), 走 dysmsapi.aliyuncs.com 全局端点
- /api/auth/send-otp:
    * 生成 6 位验证码 → Redis 5min TTL
    * 调 Aliyun SDK 发送; OK → 200, isv.* 错误 → 422, 其它 → 500
    * SMS_NOT_CONFIGURED 时 dev 仍能 console.log 验证码联调
- auth.ts verifyOtp:
    * dev 万能码 123456 保留
    * 否则 redis.get(sms:otp:phone) 比对, 通过后 del 防重放
    * Redis 未配置时 prod 拒绝, dev 接受任意 6 位

环境变量 (.env.local, 不入仓库):
- SMS_ACCESS_KEY / SMS_SECRET_KEY (RAM 子账号)
- SMS_SIGN_NAME (例: 广州气元科技)
- SMS_TEMPLATE_CODE (例: SMS_506210397)

依赖:
+ @alicloud/dysmsapi20170525
+ @alicloud/openapi-client
+ @alicloud/tea-util

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 14:56:47 +08:00
iye
7949f9bcd1 fix(nav,auth): trim nav to wireframe pages; auth gracefully degrades when DB unavailable in dev 2026-05-12 10:29:02 +08:00
iye
b7fbd5ac53 feat(auth): Auth.js v5 with phone OTP login, send-otp API, login page and user state in nav 2026-05-12 10:03:58 +08:00