api / auth.py
jamelloverz-sketch
PopCorn API v3.5 - Media metadata, people, images, open CORS
c18e004
Raw
History Blame Contribute Delete
2.71 kB
import hmac
import hashlib
import json
import os
import time
import urllib.parse
from fastapi import Request, HTTPException
from config import TELEGRAM_BOT_TOKEN
DEV_MODE = os.environ.get("DEV_MODE", "").lower() in ("true", "1", "yes")
def verify_init_data(init_data: str) -> dict | None:
if DEV_MODE and init_data.startswith("dev_"):
parts = init_data.split("_")
return {
"id": int(parts[1]) if len(parts) > 1 and parts[1].isdigit() else 5703679073,
"first_name": parts[2] if len(parts) > 2 else "Dev",
"last_name": "",
"username": parts[3] if len(parts) > 3 else "devuser",
"language_code": "en",
"is_premium": False,
}
try:
parsed = dict(urllib.parse.parse_qsl(init_data))
except Exception:
return None
hash_val = parsed.pop("hash", None)
if not hash_val:
return None
auth_date = parsed.get("auth_date")
if not auth_date:
return None
try:
if time.time() - int(auth_date) > 86400:
return None
except (ValueError, TypeError):
return None
sorted_items = sorted(parsed.items())
check_str = "\n".join(f"{k}={v}" for k, v in sorted_items)
secret_key = hmac.new(b"WebAppData", TELEGRAM_BOT_TOKEN.encode(), hashlib.sha256).digest()
computed_hash = hmac.new(secret_key, check_str.encode(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(computed_hash, hash_val):
return None
try:
user_info = json.loads(parsed.get("user", "{}"))
except (json.JSONDecodeError, TypeError):
user_info = {}
return {
"id": user_info.get("id"),
"first_name": user_info.get("first_name", ""),
"last_name": user_info.get("last_name", ""),
"username": user_info.get("username", ""),
"language_code": user_info.get("language_code", "en"),
"is_premium": user_info.get("is_premium", False),
"photo_url": user_info.get("photo_url", ""),
}
async def get_current_user(request: Request) -> dict:
auth_header = request.headers.get("Authorization", "")
if not auth_header.startswith("tma "):
raise HTTPException(status_code=401, detail="Missing or invalid Authorization header")
user_data = verify_init_data(auth_header[4:])
if not user_data or not user_data.get("id"):
raise HTTPException(status_code=401, detail="Invalid init data")
return user_data
async def optional_current_user(request: Request) -> dict | None:
auth_header = request.headers.get("Authorization", "")
if not auth_header.startswith("tma "):
return None
return verify_init_data(auth_header[4:])