TMA Auth Architecture
Архитектурное решение по авторизации в Telegram Mini App.
1. Summary
Решение: Используем initData + HMAC как единственный механизм авторизации TMA. JWT не используется.
Причина: Telegram Mini App имеет специфичный flow, где JWT добавляет complexity без пропорциональной выгоды.
JWT для TMA — overengineering. initData достаточно для безопасной авторизации.
2. ADR (Architectural Decision Record)
Контекст
При проектировании авторизации TMA рассматривались два подхода:
| Подход | Описание |
|---|---|
| initData only | Telegram initData с HMAC валидацией на каждый запрос |
| JWT + Refresh | initData только для 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 only | JWT + Refresh |
|---|---|---|
| Сложность | ~200 LOC | ~500+ LOC |
| Безопасность | ✅ HMAC + auth_date | ✅ JWT expiry + refresh |
| UX при истечении | "Закрой и открой" | Seamless refresh |
| Когда истекает | При открытом >24h | access: 15min, refresh: 24h |
| Частота проблемы | Редко (edge case) | Никогда |
3. Почему JWT избыточен
- Новый заход = новый initData — проблема истечения не возникает при нормальном использовании
- HMAC достаточен — Telegram гарантирует подлинность данных
- auth_date защищает от replay — старые initData отклоняются
- Complexity не оправдана — JWT требует:
- 2 новых endpoint (
/auth/login,/auth/refresh) - Хранение refresh token (cookie/storage)
- Логика refresh на frontend
- Обработка race conditions при refresh
- 2 новых endpoint (
Последствия
Положительные:
- Простая архитектура
- Меньше кода для поддержки
- Меньше точек отказа
Отрицательные:
- При открытом 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-session | 24h | telegram-bot.routes.ts:513 |
activate-session — это onboarding endpoint для UTM/referral, не обычная авторизация. Вызывается однократно при первом входе нового пользователя.
4. Когда показывается "Сессия устарела"
Условия
- Пользователь открыл TMA
- Держал открытым >24 часов без закрытия
- Попытался выполнить
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 имел бы смысл
- Offline режим (кэширование данных)
- Cross-platform (web + mobile native)
- 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:
- HMAC подпись — initData нельзя подделать
- auth_date — старые initData отклоняются
- HTTPS — трафик зашифрован
- Rate limiting — защита от брутфорса
7. Related
- Security Matrix — полная матрица защит
- Telegram Bot — Telegram интеграция
- Referrals — реферальная система (InviteSession)
- UTM Tracking — UTM tracking (InviteSession)
8. References
Код:
backend/src/domains/telegram/middleware/telegram-auth.middleware.tsbackend/src/domains/telegram/utils/telegram-init-data.util.tsbackend/src/common/constants/time.constants.tsfrontend/src/services/api-client.tsfrontend/src/components/SessionExpiredScreen.tsx
Telegram Docs: