Spaces:
Sleeping
Sleeping
File size: 4,470 Bytes
80a4a65 45b98cc 80a4a65 f7a0350 3c7b4e4 80a4a65 f7a0350 80a4a65 45b98cc 80a4a65 3c7b4e4 80a4a65 3c7b4e4 80a4a65 3c7b4e4 45b98cc efbf2ba dacb98a efbf2ba dacb98a efbf2ba f7a0350 efbf2ba dacb98a f7a0350 45b98cc e72b508 80a4a65 f7a0350 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | """``/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
|