Skip to content

feat: require CAPTCHA before sending email verification code on registration - #5857

Open
Gravirei wants to merge 2 commits into
QuantumNous:mainfrom
Gravirei:enhancement_1
Open

feat: require CAPTCHA before sending email verification code on registration#5857
Gravirei wants to merge 2 commits into
QuantumNous:mainfrom
Gravirei:enhancement_1

Conversation

@Gravirei

@Gravirei Gravirei commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

📝 变更描述 / Description

新增注册及发送邮箱验证码时的人机验证支持,以防止机器人恶意刷注册接口和消耗 SMTP 额度。
允许管理员在后台的"Bot Protection"设置中,单独开启此选项(RegisterPageWithCaptchaEnabled)。

主要改动:

  1. 后端:新增 RegisterPageWithCaptchaEnabled 常量;在 /api/status 中暴露该字段;middleware 层在该选项开启时强制对 /api/verification 和 /api/user/register 进行 Turnstile 校验,无需全局启用登录校验。
  2. 前端 (Default):Bot Protection 设置新增独立开关;注册页面对应适配。
  3. 前端 (Classic):系统设置新增对应复选框。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix)
  • ✨ 新功能 (New feature)
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

请在合并前在本地验证:开启该选项后,不完成 Turnstile 时注册接口返回校验失败;完成 Turnstile 后可正常发送验证码和注册。

Summary by CodeRabbit

  • New Features

    • Added a new system setting to require CAPTCHA/Turnstile verification during user registration (including when sending email verification codes).
    • Registration status now exposes whether this new registration CAPTCHA requirement is enabled.
  • Bug Fixes

    • Prevent enabling registration CAPTCHA/Turnstile unless the necessary Turnstile configuration is present.
    • Sign-up now activates the appropriate verification flow when the setting is enabled.
  • Documentation

    • Updated settings UI labels and added localized translations for the new registration verification options.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3ac5ccc5-c536-41ff-8f73-b5b1ebefa08f

📥 Commits

Reviewing files that changed from the base of the PR and between 89967b5 and 9db6eb0.

📒 Files selected for processing (1)
  • middleware/turnstile-check.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • middleware/turnstile-check.go

Walkthrough

Adds a new registration CAPTCHA setting, wires it through backend option storage and status APIs, enforces enablement rules and middleware behavior, and surfaces the toggle in both classic and default web settings with updated translations.

Changes

Register CAPTCHA setting

Layer / File(s) Summary
Backend option and status wiring
common/constants.go, controller/misc.go, model/option.go
Declares RegisterPageWithCaptchaEnabled, exposes it in GetStatus, and initializes/updates it through option storage.
Validation and middleware enforcement
controller/option.go, middleware/turnstile-check.go
Enabling the setting requires a Turnstile site key, and middleware turns on verification for registration and unauthenticated verification requests when the setting is active.
Classic settings and locales
web/classic/src/components/settings/SystemSetting.jsx, web/classic/src/i18n/locales/*.json
Adds the checkbox to classic settings and adds the registration Turnstile message across classic locales.
Default settings, sign-up flow, and locales
web/default/src/features/system-settings/types.ts, .../auth/index.tsx, .../auth/section-registry.tsx, .../auth/bot-protection-section.tsx, web/default/src/features/auth/hooks/use-turnstile.ts, .../sign-up/components/sign-up-form.tsx, web/default/src/i18n/locales/*.json
Adds the setting to default auth types and forms, forces Turnstile during sign-up when needed, and adds locale strings for the new registration prompts.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AdminUI
  participant UpdateOption
  participant TurnstileCheck
  participant RegisterAPI

  AdminUI->>UpdateOption: enable RegisterPageWithCaptchaEnabled
  UpdateOption->>UpdateOption: require TurnstileSiteKey
  RegisterAPI->>TurnstileCheck: request to /api/user/register
  TurnstileCheck->>TurnstileCheck: enable verification when flag is set
Loading
sequenceDiagram
  participant SignUpForm
  participant useTurnstile
  participant StatusAPI

  SignUpForm->>StatusAPI: read register_page_with_captcha, turnstile_check, turnstile_site_key
  SignUpForm->>SignUpForm: compute signupTurnstileRequired
  SignUpForm->>useTurnstile: useTurnstile({ forceEnable: signupTurnstileRequired })
  useTurnstile-->>SignUpForm: isTurnstileEnabled
Loading

Possibly related PRs

  • QuantumNous/new-api#5462: Both touch controller/option.go's UpdateOption, where this PR adds the RegisterPageWithCaptchaEnabled validation and the other adds audit logging after option updates.

Suggested reviewers: Calcium-Ion, seefs001

Poem

A checkbox hops in with a cheerful thump,
The rabbit says, “Let bots be stumped!”
Turnstile twirls at sign-up time,
Settings dance in rhythm and rhyme,
Hooray for guards that make spam jump 🐰

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: requiring CAPTCHA for registration email verification.
Linked Issues check ✅ Passed The PR adds registration CAPTCHA enforcement before email verification and wires it through backend and frontend.
Out of Scope Changes check ✅ Passed All changes relate to registration CAPTCHA, settings, middleware, UI, and translations; no unrelated scope is evident.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
controller/option.go (1)

201-218: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate validation logic between TurnstileCheckEnabled and RegisterPageWithCaptchaEnabled.

Both branches perform the identical check (option.Value == "true" && common.TurnstileSiteKey == "") and only differ in the error message. Consider extracting a small helper to avoid drift if the check logic changes later.

♻️ Proposed refactor
-	case "TurnstileCheckEnabled":
-		if option.Value == "true" && common.TurnstileSiteKey == "" {
-			c.JSON(http.StatusOK, gin.H{
-				"success": false,
-				"message": "无法启用 Turnstile 校验,请先填入 Turnstile 校验相关配置信息!",
-			})
-			return
-		}
-	case "RegisterPageWithCaptchaEnabled":
-		if option.Value == "true" && common.TurnstileSiteKey == "" {
-			c.JSON(http.StatusOK, gin.H{
-				"success": false,
-				"message": "无法启用注册人机验证,请先填入 Turnstile 校验相关配置信息!",
-			})
-			return
-		}
+	case "TurnstileCheckEnabled", "RegisterPageWithCaptchaEnabled":
+		if option.Value == "true" && common.TurnstileSiteKey == "" {
+			msg := "无法启用 Turnstile 校验,请先填入 Turnstile 校验相关配置信息!"
+			if option.Key == "RegisterPageWithCaptchaEnabled" {
+				msg = "无法启用注册人机验证,请先填入 Turnstile 校验相关配置信息!"
+			}
+			c.JSON(http.StatusOK, gin.H{
+				"success": false,
+				"message": msg,
+			})
+			return
+		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/option.go` around lines 201 - 218, The `TurnstileCheckEnabled` and
`RegisterPageWithCaptchaEnabled` branches in `option.go` duplicate the same
`option.Value == "true" && common.TurnstileSiteKey == ""` validation. Extract
that shared check into a small helper or shared condition near the existing
switch logic, and keep the two branches only for their specific error messages
so the validation stays consistent if it changes later.
web/classic/src/components/settings/SystemSetting.jsx (1)

1075-1083: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider warning admins that a Turnstile Site Key is required.

Backend UpdateOption rejects enabling this flag without a configured Turnstile Site Key (per PR objectives). Other cross-dependent checkboxes in this file (e.g. the SSRF checkboxes) use extraText to surface such dependencies proactively; this new checkbox has none, so admins will only discover the requirement via a generic error toast after clicking, and the checkbox will remain visually checked despite the rejected request (same UI-rollback gap shared by other checkboxes in this component, not new to this PR).

💡 Suggested extraText addition
                       <Form.Checkbox
                         field='RegisterPageWithCaptchaEnabled'
                         noLabel
+                        extraText={t('启用前请先配置 Turnstile Site Key')}
                         onChange={(e) =>
                           handleCheckboxChange('RegisterPageWithCaptchaEnabled', e)
                         }
                       >
                         {t('注册及发送邮箱验证码时需要 Turnstile 校验')}
                       </Form.Checkbox>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/classic/src/components/settings/SystemSetting.jsx` around lines 1075 -
1083, The RegisterPageWithCaptchaEnabled checkbox in SystemSetting.jsx should
proactively warn admins that a Turnstile Site Key is required. Add an extraText
message to this Form.Checkbox, matching the pattern used by the SSRF-related
checkboxes in this component, so the dependency is visible before submission.
Keep the existing handleCheckboxChange flow intact and place the guidance near
the RegisterPageWithCaptchaEnabled label so it’s easy to find if the UI changes.
web/default/src/features/auth/hooks/use-turnstile.ts (1)

28-35: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

forceEnable doesn't guard against missing site key.

isTurnstileEnabled can become true via forceEnable even when turnstileSiteKey is empty (if a future caller doesn't pre-check turnstile_site_key like sign-up-form.tsx does). Consider deriving isTurnstileEnabled so it also requires a non-empty site key when relying on forceEnable, to make the hook self-contained rather than depending on caller discipline.

♻️ Suggested defensive fix
-  const isTurnstileEnabled =
-    options?.forceEnable ||
-    !!(status?.turnstile_check && status?.turnstile_site_key)
   const turnstileSiteKey = status?.turnstile_site_key || ''
+  const isTurnstileEnabled =
+    !!turnstileSiteKey &&
+    (options?.forceEnable || !!status?.turnstile_check)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/auth/hooks/use-turnstile.ts` around lines 28 - 35,
The `useTurnstile` hook’s `isTurnstileEnabled` flag can become true from
`forceEnable` even when `turnstileSiteKey` is empty, so make the hook
self-contained by requiring a non-empty site key in the `useTurnstile` logic.
Update the `isTurnstileEnabled` derivation in `useTurnstile` so `forceEnable`
only enables Turnstile when `status.turnstile_site_key` is present, instead of
relying on callers like `sign-up-form.tsx` to precheck it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@middleware/turnstile-check.go`:
- Around line 20-24: The Turnstile check is too broadly enabled for
"/api/verification", which affects non-registration flows like email binding.
Update the logic in turnstile-check.go within the turnstile middleware to scope
CAPTCHA enforcement to registration only, either by splitting the verification
route or by checking the request purpose before setting enabled in the
route-matching block that currently uses common.RegisterPageWithCaptchaEnabled.

---

Nitpick comments:
In `@controller/option.go`:
- Around line 201-218: The `TurnstileCheckEnabled` and
`RegisterPageWithCaptchaEnabled` branches in `option.go` duplicate the same
`option.Value == "true" && common.TurnstileSiteKey == ""` validation. Extract
that shared check into a small helper or shared condition near the existing
switch logic, and keep the two branches only for their specific error messages
so the validation stays consistent if it changes later.

In `@web/classic/src/components/settings/SystemSetting.jsx`:
- Around line 1075-1083: The RegisterPageWithCaptchaEnabled checkbox in
SystemSetting.jsx should proactively warn admins that a Turnstile Site Key is
required. Add an extraText message to this Form.Checkbox, matching the pattern
used by the SSRF-related checkboxes in this component, so the dependency is
visible before submission. Keep the existing handleCheckboxChange flow intact
and place the guidance near the RegisterPageWithCaptchaEnabled label so it’s
easy to find if the UI changes.

In `@web/default/src/features/auth/hooks/use-turnstile.ts`:
- Around line 28-35: The `useTurnstile` hook’s `isTurnstileEnabled` flag can
become true from `forceEnable` even when `turnstileSiteKey` is empty, so make
the hook self-contained by requiring a non-empty site key in the `useTurnstile`
logic. Update the `isTurnstileEnabled` derivation in `useTurnstile` so
`forceEnable` only enables Turnstile when `status.turnstile_site_key` is
present, instead of relying on callers like `sign-up-form.tsx` to precheck it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 34a26cb1-97b7-41f3-b248-0f7718a02a7c

📥 Commits

Reviewing files that changed from the base of the PR and between 52858ad and 89967b5.

📒 Files selected for processing (26)
  • common/constants.go
  • controller/misc.go
  • controller/option.go
  • middleware/turnstile-check.go
  • model/option.go
  • web/classic/src/components/settings/SystemSetting.jsx
  • web/classic/src/i18n/locales/en.json
  • web/classic/src/i18n/locales/fr.json
  • web/classic/src/i18n/locales/ja.json
  • web/classic/src/i18n/locales/ru.json
  • web/classic/src/i18n/locales/vi.json
  • web/classic/src/i18n/locales/zh-CN.json
  • web/classic/src/i18n/locales/zh-TW.json
  • web/classic/src/i18n/locales/zh.json
  • web/default/src/features/auth/hooks/use-turnstile.ts
  • web/default/src/features/auth/sign-up/components/sign-up-form.tsx
  • web/default/src/features/system-settings/auth/bot-protection-section.tsx
  • web/default/src/features/system-settings/auth/index.tsx
  • web/default/src/features/system-settings/auth/section-registry.tsx
  • web/default/src/features/system-settings/types.ts
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh.json

Comment thread middleware/turnstile-check.go
@Calcium-Ion
Calcium-Ion force-pushed the main branch 2 times, most recently from 51fdfc5 to 2b6f1df Compare August 30, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Add CAPTCHA before sending email verification code on registration

1 participant