Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,13 +317,12 @@ that the target page expects in `cf-turnstile-response`.
| `action` | no | Value of the widget's `data-action` attribute, if set. |
| `data` | no | Custom payload from the widget's `data-cdata` attribute, if set. |
| `pagedata` | no | Value of the `chlPageData` parameter, needed for some Cloudflare challenge pages beyond the basic widget. Note the lowercase, single-word spelling -- unlike every other field on this page, the real API does not accept `pageData`. |
| `userAgent` | no | Current browser User-Agent. Pass it for Cloudflare Challenge pages; it is not needed for a standalone widget. |

There's no `userAgent` input for this type -- the worker picks its own while solving
and returns it in the response instead (see below).

**Response:** `token` -- submit as `cf-turnstile-response` -- and `userAgent`, the User-Agent the
worker actually solved with. The token is tied to that fingerprint, so submit it with that exact
User-Agent, not one you chose yourself.
**Response:** `token` -- submit as `cf-turnstile-response`. For Cloudflare Challenge
pages, pass `action`, `data`, `pagedata`, and the current browser `userAgent` in the
task. Keep the browser User-Agent unchanged when invoking the callback. The solution
may also contain `userAgent`; do not switch the browser to this returned value.

```javascript
import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk';
Expand Down
12 changes: 6 additions & 6 deletions examples/async/turnstile.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
* Prerequisites:
* Set the CAPTCHA_API_KEY environment variable.
* Replace websiteURL and websiteKey with values from your target page.
* For Cloudflare Challenge pages, also extract and pass action, data, and pagedata.
* For Cloudflare Challenge pages, also extract and pass action, data, pagedata,
* and the current browser userAgent.
*/

import 'dotenv/config';
Expand All @@ -15,18 +16,17 @@ const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY';
const captchaSolver = new CaptchaClient({ clientKey: apiKey });

// --- Proxyless example ---
// There's no userAgent input field here -- the worker picks its own User-Agent
// while solving and returns it as result.userAgent. The token is tied to that
// browser fingerprint, so submit it with the exact User-Agent the response gives
// you, not one you chose yourself.
// For a Cloudflare Challenge page, pass the current browser User-Agent together
// with action, data, and pagedata. Do not change the browser User-Agent after solving.
try {
const task = new Tasks.TurnstileProxyless({
websiteURL: 'https://example.com/login', // Full URL of the page with Turnstile
websiteKey: '0x4AAAAAAAxxxxxxxxxxxxxxxx' // data-sitekey attribute value
websiteKey: '0x4AAAAAAAxxxxxxxxxxxxxxxx', // data-sitekey attribute value
// Optional fields (pass only if the target site sets them)
// action: 'login', // Value of data-action attribute
// data: 'custom-cdata-value', // Value of data-cdata attribute
// pagedata: 'chl-page-data-value', // Value of chlPageData parameter
// userAgent: 'Mozilla/5.0 ...', // Current browser User-Agent
});
const result = await captchaSolver.solve(task);
// Solution contains { token: "0.zxcv...", userAgent: "Mozilla/5.0 ..." }
Expand Down
12 changes: 6 additions & 6 deletions examples/sync/turnstile.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
* Prerequisites:
* Set the CAPTCHA_API_KEY environment variable.
* Replace websiteURL and websiteKey with values from your target page.
* For Cloudflare Challenge pages, also extract and pass action, data, and pagedata.
* For Cloudflare Challenge pages, also extract and pass action, data, pagedata,
* and the current browser userAgent.
*/

import 'dotenv/config';
Expand All @@ -15,17 +16,16 @@ const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY';
const captchaSolver = new CaptchaClient({ clientKey: apiKey });

// --- Proxyless example ---
// There's no userAgent input field here -- the worker picks its own User-Agent
// while solving and returns it as result.userAgent. The token is tied to that
// browser fingerprint, so submit it with the exact User-Agent the response gives
// you, not one you chose yourself.
// For a Cloudflare Challenge page, pass the current browser User-Agent together
// with action, data, and pagedata. Do not change the browser User-Agent after solving.
const proxylessTask = new Tasks.TurnstileProxyless({
websiteURL: 'https://example.com/login', // Full URL of the page with Turnstile
websiteKey: '0x4AAAAAAAxxxxxxxxxxxxxxxx' // data-sitekey attribute value
websiteKey: '0x4AAAAAAAxxxxxxxxxxxxxxxx', // data-sitekey attribute value
// Optional fields (pass only if the target site sets them)
// action: 'login', // Value of data-action attribute
// data: 'custom-cdata-value', // Value of data-cdata attribute
// pagedata: 'chl-page-data-value', // Value of chlPageData parameter
// userAgent: 'Mozilla/5.0 ...', // Current browser User-Agent
});

captchaSolver.solve(proxylessTask)
Expand Down
14 changes: 9 additions & 5 deletions src/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,21 +340,25 @@ export interface TurnstileProxylessParams {
* the API does not accept `pageData`.
*/
pagedata?: string | null;
/**
* User-Agent of the browser that loaded the challenge. Pass it for
* Cloudflare Challenge pages; it is not needed for a standalone widget.
*/
userAgent?: string | null;
}

/** Parameters of {@link Turnstile}. */
export interface TurnstileParams extends TurnstileProxylessParams, ProxyParams {}

/**
* Cloudflare Turnstile without a proxy (`TurnstileTaskProxyless`).
* There is no `userAgent` input: the worker picks one and returns it in the solution.
* Solution: {@link TurnstileSolution}.
*
* @see https://captcha-solver.com/en/docs/captcha-types#cloudflare-turnstile
*/
export class TurnstileProxyless extends BaseTask<TurnstileSolution> {
constructor({ websiteURL, websiteKey, action = null, data = null, pagedata = null }: TurnstileProxylessParams) {
super({ type: 'TurnstileTaskProxyless', websiteURL, websiteKey, action, data, pagedata });
constructor({ websiteURL, websiteKey, action = null, data = null, pagedata = null, userAgent = null }: TurnstileProxylessParams) {
super({ type: 'TurnstileTaskProxyless', websiteURL, websiteKey, action, data, pagedata, userAgent });
}
}

Expand All @@ -365,8 +369,8 @@ export class TurnstileProxyless extends BaseTask<TurnstileSolution> {
* @see https://captcha-solver.com/en/docs/captcha-types#cloudflare-turnstile
*/
export class Turnstile extends BaseTask<TurnstileSolution> {
constructor({ websiteURL, websiteKey, action = null, data = null, pagedata = null, proxyType, proxyAddress, proxyPort, proxyLogin = null, proxyPassword = null }: TurnstileParams) {
super({ type: 'TurnstileTask', websiteURL, websiteKey, action, data, pagedata, proxyType, proxyAddress, proxyPort, proxyLogin, proxyPassword });
constructor({ websiteURL, websiteKey, action = null, data = null, pagedata = null, userAgent = null, proxyType, proxyAddress, proxyPort, proxyLogin = null, proxyPassword = null }: TurnstileParams) {
super({ type: 'TurnstileTask', websiteURL, websiteKey, action, data, pagedata, userAgent, proxyType, proxyAddress, proxyPort, proxyLogin, proxyPassword });
}
}

Expand Down
2 changes: 1 addition & 1 deletion tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Common to every type's serialization:
| `recaptcha_v2.test.js` | 6 / 1 | the field names `recaptchaDataSValue` and `apiDomain` (not `dataSValue`); `isInvisible`, `userAgent`; proxy variant. `solve()` goes through an intermediate `status: 'processing'` — exactly 3 requests |
| `recaptcha_v2_enterprise.test.js` | 4 / 1 | `enterprisePayload` as an object, `isInvisible`; an exact `toDict()` match for the minimal field set |
| `recaptcha_v3.test.js` | 4 / 1 | `minScore` is required — without it the constructor throws `ValidationError`; `pageAction`, `apiDomain` |
| `turnstile.test.js` | 5 / 1 | the field names `data` and `pagedata` (not `cData` or `pageData` -- `pagedata` is the one field on this type the real API takes lowercase); `userAgent` is rejected as input entirely -- it's response-only on this type |
| `turnstile.test.js` | 5 / 1 | the field names `data` and `pagedata` (not `cData` or `pageData` -- `pagedata` is the one field on this type the real API takes lowercase); browser `userAgent` support for Cloudflare Challenge pages; proxy variant |
| `geetest.test.js` | 7 / 2 | v3: `gt` + `challenge`, no `version` field. v4: `version: 4` + `initParameters`. Plus `geetestApiServerSubdomain` and `risk_type` (never `cookies` -- not a real field on this type). `solve()` is tested separately for v3 and v4 — their solution shapes differ |
| `yandex_smartcaptcha.test.js` | 4 / 1 | `userAgent`, `cookies`; proxy variant |
| `tencent.test.js` | 4 / 1 | `appId`, optional `captchaScript` |
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/sync/turnstile.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ describe('TurnstileProxyless', () => {
expect(result.pageData).toBeUndefined();
});

test('does not accept userAgent as input -- the API only returns it in the solution', () => {
test('includes the browser userAgent for a Cloudflare Challenge', () => {
const task = new Tasks.TurnstileProxyless({
websiteURL: 'https://example.com',
websiteKey: 'test_key',
userAgent: 'Mozilla/5.0'
});

const result = task.toDict();
expect(result.userAgent).toBeUndefined();
expect(result.userAgent).toBe('Mozilla/5.0');
});
});

Expand Down
Loading