Files
DoC/backend/api/routers/google_auth.py
T
Mireya Cueto Garrido 6d87a32bc4 Mejorar verificacion por email y OAuth de Google para despliegue en sinbad2.
Endurece el envio SMTP (EHLO/STARTTLS, timeouts, errores tipados y logging), centraliza el manejo de fallos en registro y reenvio, codifica la redirect_uri de Google OAuth y documenta la configuracion de produccion en .env.example.
2026-05-21 12:14:17 +02:00

123 lines
3.5 KiB
Python

from datetime import datetime, timedelta
from urllib.parse import quote
import httpx
import jwt
import os
from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import RedirectResponse
from api.database.mongodb import users_collection
router = APIRouter(prefix="/auth/google", tags=["auth"])
GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
REDIRECT_URI = os.getenv("GOOGLE_REDIRECT_URI")
SECRET_KEY = os.getenv("SECRET_KEY")
FRONTEND_URL = os.getenv("FRONTEND_URL", "http://localhost:5173")
@router.get("/login")
async def google_login():
if not GOOGLE_CLIENT_ID or not REDIRECT_URI:
raise HTTPException(
status_code=500,
detail="GOOGLE_CLIENT_ID o GOOGLE_REDIRECT_URI no configurados",
)
redirect_uri = quote(REDIRECT_URI, safe="")
google_auth_url = (
"https://accounts.google.com/o/oauth2/auth"
"?response_type=code"
f"&client_id={GOOGLE_CLIENT_ID}"
f"&redirect_uri={redirect_uri}"
"&scope=openid%20email%20profile"
"&access_type=offline"
"&prompt=consent"
)
return RedirectResponse(google_auth_url)
@router.get("/callback")
async def google_callback(request: Request):
code = request.query_params.get("code")
if not code:
raise HTTPException(status_code=400, detail="Missing code parameter")
token_url = "https://oauth2.googleapis.com/token"
data = {
"code": code,
"client_id": GOOGLE_CLIENT_ID,
"client_secret": GOOGLE_CLIENT_SECRET,
"redirect_uri": REDIRECT_URI,
"grant_type": "authorization_code",
}
async with httpx.AsyncClient() as client:
token_response = await client.post(token_url, data=data)
token_json = token_response.json()
if "access_token" not in token_json:
raise HTTPException(status_code=400, detail=token_json)
access_token = token_json["access_token"]
async with httpx.AsyncClient() as client:
userinfo = await client.get(
"https://www.googleapis.com/oauth2/v2/userinfo",
headers={"Authorization": f"Bearer {access_token}"}
)
user_data = userinfo.json()
google_id = user_data["id"]
email = user_data["email"]
name = user_data.get("name", "Usuario")
user = await users_collection.find_one({"email": email})
if not user:
new_user = {
"username": name,
"email": email,
"password_hash": None,
"google_id": google_id,
"history": [],
"is_email_verified": True,
"email_verified_at": datetime.utcnow(),
}
result = await users_collection.insert_one(new_user)
user_id = result.inserted_id
else:
user_id = user["_id"]
token = jwt.encode(
{
"sub": str(user_id),
"email": email,
"name": name,
"exp": datetime.utcnow() + timedelta(hours=24)
},
SECRET_KEY,
algorithm="HS256"
)
await users_collection.update_one(
{"_id": user_id},
{
"$set": {
"token": token,
"is_email_verified": True,
},
"$unset": {
"email_verification_code_hash": "",
"email_verification_expires_at": "",
"email_verification_attempts": "",
"email_verification_requested_at": "",
},
},
)
return RedirectResponse(f"{FRONTEND_URL}/login?token={token}")