v0.1 - макет интерфейса, аутентификация через логин, аккаунт админа, создание партии в 2 этапа, базовые настройки профиля и группы, переключение между группами, статистика
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
"""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),
|
||||
)
|
||||
Reference in New Issue
Block a user