126 lines
4.8 KiB
Python
126 lines
4.8 KiB
Python
|
|
"""
|
||
|
|
Telegram Mini App authentication middleware
|
||
|
|
"""
|
||
|
|
from typing import Optional
|
||
|
|
from fastapi import Request, HTTPException, status
|
||
|
|
from database import users_collection
|
||
|
|
from utils.telegram_initdata import validate_init_data, normalize_telegram_user
|
||
|
|
from config import settings
|
||
|
|
|
||
|
|
|
||
|
|
async def authenticate_telegram_moderation(request: Request) -> Optional[dict]:
|
||
|
|
"""
|
||
|
|
Authenticate user via Telegram Mini App initData
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
User dict if authenticated, None otherwise
|
||
|
|
|
||
|
|
Raises:
|
||
|
|
HTTPException: If authentication fails
|
||
|
|
"""
|
||
|
|
# Get initData from headers
|
||
|
|
auth_header = request.headers.get('authorization', '')
|
||
|
|
init_data_raw = None
|
||
|
|
|
||
|
|
if auth_header.startswith('tma '):
|
||
|
|
init_data_raw = auth_header[4:].strip()
|
||
|
|
|
||
|
|
if not init_data_raw:
|
||
|
|
init_data_raw = request.headers.get('x-telegram-init-data')
|
||
|
|
if init_data_raw:
|
||
|
|
init_data_raw = init_data_raw.strip()
|
||
|
|
|
||
|
|
if not init_data_raw:
|
||
|
|
print('[TelegramAuth] No initData found in headers')
|
||
|
|
return None
|
||
|
|
|
||
|
|
try:
|
||
|
|
# Validate initData
|
||
|
|
payload = validate_init_data(init_data_raw, settings.MODERATION_BOT_TOKEN)
|
||
|
|
|
||
|
|
telegram_user = payload.get('user')
|
||
|
|
if not telegram_user:
|
||
|
|
print('[TelegramAuth] No user data in initData')
|
||
|
|
raise HTTPException(
|
||
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||
|
|
detail='Отсутствуют данные пользователя в initData'
|
||
|
|
)
|
||
|
|
|
||
|
|
# Normalize user data
|
||
|
|
normalized_user = normalize_telegram_user(telegram_user)
|
||
|
|
telegram_id = normalized_user.get('id')
|
||
|
|
|
||
|
|
if not telegram_id:
|
||
|
|
raise HTTPException(
|
||
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||
|
|
detail='Неверный ID пользователя'
|
||
|
|
)
|
||
|
|
|
||
|
|
print(f'[TelegramAuth] Validated initData for telegramId={telegram_id}')
|
||
|
|
|
||
|
|
# Find or create user
|
||
|
|
from bson import ObjectId
|
||
|
|
user = await users_collection().find_one({'telegramId': telegram_id})
|
||
|
|
|
||
|
|
if not user:
|
||
|
|
# Create new user
|
||
|
|
from datetime import datetime
|
||
|
|
user_doc = {
|
||
|
|
'telegramId': telegram_id,
|
||
|
|
'username': normalized_user.get('username') or normalized_user.get('firstName') or 'user',
|
||
|
|
'firstName': normalized_user.get('firstName'),
|
||
|
|
'lastName': normalized_user.get('lastName'),
|
||
|
|
'photoUrl': normalized_user.get('photoUrl'),
|
||
|
|
'role': 'user',
|
||
|
|
'createdAt': datetime.utcnow()
|
||
|
|
}
|
||
|
|
result = await users_collection().insert_one(user_doc)
|
||
|
|
user = await users_collection().find_one({'_id': result.inserted_id})
|
||
|
|
print(f'[TelegramAuth] Created new user: {telegram_id}')
|
||
|
|
else:
|
||
|
|
# Update user data if needed
|
||
|
|
update_fields = {}
|
||
|
|
if normalized_user.get('username') and not user.get('username'):
|
||
|
|
update_fields['username'] = normalized_user.get('username')
|
||
|
|
if normalized_user.get('firstName') and not user.get('firstName'):
|
||
|
|
update_fields['firstName'] = normalized_user.get('firstName')
|
||
|
|
if normalized_user.get('lastName') is not None:
|
||
|
|
update_fields['lastName'] = normalized_user.get('lastName')
|
||
|
|
if normalized_user.get('photoUrl') and not user.get('photoUrl'):
|
||
|
|
update_fields['photoUrl'] = normalized_user.get('photoUrl')
|
||
|
|
|
||
|
|
if update_fields:
|
||
|
|
from datetime import datetime
|
||
|
|
update_fields['lastActiveAt'] = datetime.utcnow()
|
||
|
|
await users_collection().update_one(
|
||
|
|
{'_id': user['_id']},
|
||
|
|
{'$set': update_fields}
|
||
|
|
)
|
||
|
|
user.update(update_fields)
|
||
|
|
|
||
|
|
if user.get('banned'):
|
||
|
|
raise HTTPException(
|
||
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
||
|
|
detail='Пользователь заблокирован'
|
||
|
|
)
|
||
|
|
|
||
|
|
return user
|
||
|
|
|
||
|
|
except HTTPException:
|
||
|
|
raise
|
||
|
|
except ValueError as e:
|
||
|
|
print(f'[TelegramAuth] Validation error: {e}')
|
||
|
|
raise HTTPException(
|
||
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||
|
|
detail=f'{str(e)}. Используйте официальный клиент.'
|
||
|
|
)
|
||
|
|
except Exception as e:
|
||
|
|
print(f'[TelegramAuth] Error: {type(e).__name__}: {e}')
|
||
|
|
import traceback
|
||
|
|
traceback.print_exc()
|
||
|
|
raise HTTPException(
|
||
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||
|
|
detail='Ошибка авторизации. Используйте официальный клиент.'
|
||
|
|
)
|
||
|
|
|