Skip to main content

TMA Auth Architecture

Архитектурное решение по авторизации в Telegram Mini App.


1. Summary

Решение: Используем initData + HMAC как единственный механизм авторизации TMA. JWT не используется.

Причина: Telegram Mini App имеет специфичный flow, где JWT добавляет complexity без пропорциональной выгоды.

YAGNI

JWT для TMA — overengineering. initData достаточно для безопасной авторизации.


2. ADR (Architectural Decision Record)

Контекст

При проектировании авторизации TMA рассматривались два подхода:

ПодходОписание
initData onlyTelegram initData с HMAC валидацией на каждый запрос
JWT + RefreshinitData только для login, далее JWT токены

Решение

Выбран подход: initData only.

Обоснование

1. Типичный user flow

Пользователь:
1. Открывает TMA через бота/канал
2. Играет 10-30 минут
3. Закрывает TMA
4. Позже открывает снова → НОВЫЙ initData

Ключевой момент: Каждое открытие TMA = новый initData с новым auth_date. Проблема "сессия устарела" возникает только если держать приложение открытым >24 часов — это edge case.

2. initData vs JWT — сравнение

КритерийinitData onlyJWT + Refresh
Сложность~200 LOC~500+ LOC
Безопасность✅ HMAC + auth_date✅ JWT expiry + refresh
UX при истечении"Закрой и открой"Seamless refresh
Когда истекаетПри открытом >24haccess: 15min, refresh: 24h
Частота проблемыРедко (edge case)Никогда

3. Почему JWT избыточен

  1. Новый заход = новый initData — проблема истечения не возникает при нормальном использовании
  2. HMAC достаточен — Telegram гарантирует подлинность данных
  3. auth_date защищает от replay — старые initData отклоняются
  4. Complexity не оправдана — JWT требует:
    • 2 новых endpoint (/auth/login, /auth/refresh)
    • Хранение refresh token (cookie/storage)
    • Логика refresh на frontend
    • Обработка race conditions при refresh

Последствия

Положительные:

  • Простая архитектура
  • Меньше кода для поддержки
  • Меньше точек отказа

Отрицательные:

  • При открытом TMA >24h — экран "Сессия устарела"
  • Пользователь должен переоткрыть через Telegram

Mitigation: Это ожидаемое поведение, не баг. Экран информирует пользователя что делать.


3. Technical Implementation

Flow авторизации

Валидация initData

Файл: backend/src/domains/telegram/utils/telegram-init-data.util.ts

1. Проверка HMAC подписи (crypto.timingSafeEqual)
2. Проверка auth_date:
- Production: до 48h (с warning до 72h)
- Development: 48h строго
3. Парсинг user JSON
4. Валидация telegramId

TTL настройки

КонтекстTTLФайл
Обычные запросы48h (до 72h с warning)telegram-init-data.util.ts:62
activate-session24htelegram-bot.routes.ts:513
activate-session

activate-session — это onboarding endpoint для UTM/referral, не обычная авторизация. Вызывается однократно при первом входе нового пользователя.


4. Когда показывается "Сессия устарела"

Условия

  1. Пользователь открыл TMA
  2. Держал открытым >24 часов без закрытия
  3. Попытался выполнить activate-session (onboarding)

Почему это не проблема

СценарийРезультат
Обычный пользователь (играет 20 мин)✅ Работает
Закрыл и открыл через час✅ Новый initData
Закрыл и открыл через день✅ Новый initData
Держал открытым 25 часов⚠️ "Сессия устарела"

Частота проблемы: менее 1% пользователей.

UI экран

Файл: frontend/src/components/SessionExpiredScreen.tsx

Показывает:

  • Иконку часов
  • "Сессия устарела"
  • "Закройте и откройте заново через Telegram"
  • Кнопку "Закрыть приложение"

5. Сравнение с альтернативами

Почему НЕ JWT

Аргумент за JWTКонтраргумент
"initData не для долгой сессии"Сессия 20 мин, не сутки
"UX ломается при свернул/вернулся"Новый заход = новый initData
"Риск replay attack"HMAC + auth_date достаточно
"Стандарт индустрии"Для web apps, не для TMA

Когда JWT имел бы смысл

  1. Offline режим (кэширование данных)
  2. Cross-platform (web + mobile native)
  3. Long-running background tasks

goLoot не имеет этих требований.


6. FAQ

Q: Что если пользователь свернул TMA и вернулся?

A: initData остаётся в памяти. Если прошло <48h — работает. Если >48h — показываем экран "устарела".

Но на практике: пользователь закроет TMA раньше, при следующем открытии получит новый initData.

Q: Можно ли обновить initData без переоткрытия?

A: Нет. Telegram обновляет initData только при открытии Mini App. Это ограничение платформы.

Q: Почему TTL 48h, а не 24h?

A: 24h было слишком строго для пользователей в разных timezone. 48h даёт buffer для edge cases без ущерба безопасности.

Q: Как защищаемся от replay attack?

A:

  1. HMAC подпись — initData нельзя подделать
  2. auth_date — старые initData отклоняются
  3. HTTPS — трафик зашифрован
  4. Rate limiting — защита от брутфорса


8. References

Код:

  • backend/src/domains/telegram/middleware/telegram-auth.middleware.ts
  • backend/src/domains/telegram/utils/telegram-init-data.util.ts
  • backend/src/common/constants/time.constants.ts
  • frontend/src/services/api-client.ts
  • frontend/src/components/SessionExpiredScreen.tsx

Telegram Docs: