CyberArena / app /api /auth.py
hassanienabbasitse24's picture
h
e72b508
Raw
History Blame Contribute Delete
4.47 kB
"""``/api/auth`` — proxy to the Supabase ``apex-auth`` edge function."""
import logging
import httpx
from fastapi import APIRouter, Depends, HTTPException, Request
from slowapi import Limiter
from slowapi.util import get_remote_address
from app.core.auth import get_current_user
from app.core.config import SUPABASE_EDGE_URL
from app.types import AuthRequest
log = logging.getLogger("auth-api")
router = APIRouter()
# Rate limiter: 5 auth attempts per minute per IP (prevents brute-force)
limiter = Limiter(key_func=get_remote_address)
@router.post("/api/auth")
@limiter.limit("5/minute")
async def handle_auth(request: Request, req: AuthRequest):
async with httpx.AsyncClient(timeout=30) as client:
resp = await client.post(
f"{SUPABASE_EDGE_URL}/apex-auth",
json={"action": req.action, "email": req.email, "password": req.password, "name": req.name},
)
data = resp.json()
if resp.status_code != 200:
raise HTTPException(status_code=resp.status_code, detail="Authentication failed")
# Extract user_id from various possible fields in the response
user_id = None
# Check for common user identification fields
if "user_id" in data:
user_id = data["user_id"]
elif "id" in data:
user_id = data["id"]
elif "user" in data and isinstance(data["user"], dict):
user_id = data["user"].get("id") or data["user"].get("user_id") or data["user"].get("sub")
# Also check for user_id in JWT token if present in response
if not user_id and "access_token" in data:
try:
import jwt
token_data = jwt.decode(
data["access_token"],
options={"verify_signature": False, "verify_aud": False},
algorithms=["HS256"]
)
user_id = token_data.get("sub")
except Exception:
pass
# Last resort: check if response is just a user object
if not user_id and len(data) == 1 and isinstance(list(data.values())[0], dict):
possible_user = list(data.values())[0]
user_id = possible_user.get("id") or possible_user.get("user_id") or possible_user.get("sub")
if user_id:
data["user_id"] = user_id
log.info("[auth] User logged in, user_id=%s", user_id)
else:
log.warning("[auth] No user_id in auth response - keys: %s", list(data.keys()))
# For debugging: log the full response (masked)
import json
masked_data = json.dumps(data)
if len(masked_data) > 500:
masked_data = masked_data[:500] + "..."
log.warning("[auth] Full response (masked): %s", masked_data)
# Log the response structure (without secrets) to help debug token issues
top_keys = list(data.keys()) if isinstance(data, dict) else type(data).__name__
log.info("[auth] Edge response keys: %s", top_keys)
if "access_token" in data:
log.info("[auth] access_token present (len=%d)", len(str(data["access_token"])))
if "session" in data and isinstance(data["session"], dict):
log.info("[auth] session.access_token present: %s", "access_token" in data["session"])
if "token" in data:
log.info("[auth] token present (len=%d)", len(str(data["token"])))
# Normalize response: promote access_token / session to top level
# if the edge function wrapped them inside a "data" key
if "access_token" not in data and "session" not in data:
nested = data.get("data")
if isinstance(nested, dict):
if "access_token" in nested:
data["access_token"] = nested["access_token"]
log.info("[auth] Promoted access_token from data.%s", "access_token")
if "session" in nested and isinstance(nested["session"], dict):
data["session"] = nested["session"]
log.info("[auth] Promoted session from data.session")
if "user" in nested and isinstance(nested["user"], dict) and "user_id" not in data:
uid = nested["user"].get("id") or nested["user"].get("user_id") or nested["user"].get("sub")
if uid:
data["user_id"] = uid
log.info("[auth] Promoted user_id from data.user")
return data
@router.get("/api/auth/me")
async def get_current_auth_user(user: dict = Depends(get_current_user)):
"""Get current authenticated user information."""
return user