Skip to content

fix(captcha): use getResponsePromise for Turnstile execute-on-submit flow#3943

Merged
waleedlatif1 merged 1 commit intostagingfrom
feat/captcha
Apr 4, 2026
Merged

fix(captcha): use getResponsePromise for Turnstile execute-on-submit flow#3943
waleedlatif1 merged 1 commit intostagingfrom
feat/captcha

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

Summary

  • Replace manual `Promise.race` + `setTimeout` captcha flow with `widget.getResponsePromise()`
  • Remove `onError`/`onExpire` callbacks that were incorrectly rejecting on recoverable failures, blocking Turnstile's built-in retry logic
  • Add `appearance: 'execute'` so the widget stays hidden until the challenge runs

Type of Change

  • Bug fix

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Apr 4, 2026 7:25pm

Request Review

@cursor
Copy link
Copy Markdown

cursor bot commented Apr 4, 2026

PR Summary

Medium Risk
Touches the signup anti-bot flow by changing how Turnstile tokens are retrieved and when failures are surfaced; misbehavior could block account creation or weaken captcha enforcement.

Overview
Simplifies the signup Turnstile execute-on-submit flow by removing the custom Promise.race + timeout and resolving the captcha token via widget.getResponsePromise() after widget.execute().

Removes onSuccess/onError/onExpire rejection wiring so Turnstile can handle retries internally, and updates widget options to keep the captcha hidden until execution via appearance: 'execute'.

Reviewed by Cursor Bugbot for commit 5216d6a. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 4, 2026

Greptile Summary

This PR simplifies the Turnstile CAPTCHA flow on the signup form by replacing a hand-rolled Promise.race + setTimeout implementation with the library-native widget.getResponsePromise() API, and cleans up callback refs that were causing premature rejections on recoverable Turnstile errors.

Key changes:

  • Removes captchaResolveRef / captchaRejectRef and the manual onSuccess / onError / onExpire wiring that was incorrectly blocking Turnstile's internal retry logic on transient failures.
  • Replaces the two-Promise race (with a 15 s hard-coded timeout) with widget.execute() followed by await widget.getResponsePromise(), which carries a 30 s default timeout (acceptable given Turnstile's ~8 s auto-retry window).
  • Adds appearance: 'execute' so the invisible widget frame is not injected into the DOM until execute() is actually called, matching the existing execution: 'execute' option.
  • The catch block correctly calls setIsLoading(false) and returns early on any CAPTCHA failure, preserving the original UX behaviour.

Confidence Score: 5/5

Safe to merge — the change is a straightforward simplification that delegates lifecycle management to the Turnstile library's own API, removing fragile manual state.

No P0 or P1 issues found. The new flow correctly calls execute() before getResponsePromise(), preserves the isLoading guard, and error handling is unchanged. All previously raised concerns (timeout duration) were reviewed by a senior developer and intentionally accepted.

No files require special attention.

Important Files Changed

Filename Overview
apps/sim/app/(auth)/signup/signup-form.tsx Replaced manual Promise.race/setTimeout captcha flow with widget.getResponsePromise(); removed stale resolver refs and incorrect onError/onExpire callbacks; added appearance: 'execute' option. Logic is correct and the simplification is safe.

Sequence Diagram

sequenceDiagram
    actor User
    participant Form as SignupForm
    participant Widget as TurnstileWidget
    participant CF as Cloudflare

    User->>Form: Submit signup
    Form->>Form: Validate name/email/password
    Form->>Widget: widget.reset()
    Form->>Widget: widget.execute()
    Widget->>CF: Start challenge
    CF-->>Widget: onSuccess(token) / onError / onExpire (internally)
    Widget-->>Form: getResponsePromise() resolves/rejects (30s timeout)
    alt CAPTCHA succeeded
        Form->>Form: setFormError(null)
        Form->>Form: client.signUp.email({ headers: { x-captcha-response: token } })
    else CAPTCHA failed / timed out
        Form->>Form: setFormError("Captcha verification failed")
        Form->>Form: setIsLoading(false)
    end
Loading

Reviews (2): Last reviewed commit: "fix(captcha): use getResponsePromise for..." | Re-trigger Greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 5216d6a. Configure here.

@waleedlatif1 waleedlatif1 merged commit c2b12cf into staging Apr 4, 2026
12 checks passed
@waleedlatif1 waleedlatif1 deleted the feat/captcha branch April 4, 2026 19:34
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.

1 participant