Авторизация — itd.auth
Вход, регистрация, подтверждение по коду, пароли и управление сессиями. Обычно клиент авторизуется сам через опцию auth конструктора; эти методы нужны для ручных сценариев входа. Полное руководство — Авторизация.
Капчу требуют signIn(), signUp() и forgotPassword() (и надстройки над ними), а claimQrLogin() может запросить её отдельным статусом. Токен эти методы берут из опции клиента captcha; поле captcha: { type, token } передаётся, только когда токен уже на руках. Имя поля запроса SDK подставляет сам. Активного провайдера сообщает captchaProvider(), токены одноразовые. В Node виджеты проходит @itd-api/captcha.
Без источника и без токена запрос всё равно уходит на сервер: требовать ли капчу, решает он.
Остальным методам капча не нужна. refresh(), check(), logout() и работа с сессиями обходятся без неё, поэтому готовые токены из браузера или сохранённая сессия позволяют вообще не встречаться с капчей.
Провайдер капчи
captchaProvider(): Promise<CaptchaProvider>Возвращает { provider: 'itd', field: 'token' } либо { provider: 'cloudflare', field: 'turnstileToken' }. Оба значения сервер вправе сменить, поэтому field берётся из ответа, а не выводится из провайдера.
captchaPage(): Promise<CaptchaPage>То же самое плюс url — адрес готовой страницы виджета на домене итд.com, где ключ действителен. Пригодится встроенному браузеру: открыть её и забрать токен, ничего не собирая самому.
Автологину через опцию auth этот вызов не нужен — клиент делает его сам. Ручному входу он подсказывает, какой виджет решать:
const { provider, field } = await itd.auth.captchaProvider();
const token = await solveCaptcha(provider);
await itd.auth.signIn({ email, password, captcha: { type: provider, token, field } });Состояние авторизации
check(): Promise<AuthState>Проверяет состояние авторизации и возвращает { authenticated, banned, user }. Метод работает без токена: тогда authenticated равен false, а user — null.
Вход и регистрация
signUp(credentials: CredentialsWithCaptcha): Promise<string>Регистрирует аккаунт и запускает подтверждение по коду. Возвращает flowToken для verifyOtp().
signIn(credentials: CredentialsWithCaptcha): Promise<SignInResult>Выполняет вход. При успехе токен сохраняется в клиенте автоматически. Если сервер потребовал код, вернётся { status: 'otp_required', flowToken }.
signInWithOtp(input: CredentialsWithCaptcha & { getOtp: () => string | Promise<string> }): Promise<string>Полный вход с подтверждением: код запрашивается функцией getOtp, остальное — само. Возвращает accessToken.
verifyOtp(input: Credentials & { otp: string; flowToken: string }): Promise<string>Подтверждает вход кодом из письма. Токен сохраняется автоматически.
resendOtp(input: { email: string; flowToken: string }): Promise<void>Отправляет код подтверждения повторно.
QR-вход
startQrLogin(): Promise<QrLoginStart>Создаёт изолированную короткоживущую QR-сессию. payload нужно закодировать в QR-код; claimToken нельзя включать в QR-код или передавать сканирующему устройству. captchaRequired предупреждает, что для завершения понадобится капча; свежий токен лучше получать после approved, а не при показе QR-кода.
streamQrLogin(
input: { qrId: string; claimToken: string },
onEvent: (event: QrLoginStreamEvent) => void | Promise<void>,
options?: { signal?: AbortSignal },
): Promise<void>Доставляет простые SSE-события pending, scanned, approved, rejected. После approved вызовите claimQrLogin(); поток сам токен не возвращает. Отмена выполняется стандартным AbortSignal.
Метод делает одну попытку подключения. Если первое событие не пришло за 3 секунды либо поток закрылся, остановите его и вызывайте claimQrLogin() раз в 2 секунды. После approved вызовите claimQrLogin() сразу. Опрос прекращается на authorized, captcha_required, rejected, при истечении QR-сессии или закрытии экрана.
claimQrLogin(input: QrLoginClaimInput): Promise<QrLoginClaim>Проверяет и завершает QR-вход. При authorized клиент автоматически сохраняет access token и cookie новой сессии. После captcha_required следующий вызов добавит токен капчи сам — из опции клиента либо из аргумента вызова. До просьбы сервера токен не запрашивается: опрос идёт в цикле, и поднимать браузер на каждую проверку было бы напрасно. В серверной среде cookie QR-сессии хранятся отдельно; в браузере ими управляет сам браузер.
Подтверждение с другого устройства
Три метода для устройства, где уже есть мобильная сессия: оно сканирует чужой код и решает его судьбу. Обычный web access token сервер отклоняет с QR_APPROVER_NOT_ALLOWED; добавить Android-заголовки после выдачи токена недостаточно — они должны участвовать уже во входе, который создал сессию. Все методы требуют Bearer и принимают { qrId, secret } — оба значения лежат во fragment payload кода (ссылка вида https://итд.com/qr#i=<qrId>&s=<secret>). secret — не claimToken: первый предъявляет тот, кто сканирует, второй — тот, кто код показывает.
scanQrLogin(input: QrLoginSecrets): Promise<QrLoginTarget>
approveQrLogin(input: QrLoginSecrets): Promise<void>
rejectQrLogin(input: QrLoginSecrets): Promise<void>scanQrLogin() отмечает код отсканированным и возвращает описание устройства, которое просит вход, — чтобы человек видел, кого впускает. Показывающая сторона получает при этом статус scanned; вход завершают approveQrLogin() или rejectQrLogin().
const secrets = { qrId, secret }; // значения i и s из fragment отсканированного payload
const target = await itd.auth.scanQrLogin(secrets);
console.log(target.client, target.ipCity); // Chrome Москва
await itd.auth.approveQrLogin(secrets);Сессия
refresh(): Promise<string>Обновляет токен доступа. Параллельные вызовы объединяются в один сетевой запрос. При включённом autoRefresh вручную обычно не нужен. Refresh-токен уходит на сервер cookie refresh_token (тело запроса игнорируется), а в ответе вместе с новым access token приходит новый refresh-токен: прежний в этот момент гаснет. Обновлённая сессия сразу записывается в storage.
hasRefreshSession(): Promise<boolean>Есть ли признак живой сессии обновления (cookie is_auth или строковый refresh-токен). В браузере всегда true. Читает хранилище — верен и до первого запроса.
logout(): Promise<void>Завершает текущую сессию на сервере и очищает локальную.
logoutAll(): Promise<void>Завершает все сессии пользователя и очищает локальную.
signOut(): Promise<void>Забывает сессию локально, не обращаясь к серверу.
Пароли
forgotPassword(input: ForgotPasswordInput): Promise<string>Запрашивает письмо с кодом для сброса. Возвращает flowToken для resetPassword().
resetPassword(input: ResetPasswordInput): Promise<void>Устанавливает новый пароль по коду. Нужны все четыре поля: email, otp, flowToken, newPassword.
resetPasswordWithOtp(input: ForgotPasswordInput & { newPassword: string; getOtp: () => string | Promise<string> }): Promise<void>Полный сброс: код запрашивается функцией getOtp, остальное — само.
changePassword(input: { currentPassword: string; newPassword: string }): Promise<void>Меняет пароль. Требует действующей сессии. При неверном текущем пароле — код ACCOUNT_CURRENT_PASSWORD_INCORRECT.
Управление сессиями
sessions(): Promise<Session[]>Список активных сессий. У текущей isCurrent === true. См. Session.
revokeSession(sessionId: string): Promise<void>Завершает указанную сессию.
revokeOtherSessions(): Promise<void>Завершает все сессии, кроме текущей.
Типы
interface AuthState {
authenticated: boolean;
banned: boolean;
user: AuthUser | null;
}
interface AuthUser {
id: UserId; username: string; displayName: string;
avatar: string; bio: string; verified: boolean;
isPhoneVerified: boolean; roles: string[];
}
interface Credentials {
email: string;
password: string;
}
interface CaptchaToken {
type: CaptchaType; // 'itd' | 'cloudflare' | новый от сервера
token: string;
field?: CaptchaField; // по умолчанию — CAPTCHA_FIELDS[type]
}
type CredentialsWithCaptcha = Credentials & { captcha?: CaptchaToken };
interface ForgotPasswordInput {
email: string;
captcha?: CaptchaToken;
}
interface ResetPasswordInput {
email: string;
otp: string;
flowToken: string;
newPassword: string;
}
type SignInResult =
| { status: 'authenticated'; accessToken: string }
| { status: 'otp_required'; flowToken: string | undefined };
const SignInStatus = { Authenticated: 'authenticated', OtpRequired: 'otp_required' } as const;
interface CaptchaProvider {
provider: CaptchaType;
field: CaptchaField;
}
interface CaptchaPage extends CaptchaProvider {
url: string; // страница виджета на домене итд.com
}
const CaptchaType = { Itd: 'itd', Cloudflare: 'cloudflare' } as const;
const CaptchaField = { Itd: 'token', Cloudflare: 'turnstileToken' } as const;
const CAPTCHA_FIELDS = { itd: 'token', cloudflare: 'turnstileToken' } as const;
interface QrLoginStart {
qrId: string; claimToken: string; payload: string;
expiresIn: number; captchaRequired: boolean;
}
interface QrLoginSecrets {
qrId: string; // параметр `i` из fragment payload
secret: string; // параметр `s` из fragment payload
}
type QrLoginStreamEvent = {
status: 'pending' | 'scanned' | 'approved' | 'rejected';
expiresIn?: number;
};QrLoginTarget — устройство, которое просит вход; приходит из scanQrLogin().
Связанные: AuthInput (как клиент получает доступ), события авторизации itd.on('tokens' | 'authError' | …) — см. Клиент.