Files
marquiz-metrics/app/api/v1/endpoints/auth.py
13orlov 37ebd8f694
All checks were successful
continuous-integration/drone/push Build is passing
feat(auth): Add endpoint to get user info by token and fix imports
2025-08-24 22:26:27 +01:00

93 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from fastapi import APIRouter, HTTPException, status, Depends
from fastapi.responses import JSONResponse
import httpx
from loguru import logger
from typing import Annotated
from app.core.config import settings
# ИСПРАВЛЕНИЕ ЗДЕСЬ
from app.api.v1.schemas.auth import TokenRequest, TokenResponse, UserInfoResponse
# --- ДОБАВЛЕНО ДЛЯ НОВОГО ЭНДПОИНТА ---
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
router = APIRouter()
bearer_scheme = HTTPBearer()
YANDEX_TOKEN_URL = "https://oauth.yandex.ru/token"
# --- ДОБАВЛЕНО ДЛЯ НОВОГО ЭНДПОИНТА ---
YANDEX_USERINFO_URL = "https://login.yandex.ru/info"
@router.post("/token", response_model=TokenResponse, summary="Обмен кода авторизации на токен доступа")
async def exchange_code_for_token(request: TokenRequest):
"""
Принимает временный 'code' от фронтенда, обменивает его на 'access_token'
у Яндекса и возвращает токен клиенту.
"""
logger.info("Attempting to exchange authorization code for an access token.")
payload = {
'grant_type': 'authorization_code',
'code': request.code,
'client_id': settings.YANDEX_CLIENT_ID,
'client_secret': settings.YANDEX_CLIENT_SECRET
}
try:
async with httpx.AsyncClient() as client:
response = await client.post(YANDEX_TOKEN_URL, data=payload)
response.raise_for_status()
token_data = response.json()
logger.success("Successfully received access token from Yandex.")
return TokenResponse(
access_token=token_data.get("access_token"),
token_type=token_data.get("token_type", "bearer")
)
except httpx.HTTPStatusError as e:
error_details = e.response.json()
logger.error(f"Yandex OAuth error: {e.response.status_code} - {error_details}")
raise HTTPException(
status_code=e.response.status_code,
detail=f"Yandex OAuth error: {error_details.get('error_description', 'Unknown error')}"
)
except Exception as e:
logger.opt(exception=True).error("An unexpected error occurred during token exchange.")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="An internal server error occurred."
)
# --- НОВЫЙ ЭНДПОИНТ ---
@router.get("/userinfo", response_model=UserInfoResponse, summary="Получение информации о пользователе")
async def get_user_info(credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)]):
"""
Используя access_token, получает информацию о пользователе Яндекса.
Токен должен быть передан в заголовке 'Authorization: Bearer <token>'.
"""
token = credentials.credentials
headers = {'Authorization': f'OAuth {token}'}
try:
async with httpx.AsyncClient() as client:
response = await client.get(YANDEX_USERINFO_URL, headers=headers)
response.raise_for_status()
user_data = response.json()
logger.info(f"Fetched user info for login: {user_data.get('login')}")
return UserInfoResponse(**user_data)
except httpx.HTTPStatusError as e:
error_details = e.response.json()
logger.error(f"Yandex UserInfo API error: {e.response.status_code} - {error_details}")
raise HTTPException(
status_code=e.response.status_code,
detail=f"Yandex API Error: {error_details.get('error_description', 'Unknown error')}"
)
except Exception:
logger.opt(exception=True).error("An unexpected error occurred while fetching user info.")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="An internal server error occurred."
)