fix: Stop rejecting fresh reCAPTCHA tokens as stale - #144
Open
yahyafakhroji wants to merge 1 commit into
Open
yahyafakhroji wants to merge 1 commit into
yahyafakhroji wants to merge 1 commit into
Conversation
…kens verifyRecaptcha accepted Google's success verdict and then rejected the token as "stale" when challenge_ts was more than two minutes old. For v3, challenge_ts is when the page's challenge was created, not when execute() minted the token at submit time. Any form submitted more than about two minutes after page load failed with a clean score. Google already enforces expiry and single use, and reports both as success=false with timeout-or-duplicate, which lands as reason "rejected". The local check was redundant and anchored on the wrong timestamp. Removes MAX_TOKEN_AGE_MS, the "stale" reason, and the tolerateStale option from recaptchaRejects, so the recovery code step calls it plainly again. The no-token, rejected, action-mismatch, hostname-mismatch and fail-open paths are unchanged. Staging, on both recovery steps: signup_recaptcha_scored verdict=invalid reason=stale score=0.9 action=recovery signup_recaptcha_scored verdict=invalid reason=stale score=0.9 action=recovery_code
yahyafakhroji
requested review from
gaghan430,
kevwilliams,
mattdjenkinson and
mdobush
September 25, 2026 19:37
yahyafakhroji
enabled auto-merge
September 25, 2026 19:38
Contributor
🧪 Test Summary
|
This branch has not been deployed
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.
Problem
Testing recovery on staging, both steps rejected a freshly minted reCAPTCHA token as stale even though Google had verified it with a score of 0.9:
recaptcha.server.tsapplied its own two-minute age check after Google'ssuccess, keyed offchallenge_ts. For reCAPTCHA v3 that timestamp is when the page's challenge was created, not whenexecute()minted the token, so anyone who took more than two minutes on the page was rejected. On the request step the route then issued a filler ticket and sent no mail, silently. #143'stolerateStaleonly covered the code step.Solution
Delete the local age check and
tolerateStale. Google already enforces expiry and single use, answeringsuccess=falsewithtimeout-or-duplicate, which this file already handles as a rejection. Every other guard is unchanged: no-token, rejected, action mismatch, hostname mismatch, and the fail-open cases. Tests re-pinned so an old-but-valid token is accepted andtimeout-or-duplicateis rejected.Follow-up: two unrelated Cypress fixtures still stub the now-unused
challenge_tsfield. Harmless, not done here.Refs #112