Files
ForbiddenStarsApp/backend/app/auth/telegram.py
T

60 lines
2.2 KiB
Python

"""Telegram Login Widget провайдер.
Проверяет подпись данных виджета (HMAC-SHA256 ключом SHA256(BOT_TOKEN)) и свежесть
auth_date. Нужны TELEGRAM_BOT_TOKEN (+ TELEGRAM_BOT_USERNAME для виджета на фронте).
Доступен и в dev, и в prod (в prod — единственный метод входа).
"""
from __future__ import annotations
import hashlib
import hmac
import time
from typing import Any
from app.auth.provider import AuthProvider, ExternalIdentity
from app.core.config import settings
from app.core.errors import AuthError
_MAX_AUTH_AGE_SECONDS = 86400 # сутки
class TelegramProvider(AuthProvider):
name = "telegram"
def authenticate(self, payload: dict[str, Any]) -> ExternalIdentity:
token = settings.telegram_bot_token
if not token:
raise AuthError("Telegram-вход не настроен (нет TELEGRAM_BOT_TOKEN).")
data = {k: v for k, v in payload.items() if k != "hash" and v is not None}
received_hash = payload.get("hash")
if not received_hash:
raise AuthError("Отсутствует подпись Telegram.")
check_string = "\n".join(f"{k}={data[k]}" for k in sorted(data))
secret_key = hashlib.sha256(token.encode("utf-8")).digest()
computed = hmac.new(
secret_key, check_string.encode("utf-8"), hashlib.sha256
).hexdigest()
if not hmac.compare_digest(computed, str(received_hash)):
raise AuthError("Подпись Telegram не прошла проверку.")
auth_date = int(data.get("auth_date", 0))
if auth_date and time.time() - auth_date > _MAX_AUTH_AGE_SECONDS:
raise AuthError("Срок действия данных Telegram истёк.")
tg_id = int(data["id"])
username = data.get("username")
first = data.get("first_name", "")
last = data.get("last_name", "")
suggested = username or (f"{first} {last}".strip()) or str(tg_id)
return ExternalIdentity(
provider=self.name,
external_id=str(tg_id),
suggested_nickname=suggested,
telegram_id=tg_id,
raw=dict(payload),
)