Авторизация и сессии
Клиенту нужен access token: он уходит в заголовке Authorization: Bearer … с каждым защищённым запросом. Всё остальное в этом руководстве — способы его получить, продлить и не потерять при перезапуске.
Если аккаунт уже есть, самый короткий путь — взять готовые токены из браузера: капча при этом не нужна.
new ItdClient({ auth: '<accessToken>' }); // разовый скрипт
new ItdClient({ auth: { accessToken, refreshToken } }); // токены из браузера
new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') }); // сохранённая сессия
new ItdClient({ auth: { email, password }, captcha: createCaptchaSolver() }); // вход паролем
new ItdClient({ auth: { getToken: () => vault.read() } }); // токен из внешнего источника
new ItdClient(); // только публичные методыstorage отражает текущее состояние сессии и имеет приоритет, а отсутствующие в нём поля дополняются из auth.
Что чем является
| Значение | Откуда берётся | Сколько живёт | Зачем нужно |
|---|---|---|---|
| access token | ответ sign-in, verify-otp или refresh | 15 минут | подставляется в Authorization: Bearer каждого защищённого запроса |
| refresh token | cookie refresh_token (HttpOnly, путь /api/v1/auth) | до 30 суток, обновляется при каждом продлении | получить новый access token без пароля и без капчи |
deviceId | заводится клиентом при первом запросе | должен пережить перезапуск | заголовок X-Device-Id; сервер различает по нему устройства в списке сессий |
| токен капчи | активный виджет ИТД или Cloudflare | несколько минут, одноразовый | нужен только для входа, регистрации и сброса пароля |
Если токен истечёт в ближайшие 30 секунд, клиент продлит сессию до защищённого запроса. Если срок из токена прочитать нельзя, обновление по-прежнему запускается после 401.
Как это работает
- Истекающий токен обновляется до защищённого запроса.
- Клиент подставляет актуальный access token в защищённый запрос.
- Если сервер отвечает
401, клиент делаетPOST /api/v1/auth/refresh; refresh token передаётся в cookie, тело запроса сервер игнорирует. - В ответе приходит новый access token, а в
Set-Cookie— новый refresh token: прежний в этот момент гасится. - Клиент сохраняет сессию и при необходимости повторяет исходный запрос.
- Параллельные запросы ждут одного продления.
- Продлить нечем или сервер отказал → ошибка приходит вызывающему коду и в событие
authError.
Выберите способ
| Ситуация | Как |
|---|---|
| Разовый скрипт, токен под рукой | auth: '<accessToken>' |
| Аккаунт есть, вход удобно сделать руками в браузере | токены из DevTools |
| Долгоживущий процесс, вход уже был | storage |
| Токен выдаёт серверное приложение, хранилище секретов или другой процесс | auth: { getToken } |
| Автоматический вход по email и паролю в Node | @itd-api/captcha — решает оба провайдера |
Токены из браузера
Способ без капчи: вход выполняете вы сами на сайте, а библиотека получает уже готовую сессию. Подходит для скриптов, ботов и CI.
- Откройте
итд.comи войдите в аккаунт. - Откройте DevTools (
F12) → вкладка Network, обновите страницу и выберите любой запрос к/api/…. В разделе Request Headers найдите строкуauthorization: Bearer eyJ…— всё послеBearerи есть access token. - Вкладка Application (в Firefox — Storage) → Cookies →
https://итд.com. Значение cookierefresh_token— это refresh token. Она помеченаHttpOnlyи ограничена путём/api/v1/auth, поэтому из JavaScript её не прочитать, а в DevTools видно. Рядом лежитis_auth— её копировать не нужно.
import { ItdClient } from 'itd-api';
import { FileTokenStorage } from 'itd-api/node';
const itd = new ItdClient({
auth: {
accessToken: process.env.ITD_ACCESS_TOKEN!,
refreshToken: process.env.ITD_REFRESH_TOKEN!,
},
// Дальше клиент продлевает сессию сам и записывает свежие токены сюда:
// из браузера их больше копировать не придётся.
storage: new FileTokenStorage('./.itd-session.json'),
});
const { authenticated, user } = await itd.auth.check();
console.log(authenticated ? `Вошли как @${user?.username}` : 'Токен не подошёл');Есть только refresh token? Access token клиент получит сам:
const itd = new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') });
await itd.setSession({ refreshToken: process.env.ITD_REFRESH_TOKEN! });
await itd.auth.refresh();WARNING
Продление гасит предыдущий refresh-токен. Если этой же сессией пользуется открытая вкладка браузера, первое же продление в скрипте её разлогинит — и наоборот. Для бота заведите отдельный вход: приватное окно, другой браузер или другое устройство. Все живые сессии видны в itd.auth.sessions(), лишние снимаются revokeSession().
NOTE
Один access token без refresh-токена тоже работает, но продлить его будет нечем: когда сервер начнёт отвечать 401, клиент сообщит об этом событием authError. Для разовых запусков этого достаточно, для долгоживущего процесса — нет.
В браузере передавать refresh-токен строкой бессмысленно: cookie помечена HttpOnly, выставить её из JavaScript нельзя, и продление там работает только на той, что поставил сам сервер для домена приложения.
Токен из внешнего источника
Если токен добывает и обновляет кто-то другой — серверное приложение, хранилище секретов, соседний процесс, — отдайте клиенту функцию. Её спрашивают перед каждым запросом, поэтому источник сам решает, когда обновлять значение:
new ItdClient({ auth: { getToken: () => vault.read() } });Такой клиент не хранит сессию и не продлевает её сам.
Вход по email и паролю
Почему нужна капча
Токен капчи требуют POST /api/v1/auth/sign-in, /sign-up и /forgot-password, а подтверждение QR-входа — по требованию сервера. Продление сессии, обычные запросы и события её не требуют, а сохранённая сессия или токены из браузера обходятся без неё вовсе.
Токен живёт несколько минут и годится на один запрос, поэтому клиенту передают не токен, а его источник — опцию captcha. С ней signIn(), signUp() и forgotPassword() вызываются без поля captcha, а ItdAccounts раздаёт один источник всем аккаунтам.
Сам SDK браузер не запускает: виджеты проходит отдельный @itd-api/captcha.
Когда ни источника, ни токена нет, запрос всё равно уходит на сервер — требовать ли капчу, решает он.
Node: браузер добывает токен сам
npm i @itd-api/captcha patchright
npx patchright install chromiumimport { ItdClient } from 'itd-api';
import { FileTokenStorage } from 'itd-api/node';
import { createCaptchaSolver } from '@itd-api/captcha';
const itd = new ItdClient({
storage: new FileTokenStorage('./.itd-session.json'),
auth: { email: process.env.ITD_EMAIL!, password: process.env.ITD_PASSWORD! },
// SDK читает активного провайдера и просит решить именно его виджет.
captcha: createCaptchaSolver(),
});Пакет не заходит на сайт: навигация на итд.com перехватывается, и виджет отображается на подставной странице с доменом итд.com. Пароль в браузер не попадает: форма входа не участвует, вход выполняет сам itd-api. Со storage браузер понадобится только при первом запуске. Подробности, работа на сервере без графической оболочки и Docker — в документации пакета.
Драйвер браузера берётся тот, что установлен: patchright, playwright или playwright-core. Рабочие связки перечислены в разделе Совместимость.
NOTE
Нарисовать виджет на собственной странице не выйдет: ключи виджетов привязаны к домену итд.com, и на другом домене проверка завершается ошибкой (Cloudflare — кодом 110200). Экспорт TURNSTILE_SITE_KEY пригодится только коду, который выполняется на самом итд.com, — например расширению браузера. Всем остальным остаются токены из браузера, сохранённая сессия или @itd-api/captcha.
Какую капчу проходить
GET /api/v1/auth/captcha/provider сообщает активный вариант: провайдера (itd или cloudflare) и поле, в котором сервер ждёт токен. По умолчанию клиент спрашивает его перед каждым получением токена и кладёт токен ровно в названное поле.
Сервер принимает обе капчи, поэтому виджет можно закрепить — тогда и запроса не будет:
import { createCaptchaSolver, CaptchaType } from '@itd-api/captcha';
new ItdClient({
auth: { email, password },
captcha: createCaptchaSolver({ type: CaptchaType.Cloudflare }), // всегда Turnstile
});Имя поля для закреплённого типа SDK берёт из своей таблицы. Если сервер его переименовал, назовите поле сами: createCaptchaSolver({ type, field: 'c7f2' }).
Свой источник токена
Источником может быть обычная функция: получает имя провайдера, возвращает токен. Подойдёт сервис решения капч, собственный браузер без интерфейса или ручной ввод:
import { CaptchaType } from 'itd-api';
new ItdClient({
auth: { email, password },
captcha: (type) => (type === CaptchaType.Itd ? itdSolver.solve() : cloudflareSolver.solve()),
});Тип и поле закрепляются объектной формой: captcha: { type: CaptchaType.Cloudflare, field: 'c7f2', getToken: () => … }.
Готовый токен вместо источника
Разовый токен принимают и сами методы, и опция auth — форма одна: { type, token } и необязательное field.
const { provider, field } = await itd.auth.captchaProvider();
const token = await solveCaptcha(provider);
await itd.auth.signIn({ email, password, captcha: { type: provider, token, field } });Токен в auth.captcha расходуется ровно на один вход: повторный вход и восстановление сессии пойдут уже к источнику. Токен, переданный в сам вызов, важнее обоих.
Если сервер потребует код из письма
signIn() вернёт { status: 'otp_required', flowToken }, а автоматический вход через опцию auth завершится ошибкой: подтверждение кодом нельзя пройти без участия человека. Используйте методы с getOtp:
const { provider, field } = await itd.auth.captchaProvider();
const captcha = { type: provider, token: await solveCaptcha(provider), field };
await itd.auth.signInWithOtp({
email,
password,
captcha,
getOtp: () => rl.question('Код из письма: '),
});
await itd.auth.resetPasswordWithOtp({
email,
captcha,
newPassword,
getOtp: () => rl.question('Код из письма: '),
});Хранение сессии
Хранилище избавляет от повторного входа: токены переживают перезапуск, а серия входов подряд может привести к временной блокировке аккаунта.
import { ItdClient } from 'itd-api';
import { FileTokenStorage } from 'itd-api/node';
const itd = new ItdClient({
storage: new FileTokenStorage('./.itd-session.json'),
});
const me = await itd.users.me();Если access token истёк, клиент использует refresh-сессию, повторяет исходный запрос и сохраняет обновлённые данные. Refresh-токен обновляется при каждом продлении, и штатные хранилища записывают его автоматически. При собственном хранении сохраняйте сессию после каждого события tokens:
itd.on('tokens', async () => saveSomewhere(await itd.getSession()));
await itd.setSession(await loadFromSomewhere());Проверить возможность продления заранее:
if (await itd.auth.hasRefreshSession()) await itd.auth.refresh();
else redirectToLogin();В браузере метод возвращает true, потому что HttpOnly cookie недоступна JavaScript: это разрешение попробовать refresh, а не гарантия его успеха.
Автоматическое продление отключается через autoRefresh: false.
Что хранится
Полная сессия включает:
- access и refresh tokens;
- cookie;
deviceId.
Вне браузера fetch не ведёт cookie сам, поэтому библиотека хранит их вместе с сессией. deviceId должен переживать перезапуски, иначе сервер будет видеть каждый запуск как новое устройство.
Доступные хранилища:
| Хранилище | Импорт | Среда |
|---|---|---|
MemoryTokenStorage | itd-api | везде |
LocalStorageTokenStorage | itd-api/web | браузер |
SessionStorageTokenStorage | itd-api/web | браузер, текущая page session |
FileTokenStorage | itd-api/node | Node, Bun, Deno |
createTokenStorage(KeyValueStore) | itd-api | Redis, БД, AsyncStorage и другие |
WARNING
Файл сессии содержит токены и cookie: refresh-токен даёт полный доступ к аккаунту, пока сессия не отозвана. Добавьте путь в .gitignore, не печатайте сессию в логах (встроенный logger маскирует токены сам), а скомпрометированную сессию снимите через itd.auth.logoutAll().
Когда сессия теряется
Ошибка продления приходит вызывающему коду и в событие authError:
itd.on('authError', ({ error }) => {
if (isItdApiError(error)) {
console.error(error.code);
}
});| Что видно | Причина | Что делать |
|---|---|---|
CAPTCHA_FAILED, TURNSTILE_VERIFICATION_FAILED | сервер потребовал капчу, а токена в запросе не было или он не подошёл | задать опцию captcha или войти токенами из браузера |
ItdConfigError о капче ИТД или Turnstile | источник не вернул токен для провайдера, выбранного сервером | проверить captcha.getToken — он вызывается с именем провайдера |
422 на signIn | токен капчи просрочен или уже использован | задать источник, а не готовую строку: он даёт свежий токен на каждый вход |
{ status: 'otp_required' } | сервер требует код из письма | signInWithOtp() |
SESSION_EXPIRED | продлевать нечем: нет ни cookie is_auth, ни refresh-токена | войти заново или передать refreshToken |
REFRESH_TOKEN_MISSING | refresh-токен не дошёл до сервера | сохранять и восстанавливать cookie вместе с сессией — это делает любое штатное хранилище |
SESSION_NOT_FOUND, SESSION_REVOKED | сессия завершена на сервере: logoutAll(), снятие сессии, смена пароля | войти заново |
401 на каждом запросе в браузере | другой origin: CORS не пропускает основной API | серверный прокси |
При наличии email и пароля в конфигурации клиент после неудачного продления сам пробует войти заново; отключается опцией reloginOnRefreshFailure: false.
Свои сессии можно посмотреть и снять:
for (const session of await itd.auth.sessions()) {
console.log(session.isCurrent, session.clientName, session.lastUsedAt);
}
await itd.auth.revokeOtherSessions();Частые вопросы
Нужна ли капча при каждом запуске? Нет. Только входу по паролю, регистрации и сбросу пароля. Со storage вход происходит один раз, дальше сессия продлевается сама.
Можно ли обойтись без браузера совсем? Да: один раз скопируйте токены из DevTools — дальше браузер не нужен.
Сколько живёт сессия? Refresh-токен сервер выдаёт на 30 суток и обновляет при каждом продлении, поэтому активный процесс не разлогинивается. Простой дольше срока жизни токена потребует нового входа.
Почему hasRefreshSession() в браузере всегда true? Признак лежит в HttpOnly cookie, которую JavaScript не видит. Метод отвечает «попробовать имеет смысл», а не «точно получится».
Как понять, чей это токен? await itd.auth.check() вернёт { authenticated, banned, user } и работает даже без токена.
Примеры
examples/bot-with-session.mjs— ручной токен активного провайдера капчи при первом входе и сохранение сессии.examples/captcha-login.mjs— автоматическое получение токена капчи через браузер (оба провайдера).examples/browser-tokens.mjs— сессия из токенов, скопированных в DevTools; капча не участвует.examples/qr-login.html— QR-вход в браузере черезstreamQrLogin()с переходом на опрос. Запускается вместе с локальным обратным прокси; капча при необходимости решается на нём через@itd-api/captcha.
Запуск из корня:
ITD_EMAIL=you@example.com ITD_PASSWORD=secret ITD_CAPTCHA=... \
node guides/authentication/examples/bot-with-session.mjs
ITD_EMAIL=you@example.com ITD_PASSWORD=secret \
node guides/authentication/examples/captcha-login.mjs
ITD_ACCESS_TOKEN=eyJ... ITD_REFRESH_TOKEN=... \
node guides/authentication/examples/browser-tokens.mjs
npx patchright install chromium # один раз
npm run example:qr