Files
ForbiddenStarsApp/backend/app/auth/telegram.py
T
NotBigGhostandClaude Opus 5 ec1e94119c Чистка: устаревшие комментарии и подсказки, export-скрипт, мусорный файл
Комментарии про методы входа приведены к факту (логин/пароль и Telegram везде,
stub только в development): config.py, .env.example, telegram.py, dev_stub.py,
dev_auth.py, login.py. admin_login.py больше не обещает «строже игроцкого» —
описаны реальные лимиты; client_ip — адрес за прокси уже даёт uvicorn.

Подсказки деплоя: run.ps1/run.sh (build-push на ПК, `up -d` на Pi), compose,
.env.example (build-push.ps1), Caddyfile (туннель-контейнер вместо autossh);
ratelimit.py ссылается на core/events.py вместо CLAUDE.md.

Удалены неиспользуемые scripts/export-prod.sh (с блоком export-ignore в
.gitattributes и упоминаниями в README), db/init_db.py и случайно закоммиченный
файл «h -u origin dev…». #70

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqSoRj99iwVEH5U5fnZgsd
2026-09-19 03:12:16 +03:00

60 lines
2.3 KiB
Python

"""Telegram Login Widget провайдер.
Проверяет подпись данных виджета (HMAC-SHA256 ключом SHA256(BOT_TOKEN)) и свежесть
auth_date. Нужны TELEGRAM_BOT_TOKEN (+ TELEGRAM_BOT_USERNAME для виджета на фронте).
Доступен во всех окружениях — наряду со входом по логину и паролю.
"""
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),
)