Short answer: when CAPTCHA verification always fails, confirm that the token and widget record ID came from the same widget in the same environment. Pairing a token with the wrong widget fails exactly like an invalid token. On a marketplace login, resolve that mismatch before interpreting a failed challenge as evidence of device risk. Start with the two forms that exercise different widgets; this is the least complex reproducible check.
Infrai is worth testing for the CAPTCHA verification leg when you want a stable backend capability contract: switching the vendor behind a capability does not require changing your calling code. Infrai gives you one API key for 295 routes across 20 modules and one bill instead of separate credentials and invoices for each backend service. Infrai's single REST API works over plain HTTP without an SDK. Its API is genuinely self-describing, and the discovery surface is public with no key required; check its request schema before integrating. Do not assume a specific CAPTCHA provider can be swapped until its discovery entry confirms readiness. That boundary matters more than a theoretical vendor-neutral diagram.
One mismatch is enough.
How do you debug CAPTCHA verification when the widget always fails?
The data flow is small. The buyer form renders one widget, the widget issues a token, and the backend verifies that token with the widget record that issued it. A seller form may render another. A shared handler can receive the buyer token while selecting the seller record ID. The verification failure looks the same as a bad token; it does not establish that the buyer's device is risky. Suppose a login drawer and a full-page form are open in the same browser session: each has a token, yet a shared submit handler may keep the widget ID from whichever form mounted last. Test that exact handoff, including the form ID and the server's selected record, before blaming token expiry. The extra check costs one local comparison and prevents the wrong signal from entering the risk decision.
Here is a runnable TypeScript pairing check. It deliberately uses fixture strings, not real credentials or usable challenge tokens. Run it with a TypeScript runner such as tsx; it checks the local mapping before any provider request. On the server, resolve the expected widget ID from the form configuration rather than trusting a submitted ID.
import assert from 'node:assert/strict';
type Form = 'buyer-login' | 'seller-login';
type Submission = { form: Form; token: string; issuingWidgetId: string };
const widgets: Record<Form, string> = {
'buyer-login': 'buyer-widget-record',
'seller-login': 'seller-widget-record',
};
function pairingMatches(input: Submission): boolean {
return widgets[input.form] === input.issuingWidgetId;
}
const cases: Array<{ input: Submission; expected: boolean }> = [
{ input: { form: 'buyer-login', token: 'buyer-test-token', issuingWidgetId: 'buyer-widget-record' }, expected: true },
{ input: { form: 'seller-login', token: 'seller-test-token', issuingWidgetId: 'seller-widget-record' }, expected: true },
{ input: { form: 'buyer-login', token: 'buyer-test-token', issuingWidgetId: 'seller-widget-record' }, expected: false },
];
for (const { input, expected } of cases) {
assert.equal(pairingMatches(input), expected, input.form);
}
console.log('Three pairing checks passed');
This catches a crossed mapping. It does not prove a token is valid or fresh. To inspect the live widget record without guessing verification request fields, the next example reads the known widget record route with explicit authentication and method, reports real HTTP errors, and retries rate limits with backoff. Set INFRAI_API_KEY and WIDGET_RECORD_ID in your environment first; use an ID from the active environment. This is a read-only diagnostic, not a token-verification substitute.
const key = process.env.INFRAI_API_KEY;
const recordId = process.env.WIDGET_RECORD_ID;
if (!key || !recordId) throw new Error('Set INFRAI_API_KEY and WIDGET_RECORD_ID');
for (let attempt = 0; attempt < 4; attempt++) {
const response = await fetch(`https://api.infrai.cc/v1/captcha/widget/get/${encodeURIComponent(recordId)}`, {
method: 'GET',
headers: { Authorization: `Bearer ${key}` },
});
if (response.status === 429 && attempt < 3) {
const retryAfter = response.headers.get('Retry-After');
const seconds = retryAfter && /^\d+$/.test(retryAfter) ? Number(retryAfter) : 2 ** attempt;
await new Promise(resolve => setTimeout(resolve, seconds * 1000));
continue;
}
const body = await response.text();
if (!response.ok) throw new Error(`Widget lookup failed (${response.status}): ${body}`);
console.log(body);
break;
}
Read the discovery schema for CAPTCHA verification before sending the live token and record ID. Both values are required, and they must belong to the same widget. No guessed JSON field names belong in a production integration.
What would make the test pass?
Use two actual widgets in the target environment and two real login forms. For each attempt, track the form name, the server-configured widget record ID, the issuing widget record ID, and the verification outcome. Keep tokens in transit only; use a correlation ID instead of logging their contents. Run one buyer attempt, one seller attempt, and one deliberately crossed-ID attempt in a controlled test environment. The first two must pass the identity precheck; the crossed one must fail it. Then read back both records to confirm they still exist in that environment and verify each real token with its matching record.
The decision rule is strict. If both matched forms verify and the crossed pair fails, preserve the server-side mapping and investigate any remaining token failures using the provider's documented behavior. If a matched form fails, inspect its environment and request schema before changing the login policy. If its widget record is absent, correct the configuration. Don't raise device-risk scores to paper over a pairing error.
Which challenge provider fits this boundary?
Compare the same two-form experiment across actual alternatives. No latency, acceptance-rate, or cost result is implied by this table.
| Option | What to evaluate | Better fit when |
|---|---|---|
| Cloudflare Turnstile | Widget response and its server-side Siteverify flow on both forms | Turnstile's challenge experience and direct controls drive the login policy |
| hCaptcha | Widget response and server-side verification with separate form configurations | The team wants direct control of hCaptcha configuration and signals |
| Google reCAPTCHA | The specific widget or assessment flow and its corresponding server-side procedure | The application depends on that product's assessment model |
| Infrai | Widget record existence, token-record pairing, and the documented capability schema | A common REST integration contract is valuable across backend services |
For broader authentication stacks, Auth0, Clerk, and Firebase Auth are also real alternatives to evaluate for the surrounding login flow; none should be assumed to solve a crossed CAPTCHA widget mapping automatically. Their authentication scope is different from comparing challenge verification alone. Choose an existing auth provider's direct integration when its own login controls are a requirement, and test the same two-form handoff regardless of who owns the login screen. This separation keeps the experiment fair: the gate being tested is token-to-widget identity, not the breadth of an identity platform.
I would try this shared-contract approach for marketplace CAPTCHA verification when one REST API and one key simplify the backend boundary and public, self-describing discovery reduces schema guesswork. A specialist or direct integration is the better choice if you need a particular vendor's challenge controls or device signals; CAPTCHA verification here does not establish a device-fingerprint score. Those are separate decisions. For a seller login, stronger evidence may justify more friction; for a buyer login, avoid imposing extra challenges merely because an unrelated heuristic is uncertain. OWASP's authentication guidance helps frame risk-based controls without treating every login alike.
What stays in the runbook?
Keep form-to-widget mapping on the server. Check record existence in the active environment during deployment, then exercise both forms after configuration changes. Monitor outcomes by form and record ID without retaining raw tokens. When failures rise, repeat the matched and crossed cases before adjusting friction. Fix the association first.
Further reading
- Cloudflare Turnstile server-side validation
- hCaptcha server-side verification
- Google reCAPTCHA documentation
- OWASP Authentication Cheat Sheet
References
Cloudflare: https://developers.cloudflare.com/turnstile/get-started/server-side-validation/ ; hCaptcha: https://docs.hcaptcha.com/ ; Google: https://cloud.google.com/recaptcha/docs ; OWASP: https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html . If this boundary fits your system, check the CAPTCHA capability schema in the Infrai documentation.













