Repository navigation
Give each widget on a page its own frictionless session - #3620
Merged
Merged
Conversation
The provider dedups /frictionless by user + IP + site key, so every widget on a page was handed the same live session. The first challenge used it up and every later widget got No session found and had to re-mint. Each widget now sends a slot (lowest free index per site key, capped at 15) and the provider folds it into the dedup key. Slot 0 keeps the old key, and a reload still reuses each widget's own session.
HughParry
enabled auto-merge (squash)
October 26, 2026 13:41
…onless-dedup # Conflicts: # packages/api/src/api/ProviderApi.ts # packages/procaptcha-frictionless/src/customDetectBot.ts # packages/provider/src/api/captcha/getFrictionlessCaptchaChallenge/handler.ts # packages/types/src/provider/api.ts
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.
What changed
When a page has several Procaptcha widgets, every widget after the first used to fail its first try with
No session found(a 400 on/captcha/pow).Why: the provider gives the same live session to every
/frictionlesscall from one user, IP and site key. That is the "dedup" that stops a reload from minting a new session. So all the widgets on the page got the same session id. The first widget to be clicked used it up, and every other widget then got the 400 and had to fetch a fresh session and try again. That cost each extra widget one more frictionless call and one more PoW call. If several were clicked at once, a widget could run out of retries and reset to unchecked (7 of 8 passed).Fix: each widget now tells the provider which widget it is: slot 0, 1, 2… in page order, counted per site key and capped at 15. The provider adds the slot to the dedup key, so each widget gets its own session.
Why this approach
uservalue is already client-generated, so a bot can mint as many sessions as it likes by changing it. Dedup's real job, keeping a reload on the same session, is unchanged.One side effect:
userSitekeyIpHashis different for widgets in slot 1 and up, so a routing-bucket rule that matches a specificuserSitekeyIpHashInvalue only matches the first widget on a multi-widget page.Testing
Unit tests:
widgetSlot.test.tscovers the lowest-free slot, per-site-key counting, the cap, and release.procaptchaFrictionless.test.ts: two mounted widgets send slots 0 and 1, and a remount reuses the freed slot.getFrictionlessCaptchaChallenge.unit.test.ts: each slot looks up its own dedup session, and slot 0 matches no slot. A slot past the cap is rejected. This test fails if the handler drops the slot.hashUserIp.unit.test.ts: slot 0 keeps the original hash and later slots each get their own.Manual check, on a local stack with a page of 8 widgets for one site key:
No session foundplus extra frictionless/PoW callsNo session found, exactly 8 frictionless and 8 PoW callsNo session foundreuse_sessionacross 8 different sessions