feat(auth): Discord 登录灰度白名单 + OTP 本地 dev-console 兜底 - #53
Merged
Conversation
两件事: 1) Discord 登录灰度(线上"打开一半") OAuthController 回调处新增 Discord user id 白名单闸: auth.discord.allowlist 非空时,只有名单内的 Discord id 能登入,其他人 (含直连 /oauth/render/discord 绕过前端按钮的)被弹回 /login?error=discord_canary。 空名单=对所有人开放,GA(OTP wiring 完成)时清空即可。 白名单外的人换 token 已发生,但不建号/不登入——不碰未完成的新用户 OTP 路径。 2) OTP 本地 dev-console 兜底 RegistrationService.sendOtp 在 Resend 未配置(isConfigured()==false)时, 把验证码打到控制台并返 SENT,而非 SEND_FAILED——让贡献者不配 Resend key 也能 本地跑通完整注册流(Django/Rails 的 console email backend 同款)。生产必配 key, 走不到这里。新增用例锁定:未配置时不调 sendHtml、仍返 SENT。 全量 289 测试通过。
longsizhuo
added a commit
that referenced
this pull request
Jul 26, 2026
xhigh review(#54)对已上线的 Discord 灰度(#53)查出的后端问题,逐条修: 灰度闸 - 白名单启动时解析成 Set 并**播报当前模式**:缺 env 与故意 GA 行为完全一致, 没有这条日志,一次丢掉 AUTH_DISCORD_ALLOWLIST 的部署会静默把 Discord 对全网 打开,唯一信号是"没有拒绝日志"。 - 配了值却解析不出任何 id(清列表时手滑留个逗号;Java 的 ",".split(",") 返回零长 数组)不再塌缩成全开放:保持拒绝并 ERROR 告警——错误方向要选"没人能登"。 - 剥引号:docker-compose env_file 不剥,ALLOWLIST="123" 会带引号比对失败, 把本该放行的人也锁在外面。 - 空 uuid 不放行。 - **已有账号的回访登录豁免**:灰度要拦的是"建新号"(OTP wiring 未完成会分叉账号), 不是把已有账号的人锁在自己账号外面、还只提示"敬请期待"。 - 拒绝时撤销刚换到的 access token(AuthDiscordRequest#revokeToken):既然不让登, 就别把用不上的 token 和授权留在人家 Discord 账号里。撤销失败不阻断拒绝流程。 OTP dev 兜底 - 改由显式开关 registration.otp.dev-console 驱动,不再只看"Resend 没配"——后者同时 也是生产掉 key 的样子,那样 prod 丢 key 会假装发送成功,用户永远卡在输码页 (改前是 SEND_FAILED,可重试)。关掉开关即恢复该行为。 - 日志不再打印收件邮箱(PII),只留 pendingId + code,与本文件下方及 ResendEmailService 的既有规则一致。 测试与文档 - 新增 OAuthControllerAllowlistTests(10 条):畸形取值解析、未配即全开、配了却 解析不出 id 时拒绝所有人、空 uuid、已有 identity 放行、identity 查询失败不放行。 - RegistrationServiceTests:dev 兜底改测"会话状态确实写入"(限流已计数),挡住把 isConfigured() 检查上提导致本地流程静默坏掉的重构;补"没开开关时仍返 SEND_FAILED"。 - application-test.properties pin 掉两个新属性:本机 .env 会渗进测试 JVM,否则同一 个测试在本机和 CI 结论不同(与 justauth.type.discord.* 既有 pin 同因)。 - .env.example 补 AUTH_DISCORD_ALLOWLIST / REGISTRATION_OTP_DEV_CONSOLE:照文档配出来 的环境此前 = 白名单为空 = 全开放。 - SECURITY.md 补 INV-008(闸只放行白名单与已有账号,且不得静默失效)。 全量 300 测试通过(289 → +11)。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1. Discord 登录灰度(线上"打开一半")
OAuthController回调处按 Discord user id 白名单放行:auth.discord.allowlist(AUTH_DISCORD_ALLOWLIST,逗号分隔)非空 → 只有名单内 id 能登入;其他人被弹回/login?error=discord_canary。/oauth/render/discord绕过前端按钮的人一并挡在回调处(这其实收紧了此前无闸、手动 URL 即可登入的面)。配套前端 PR 放出 Discord 按钮并显示灰度提示。
2. OTP 本地 dev-console 兜底
RegistrationService.sendOtp在 Resend 未配置时把验证码打到控制台并返SENT(Django/Rails 的 console email backend 同款),让贡献者不配 Resend key 也能本地跑通完整注册流。生产必配 key,走不到此分支。验证
全量 289 通过;新增用例:Resend 未配置时不调
sendHtml、仍返SENT。🤖 Generated with Claude Code
https://claude.ai/code/session_01RtSNVtrEkCsFVHrbt4Zjgi