Skip to content

Turnstile token is never reset after a failed submit → retry dead-end #67

Description

@tkowalczyk

Summary

Cloudflare siteverify tokens are single-use. The lead form consumes the token on the first submit; if the overall request fails, the form re-enables Submit with the same token, so every retry fails timeout-or-duplicate until the widget self-expires. The "try again" copy cannot succeed. The widget also caches a rejected script-load promise module-globally, so one api.js network failure disables the widget for the session.

Evidence

  • apps/user-application/src/components/leads/lead-capture-form.tsx:30-34,108-118 — only onSuccess resets; error path keeps turnstileToken.
  • apps/user-application/src/components/leads/turnstile-widget.tsx:23-39 — module-global scriptPromise cached even when rejected; no reset() exposed to the parent.

Proposed failing test (TDD)

lead-capture-form.test.tsx"resets the turnstile widget after a failed submit": mock submitLead rejecting once and window.turnstile.reset as a spy; submit, await error; assert turnstile.reset was called (or the token was cleared so Submit is disabled until a fresh token). Fails today.

Fix direction

On mutation error, call turnstile.reset() and clear the token; do not cache a rejected script promise.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    audit/2026-06Filed from June 2026 deep auditbugSomething isn't workingseverity/mediumMedium severity security or reliability risk

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions