Uh oh!
There was an error while loading. Please reload this page.
Server-side CAPTCHA: Turnstile on free signup (+ web 4.4.0) - #910
Conversation
Free signup now uses Cloudflare Turnstile (Managed mode) and actually SENDS the token to the backend for server-side verification. The previous reCAPTCHA was checked client-side only and the token was discarded, so it was trivially bypassed. - New dependency-free <Turnstile> component (loads the CF script, explicit render, expire/error reset). - SDK signUp() accepts an optional captchaToken and includes captcha_token in the request body (backward compatible; existing 3-arg callers unaffected). - Public sitekey via NEXT_PUBLIC_TURNSTILE_SITEKEY (with the site default). - Bump @ecency/web to 4.4.0. Backend verification lands separately and is off by default until enabled; the secret stays server-side only. Mobile + per-link untrusted styling are follow-ups.
Caution Review failedPull request was closed or merged during review No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (7)
📒 Files selected for processing (9)
📝 WalkthroughWalkthroughThis PR migrates the free signup form from Google reCAPTCHA to Cloudflare Turnstile. It introduces a new reusable Turnstile React component with lazy script loading and error handling, extends the SDK signUp endpoint to accept a captcha token, integrates Turnstile into the signup form with proper state management and retry behavior, and updates localization strings and package versions across three packages. ChangesTurnstile Verification Migration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
packages/sdk/src/modules/private-api/requests.tsOops! Something went wrong! :( ESLint: 8.57.1 YAMLException: Cannot read config file: /packages/sdk/eslint.config.mjs 2 | 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. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:3cb3769cc0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
| const response = await signUp(username, email, referral); | ||
| const response = await signUp(username, email, referral, captchaToken); |
There was a problem hiding this comment.
Rebuild SDK dist before passing the CAPTCHA token
In the web app, @ecency/sdk resolves through the package exports to the checked-in packages/sdk/dist/* artifacts, but this commit only changed packages/sdk/src; I checked packages/sdk/dist/browser/index.js and packages/sdk/dist/node/index.mjs, and their signUp implementations still accept three arguments and serialize only { username, email, referral }. This fourth argument is therefore dropped in the production bundle, so once the backend requires Turnstile verification the free signup request will still arrive without captcha_token and be rejected.
Useful? React with 👍 / 👎.
Greptile SummaryReplaces client-only reCAPTCHA on the free signup page with Cloudflare Turnstile (Managed mode) and wires the token to the backend for actual server-side verification. The old reCAPTCHA token was never sent to the backend, so this closes a trivially bypassable signup gate.
Confidence Score: 5/5Safe to merge; all previous review concerns have been addressed and no new functional defects are introduced. The Turnstile component is cleanly implemented — script deduplication, proper cleanup on unmount, stable callbacks via refs, and imperative reset all work correctly. The signup page correctly gates submission on a live token and resets both the state and the widget on every failure branch. The SDK change is backward-compatible. No files require special attention; the core changes in turnstile.tsx and _page.tsx are self-consistent. Important Files Changed
Reviews (3): Last reviewed commit: "chore: apply changeset versioning for PR..." | Re-trigger Greptile |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…stness - Turnstile tokens are single-use: clear the token and reset the widget after a failed signup (error code or thrown error) so a retry gets a fresh challenge instead of a confusing CAPTCHA failure. Turnstile now exposes a reset() handle. - Hold onVerify/onExpire in refs so the widget renders once but always calls the latest callbacks (no stale closures if a caller passes inline callbacks). - Show a visible message when the Turnstile script fails to load (ad/script blocker, CSP) instead of leaving the submit button silently disabled.
What
Replaces the client-only reCAPTCHA on the free signup page with Cloudflare Turnstile (Managed mode) and actually sends the token to the backend for server-side verification. The old reCAPTCHA token was never sent/verified, so the check was trivially bypassed.
Changes
features/shared/turnstile.tsx— small dependency-free Turnstile component (loads the CF script once, explicit render, resets the token on expire/error)./signup/free— renders<Turnstile>instead of<ReCAPTCHA>; the submit button stays disabled until a token is obtained; the token is passed tosignUp.@ecency/sdksignUp()— optionalcaptchaTokenparam →captcha_tokenin the POST body. Backward compatible (existing 3-arg callers unchanged).NEXT_PUBLIC_TURNSTILE_SITEKEY(site default baked in; it's public by design).@ecency/webto 4.4.0.Rollout
Inert until the backend enables verification (
CAPTCHA_MODE=soft→hard). The verification secret stays server-side only (onboard env); only the public sitekey is in the client. reCAPTCHA on the login page is intentionally left untouched (out of scope). Mobile widget + per-outbound-link "untrusted" styling are separate follow-ups.Validation
Typecheck clean for the changed files; existing tests unaffected (no spec referenced the reCAPTCHA flow).
Summary by CodeRabbit