@itd-api/turnstile
v0.1.0
Published
Получение токена Cloudflare Turnstile для входа в itd-api через локальный браузер
Downloads
467
Maintainers
Readme
@itd-api/turnstile
Токен Cloudflare Turnstile для входа по логину и паролю в itd-api.
Вход на итд.com требует токен капчи, а получить его можно только в браузере. Этот пакет
поднимает браузер, берёт токен и отдаёт функцию, которая подставляется в auth клиента.
Отдельным пакетом — чтобы itd-api оставался клиентом API: без Playwright в зависимостях,
без требования графической оболочки и без кода, который нужен далеко не всем.
Когда пакет не нужен
Капча участвует только в самом входе по паролю — ни продление сессии, ни обычные запросы, ни realtime её не требуют. Пакет незачем ставить, если:
- сессия уже сохранена в
FileTokenStorage— клиент продлевает её сам; - токены можно скопировать из браузера,
где вы уже вошли: DevTools отдают и access token, и cookie
refresh_token; - токен выдаёт ваш backend или secret manager — тогда подойдёт
auth: { getToken }; - токен капчи приходит из своего источника —
getTurnstileTokenпринимает любую функцию.
Пакет нужен ровно для одного сценария: полностью автоматический вход по email и паролю в Node без участия человека.
Установка
npm i @itd-api/turnstile patchright
npx patchright install chromiumДрайвер подключается динамически, в порядке patchright → playwright → playwright-core:
что из этого установлено, то и берётся. Все три — необязательные одноранговые зависимости,
достаточно любой одной. Любой другой совместимый по API драйвер передаётся через launch.
Какие связки сейчас выдают токен — в разделе Совместимость.
Использование
import { ItdClient } from 'itd-api';
import { FileTokenStorage } from 'itd-api/node';
import { createTurnstileSolver } from '@itd-api/turnstile';
const itd = new ItdClient({
storage: new FileTokenStorage('./.itd-session.json'),
auth: {
email: process.env.ITD_EMAIL!,
password: process.env.ITD_PASSWORD!,
getTurnstileToken: createTurnstileSolver(),
},
});Передаётся именно функция: токен одноразовый и живёт несколько минут, поэтому клиент спрашивает его заново перед каждым входом. Браузер поднимается на время одного вызова и сразу закрывается.
Разовый вызов без клиента:
import { solveTurnstile } from '@itd-api/turnstile';
const token = await solveTurnstile();Запуск на сервере
Браузер по умолчанию запускается с окном. В безоконном режиме виджет проходится заметно
хуже: признаки такого режима видны странице. На сервере без графической оболочки поднимите
виртуальный дисплей — это надёжнее, чем headless: true:
apt install xvfb
xvfb-run -a node bot.jsВ Docker к образу нужны системные библиотеки браузера: npx patchright install --with-deps chromium.
Как это устроено
Пакет не заходит на сайт. Навигация на https://xn--d1ah4a.com/ перехватывается и вместо
настоящей страницы отдаётся своя — с одним виджетом Turnstile. Для браузера origin при этом
настоящий, поэтому привязка ключа к домену не нарушается, а сервер при проверке токена видит
ожидаемый hostname.
Из этого следует остальное:
- пароль в браузер не попадает — форма входа не участвует, вход выполняет сам
itd-api; - ничего не ломается от изменений вёрстки сайта: важен только публичный ключ виджета;
- нет гонки с настоящим запросом входа, а значит и незачем его подвешивать.
Чекбокс живёт в iframe чужого происхождения, до его DOM не дотянуться — клик идёт по
координатам. Отсчёт ведётся от собственного контейнера известного размера, поэтому попадание
не зависит от чужой вёрстки. Координаты слегка разбрасываются, первому касанию предшествует
пауза, а User-Agent не подменяется: заявленная версия, разошедшаяся с реальным движком,
сама по себе служит признаком автоматизации.
Настройки
Все необязательны.
| Параметр | По умолчанию | Что делает |
| --- | --- | --- |
| headless | false | Запуск без окна. См. раздел про сервер. |
| disableSandbox | false | Отключить sandbox Chromium; только для изолированного контейнера. |
| timeout | 60000 | Сколько ждать токен, мс. |
| attempts | 2 | Сколько попыток при таймауте. |
| theme | 'auto' | Оформление виджета. |
| origin | https://xn--d1ah4a.com | Сайт, чей виджет решается. |
| sitekey | ключ итд.com | Публичный ключ виджета. |
| driver | перебор | Какой драйвер брать, когда установлено несколько. |
| executablePath | — | Путь к браузеру, если он лежит не там, где его ищет драйвер. |
| channel | — | Канал браузера, например chrome, вместо сборки из комплекта драйвера. |
| args | — | Дополнительные аргументы командной строки. |
| proxy | — | Прокси для браузера. |
| browser | — | Готовый браузер. Тогда пакет его не запускает и не закрывает. |
| launch | — | Свой запуск браузера. Заменяет все параметры запуска. |
| contextOptions | locale: 'ru-RU', окно 1280×800 | Настройки контекста. Заменяют стандартные целиком. |
| logger | — | Куда писать ход решения, например console.debug. |
Свой драйвер:
createTurnstileSolver({
launch: async () => {
const { chromium } = await import('patchright');
return chromium.launch({ headless: false });
},
});Драйверу, который собирает отпечаток браузера сам, контекст лучше отдать целиком:
createTurnstileSolver({
contextOptions: {},
launch: async () => {
const { Camoufox } = await import('camoufox-js');
return Camoufox({ headless: false, humanize: true });
},
});Ошибки
Всё, что пошло не так, приходит как TurnstileError с полем reason:
| reason | Что делать |
| --- | --- |
| driver-missing | Установить patchright либо передать свой launch. |
| launch-failed | Браузер не запустился: нет исполняемого файла или дисплея. |
| timeout | Виджет не отдал токен. Обычно лечится повтором. |
| widget-error | Виджет отказал; код Cloudflare лежит в widgetCode. |
Код 110200 в widgetCode означает, что ключ не разрешён для указанного домена, —
повторять бессмысленно, и пакет этого не делает.
Совместимость
Виджет пропускает не всякий браузер. Данные на 5 августа 2026 года:
| Драйвер | Версии | Браузер | Токен |
| --- | --- | --- | --- |
| patchright | 1.59.4, 1.60.2, 1.61.1 | Chromium 147, 148, 149 | ✅ |
| playwright | 1.60.0, 1.61.0 | Chromium 148, 149 | ✅ |
| playwright | 1.62.1 | Chromium 151 | ❌ |
| playwright с channel: 'chrome' | 1.61.0 | Google Chrome 150 | ❌ |
| camoufox-js с contextOptions: {} | 0.11.5, 0.12.0 | Camoufox 152 | ✅ |
| camoufox-js с contextOptions: {} | 0.10.2 | Camoufox 152 | ❌ |
| rebrowser-playwright | 1.48.2, 1.49.1, 1.52.0 | Chromium 149 | ❌ |
| playwright-extra + puppeteer-extra-plugin-stealth 2.11 | 4.3.4, 4.3.5, 4.3.6 | Chromium 149 | ❌ |
Всё перечисленное — с окном; headless: true токена не даёт нигде, кроме Camoufox.
Двум последним сборка задана принудительно, чтобы отказ относился к самой библиотеке.
Сборка приезжает вместе с версией драйвера, ею и выбирается — npm i [email protected].
Поставленная отдельно тоже годится: npx @puppeteer/browsers install [email protected]
и путь в executablePath. Версию запущенного браузера пакет называет в сообщении о таймауте.
Таблица пересобирается скриптом scripts/drivers.mjs из исходников пакета:
npm i --no-save patchright playwright camoufox-js
node scripts/drivers.mjsЛицензия
MIT
