Spaces:
Running
Running
fix: use Supabase Auth for real access tokens on login/signup
Browse files- auth/routes.py +63 -21
auth/routes.py
CHANGED
|
@@ -3,6 +3,7 @@ from fastapi import APIRouter, HTTPException
|
|
| 3 |
from pydantic import BaseModel
|
| 4 |
from typing import Optional
|
| 5 |
|
|
|
|
| 6 |
from src.store import create_user, get_user_by_email
|
| 7 |
|
| 8 |
router = APIRouter(prefix="/api/auth", tags=["Auth"])
|
|
@@ -27,50 +28,91 @@ class GoogleAuthRequest(BaseModel):
|
|
| 27 |
|
| 28 |
@router.post("/login")
|
| 29 |
async def login(body: LoginRequest):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
user = get_user_by_email(body.email)
|
| 31 |
if not user or user.get("password") != body.password:
|
| 32 |
raise HTTPException(401, "Invalid email or password")
|
| 33 |
-
token = str(uuid.uuid4())
|
| 34 |
return {
|
| 35 |
-
"token":
|
| 36 |
-
"user": {
|
| 37 |
-
"id": user["id"],
|
| 38 |
-
"name": user["name"],
|
| 39 |
-
"email": user["email"],
|
| 40 |
-
},
|
| 41 |
}
|
| 42 |
|
| 43 |
|
| 44 |
@router.post("/signup")
|
| 45 |
async def signup(body: SignupRequest):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
try:
|
| 47 |
user = create_user(body.name, body.email, body.password)
|
| 48 |
except ValueError as e:
|
| 49 |
raise HTTPException(400, str(e))
|
| 50 |
-
token = str(uuid.uuid4())
|
| 51 |
return {
|
| 52 |
-
"token":
|
| 53 |
-
"user": {
|
| 54 |
-
"id": user["id"],
|
| 55 |
-
"name": user["name"],
|
| 56 |
-
"email": user["email"],
|
| 57 |
-
},
|
| 58 |
}
|
| 59 |
|
| 60 |
|
| 61 |
@router.post("/google")
|
| 62 |
async def google_auth(body: GoogleAuthRequest):
|
|
|
|
|
|
|
| 63 |
email = body.email or f"google_{uuid.uuid4().hex[:8]}@google.com"
|
| 64 |
name = body.name or "Google User"
|
| 65 |
user = get_user_by_email(email)
|
| 66 |
if not user:
|
| 67 |
user = create_user(name, email, "")
|
| 68 |
-
token = str(uuid.uuid4())
|
| 69 |
return {
|
| 70 |
-
"token":
|
| 71 |
-
"user": {
|
| 72 |
-
"id": user["id"],
|
| 73 |
-
"name": user["name"],
|
| 74 |
-
"email": user["email"],
|
| 75 |
-
},
|
| 76 |
}
|
|
|
|
| 3 |
from pydantic import BaseModel
|
| 4 |
from typing import Optional
|
| 5 |
|
| 6 |
+
from src.database import get_auth_supabase
|
| 7 |
from src.store import create_user, get_user_by_email
|
| 8 |
|
| 9 |
router = APIRouter(prefix="/api/auth", tags=["Auth"])
|
|
|
|
| 28 |
|
| 29 |
@router.post("/login")
|
| 30 |
async def login(body: LoginRequest):
|
| 31 |
+
supabase = get_auth_supabase()
|
| 32 |
+
|
| 33 |
+
# Real Supabase auth
|
| 34 |
+
if supabase:
|
| 35 |
+
try:
|
| 36 |
+
res = supabase.auth.sign_in_with_password({"email": body.email, "password": body.password})
|
| 37 |
+
sb_user = res.user
|
| 38 |
+
session = res.session
|
| 39 |
+
if not sb_user or not session:
|
| 40 |
+
raise HTTPException(401, "Invalid email or password")
|
| 41 |
+
return {
|
| 42 |
+
"token": session.access_token,
|
| 43 |
+
"user": {
|
| 44 |
+
"id": str(sb_user.id),
|
| 45 |
+
"name": sb_user.user_metadata.get("name", body.email.split("@")[0]),
|
| 46 |
+
"email": sb_user.email,
|
| 47 |
+
},
|
| 48 |
+
}
|
| 49 |
+
except HTTPException:
|
| 50 |
+
raise
|
| 51 |
+
except Exception as e:
|
| 52 |
+
raise HTTPException(401, f"Invalid email or password: {e}")
|
| 53 |
+
|
| 54 |
+
# Dev fallback (no Supabase)
|
| 55 |
user = get_user_by_email(body.email)
|
| 56 |
if not user or user.get("password") != body.password:
|
| 57 |
raise HTTPException(401, "Invalid email or password")
|
|
|
|
| 58 |
return {
|
| 59 |
+
"token": str(uuid.uuid4()),
|
| 60 |
+
"user": {"id": user["id"], "name": user["name"], "email": user["email"]},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
}
|
| 62 |
|
| 63 |
|
| 64 |
@router.post("/signup")
|
| 65 |
async def signup(body: SignupRequest):
|
| 66 |
+
supabase = get_auth_supabase()
|
| 67 |
+
|
| 68 |
+
# Real Supabase auth
|
| 69 |
+
if supabase:
|
| 70 |
+
try:
|
| 71 |
+
res = supabase.auth.sign_up({
|
| 72 |
+
"email": body.email,
|
| 73 |
+
"password": body.password,
|
| 74 |
+
"options": {"data": {"name": body.name}},
|
| 75 |
+
})
|
| 76 |
+
sb_user = res.user
|
| 77 |
+
session = res.session
|
| 78 |
+
if not sb_user:
|
| 79 |
+
raise HTTPException(400, "Signup failed")
|
| 80 |
+
# session can be None if email confirmation is required
|
| 81 |
+
token = session.access_token if session else str(uuid.uuid4())
|
| 82 |
+
return {
|
| 83 |
+
"token": token,
|
| 84 |
+
"user": {
|
| 85 |
+
"id": str(sb_user.id),
|
| 86 |
+
"name": body.name,
|
| 87 |
+
"email": sb_user.email,
|
| 88 |
+
},
|
| 89 |
+
}
|
| 90 |
+
except HTTPException:
|
| 91 |
+
raise
|
| 92 |
+
except Exception as e:
|
| 93 |
+
raise HTTPException(400, f"Signup failed: {e}")
|
| 94 |
+
|
| 95 |
+
# Dev fallback (no Supabase)
|
| 96 |
try:
|
| 97 |
user = create_user(body.name, body.email, body.password)
|
| 98 |
except ValueError as e:
|
| 99 |
raise HTTPException(400, str(e))
|
|
|
|
| 100 |
return {
|
| 101 |
+
"token": str(uuid.uuid4()),
|
| 102 |
+
"user": {"id": user["id"], "name": user["name"], "email": user["email"]},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
}
|
| 104 |
|
| 105 |
|
| 106 |
@router.post("/google")
|
| 107 |
async def google_auth(body: GoogleAuthRequest):
|
| 108 |
+
# Google OAuth would require supabase.auth.sign_in_with_id_token — not wired up yet
|
| 109 |
+
# Fall back to dev mode: create/fetch user by email
|
| 110 |
email = body.email or f"google_{uuid.uuid4().hex[:8]}@google.com"
|
| 111 |
name = body.name or "Google User"
|
| 112 |
user = get_user_by_email(email)
|
| 113 |
if not user:
|
| 114 |
user = create_user(name, email, "")
|
|
|
|
| 115 |
return {
|
| 116 |
+
"token": str(uuid.uuid4()),
|
| 117 |
+
"user": {"id": user["id"], "name": user["name"], "email": user["email"]},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
}
|