Skip to main content

Обзор

Cabinet поддерживает два способа авторизации через Telegram: Оба способа работают параллельно. Когда TELEGRAM_OIDC_ENABLED=true, Cabinet показывает кастомную кнопку с popup-авторизацией. Иначе — стандартный iframe-виджет Telegram.
Требуется CABINET_ENABLED=true и заполненный CABINET_URL.

Telegram OIDC (рекомендуемый)

OIDC (OpenID Connect) — стандартный протокол авторизации. Telegram реализует его через oauth.telegram.org с поддержкой PKCE. Официальная документация Telegram: Telegram Login — OpenID Connect

Эндпоинты протокола

Шаг 1: Регистрация приложения в BotFather

  1. Откройте Mini App @BotFather
  2. Выберите вашего бота
  3. Перейдите в Bot SettingsWeb Login
  4. Зарегистрируйте Allowed URLs — укажите все домены, с которых будет выполняться авторизация:
    • Origin кабинета: https://cabinet.example.com
    • Redirect URI (если используете Authorization Code Flow напрямую): https://cabinet.example.com/auth/telegram/callback
Telegram обрабатывает вход только с предварительно зарегистрированных URL. Без регистрации домена авторизация не будет работать.
  1. BotFather отобразит Client ID и Client Secret — сохраните их
Client ID — это числовой ID вашего бота (тот же, что в BOT_TOKEN до двоеточия). Например, для токена 1234567890:AABBCCdd... Client ID = 1234567890.
Рекомендация Telegram: установите аватар бота, совпадающий с логотипом вашего сайта, а имя бота должно отражать связь с сервисом. Пользователь увидит аватар и имя бота при подтверждении авторизации.

Шаг 2: Настройка .env бота

Перезапустите бота:

Доступные scopes

Cabinet запрашивает scopes openid и profile. Scope phone не используется.

Структура id_token

После авторизации Telegram возвращает JWT id_token с подписью RS256. Пример декодированного токена:

Как работает (пошагово)

Детали каждого шага:
  1. Пользователь открывает страницу входа Cabinet
  2. Frontend запрашивает конфигурацию виджета с API бота (публичный эндпоинт, без авторизации)
  3. Если oidc_enabled=true — загружается JavaScript SDK Telegram (telegram-login.js?3)
  4. SDK инициализируется с client_id и callback-функцией:
  5. Пользователь нажимает кнопку «Войти через Telegram»
  6. SDK открывает popup-окно авторизации на oauth.telegram.org
  7. Пользователь видит имя и аватар бота, подтверждает вход
  8. Popup закрывается, SDK вызывает callback с {id_token: "eyJ…"} или {error: "..."}
  9. Frontend отправляет id_token на API бота
  10. Бот загружает публичные ключи JWKS (кэшируются на 1 час)
  11. Бот валидирует JWT-подпись и claims
  12. Бот создаёт/находит пользователя, возвращает JWT-токены Cabinet
  13. Пользователь авторизован и перенаправляется на главную страницу

JavaScript SDK

Cabinet использует Telegram Login SDK. Доступные методы: Параметры инициализации (InitOptions): Структура callback:
Данные из user в callback — только для предварительного отображения. Для авторизации всегда используйте id_token с серверной валидацией.

Валидация id_token на сервере

Бот выполняет следующие проверки при получении id_token:
  1. Подпись — загружает публичные ключи JWKS, находит ключ по kid из заголовка JWT, проверяет подпись RS256
  2. Издательiss должен быть https://oauth.telegram.org
  3. Аудиторияaud должен совпадать с TELEGRAM_OIDC_CLIENT_ID
  4. Срок действияexp не должен быть в прошлом
  5. Обязательные claims — проверяется наличие exp, iat, iss, aud, sub
  6. Replay-защита — каждый id_token может быть использован только один раз (хэш хранится до 10 минут)

Ротация ключей (JWKS)

Telegram может ротировать ключи подписи. Бот автоматически обновляет JWKS-кэш:

Rate Limiting

Эндпоинт POST /cabinet/auth/telegram/oidc защищён rate limiter:
  • 10 запросов в 60 секунд на один IP-адрес
  • При превышении — HTTP 429 с заголовком Retry-After
  • Режим fail-closed: при ошибке rate limiter запросы отклоняются

Создание и привязка аккаунтов

При авторизации через OIDC:
  • Существующий пользователь (по Telegram ID) — выполняется вход, обновляются поля first_name, username, last_name из актуальных claims
  • Новый пользователь — создаётся аккаунт с данными из claims. Если передан referral_code или campaign_slug — применяются соответствующие бонусы
  • Неактивный аккаунт — авторизация отклоняется с HTTP 403

Совместимость с другими OIDC-системами

Telegram OIDC совместим со стандартными OIDC-провайдерами и может использоваться с:
  • Keycloak — как внешний Identity Provider
  • Authentik — через Generic OIDC
  • Auth0 — через Custom Social Connection
Telegram не предоставляет отдельный UserInfo endpoint. Все claims содержатся в id_token. Некоторые OIDC-библиотеки могут требовать настройки для пропуска запроса UserInfo.

Telegram Widget (legacy)

Стандартный iframe-виджет Telegram. Используется, когда OIDC отключен (TELEGRAM_OIDC_ENABLED=false).

Настройка домена в BotFather

  1. Откройте @BotFather в Telegram
  2. Выберите вашего бота
  3. Bot SettingsDomain → укажите домен кабинета (например, cabinet.example.com)
Без привязки домена в BotFather виджет авторизации не будет работать. Telegram проверяет, что виджет загружается с авторизованного домена.

Кастомизация виджета

Внешний вид виджета настраивается через переменные окружения или через Admin Cabinet → Настройки:
Настройки виджета можно менять без перезапуска бота через Admin Cabinet → Настройки. Значения из базы данных имеют приоритет над переменными окружения.

Как работает

  1. Cabinet загружает iframe-виджет Telegram Login на странице входа
  2. Пользователь нажимает кнопку и подтверждает вход в Telegram
  3. Telegram возвращает данные пользователя (id, first_name, auth_date, hash) с HMAC-SHA256 подписью
  4. Cabinet отправляет данные на API бота
  5. Бот проверяет подпись через SHA256(BOT_TOKEN) и валидирует auth_date (не старше 24 часов)
  6. Бот возвращает JWT-токены для авторизованной сессии

OIDC vs Widget — сравнение


Конфигурационный API

GET /cabinet/branding/telegram-widget

Публичный эндпоинт (без авторизации). Возвращает конфигурацию для страницы входа. Ответ:
Frontend использует oidc_enabled и oidc_client_id чтобы выбрать способ авторизации: OIDC popup или legacy widget.

POST /cabinet/auth/telegram/oidc

Валидация id_token и авторизация пользователя. Запрос:
Ответ (200):
Ошибки:

Итоговая настройка .env

Пример с виджетом и OIDC:

Проверка

API: проверка конфигурации

Ответ должен содержать oidc_enabled: true и oidc_client_id с ID бота.

Widget (если OIDC выключен)

  1. Откройте Cabinet в браузере
  2. На экране входа должна появиться iframe-кнопка Telegram
  3. Нажмите — откроется авторизация Telegram
  4. После подтверждения — вход в Cabinet

OIDC (если включен)

  1. На странице входа отображается кастомная кнопка «Войти через Telegram»
  2. При нажатии откроется popup-окно авторизации
  3. После подтверждения popup закроется и пользователь авторизован

Устранение проблем

Кнопка «Войти через Telegram» не появляется

  1. Проверьте конфигурацию: curl -s https://hooks.example.com/cabinet/branding/telegram-widget | jq
  2. Если oidc_enabled: false:
    • Проверьте TELEGRAM_OIDC_ENABLED=true в .env
    • Проверьте что TELEGRAM_OIDC_CLIENT_ID заполнен
    • Перезапустите бота
  3. Если OIDC выключен, но виджет тоже не появляется:
    • Домен кабинета добавлен в BotFather → Bot Settings → Domain?
    • VITE_TELEGRAM_BOT_USERNAME указан верно (без @)?
    • Страница загружается по HTTPS?
  1. URL кабинета зарегистрирован в BotFather → Bot Settings → Web Login → Allowed URLs?
  2. Убедитесь что домен в CABINET_URL совпадает с зарегистрированным

”Telegram OIDC is not configured” (400)

  1. TELEGRAM_OIDC_ENABLED=true в .env?
  2. TELEGRAM_OIDC_CLIENT_ID не пустой?
  3. Перезапустили бота после изменений?

”Invalid or expired Telegram OIDC token” (401)

“Unknown kid in id_token” в логах

Telegram ротировал ключи подписи. Бот автоматически обновит JWKS. Если ошибка повторяется:
  1. Проверьте доступность https://oauth.telegram.org/.well-known/jwks.json с вашего сервера:
  2. Убедитесь что нет блокировки исходящих HTTPS-запросов к oauth.telegram.org
  3. Проверьте DNS-резолвинг:

Rate limit (429)

Эндпоинт OIDC ограничен 10 запросами в 60 секунд на IP. Это защита от брутфорса. В нормальной работе лимит не должен срабатывать. Если срабатывает в production:
  • Проверьте что reverse proxy передаёт реальный IP пользователя (заголовок X-Forwarded-For)
  • Без этого все запросы идут с IP прокси и быстро исчерпывают лимит