Fix auth
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
from fastapi import FastAPI
|
||||
from .routers import auth, users, videos
|
||||
|
||||
app = FastAPI(title="Video Voting App")
|
||||
app = FastAPI(title="Video Voting App",
|
||||
swagger_ui_parameters={"persistAuthorization": True})
|
||||
|
||||
app.include_router(auth.router, prefix="/auth", tags=["Authentication"])
|
||||
app.include_router(users.router, prefix="/users", tags=["Users"])
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
from pydantic import BaseModel, Field, EmailStr
|
||||
from typing import Optional, List
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
class LoginPasswordRequest(BaseModel):
|
||||
email: EmailStr
|
||||
password: str
|
||||
|
||||
class LoginResponse(BaseModel):
|
||||
uid: str
|
||||
id_token: str
|
||||
|
||||
class UserCreate(BaseModel):
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
import os
|
||||
import httpx
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
# Import the new dependency
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from firebase_admin import auth
|
||||
from ..firebase_config import get_db
|
||||
from ..models import UserCreate, UserRecord, LoginRequest
|
||||
# The LoginPasswordRequest model is no longer needed for this endpoint
|
||||
from ..models import UserCreate, UserRecord, LoginResponse
|
||||
|
||||
router = APIRouter()
|
||||
FIREBASE_WEB_API_KEY = os.environ.get("FIREBASE_WEB_API_KEY")
|
||||
FIREBASE_SIGN_IN_URL = f"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={FIREBASE_WEB_API_KEY}"
|
||||
|
||||
|
||||
@router.post("/register", response_model=UserRecord, status_code=status.HTTP_201_CREATED)
|
||||
async def register_user(user: UserCreate):
|
||||
"""
|
||||
Registers a user in Firebase Auth and creates a corresponding user document in Firestore.
|
||||
"""
|
||||
# ... (this function remains the same)
|
||||
try:
|
||||
user_record = auth.create_user(
|
||||
email=user.email,
|
||||
@@ -34,40 +43,50 @@ async def register_user(user: UserCreate):
|
||||
detail="Email already registered",
|
||||
)
|
||||
|
||||
@router.post("/login", response_model=UserRecord)
|
||||
async def login(login_data: LoginRequest):
|
||||
# The signature of this function is updated
|
||||
@router.post("/login", response_model=LoginResponse)
|
||||
async def login_with_password(form_data: OAuth2PasswordRequestForm = Depends()):
|
||||
"""
|
||||
Verifies a Firebase ID token, checks user role from Firestore, and returns user data.
|
||||
Authenticates a user with email/password via Firebase REST API,
|
||||
checks their role in Firestore, and returns their UID and ID token.
|
||||
"""
|
||||
try:
|
||||
decoded_token = auth.verify_id_token(login_data.id_token)
|
||||
uid = decoded_token['uid']
|
||||
|
||||
db = get_db()
|
||||
user_doc = db.collection('users').document(uid).get()
|
||||
payload = {
|
||||
# Use form_data.username as the email
|
||||
"email": form_data.username,
|
||||
"password": form_data.password,
|
||||
"returnSecureToken": True
|
||||
}
|
||||
|
||||
if not user_doc.exists:
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
response = await client.post(FIREBASE_SIGN_IN_URL, json=payload)
|
||||
response.raise_for_status()
|
||||
|
||||
auth_data = response.json()
|
||||
uid = auth_data['localId']
|
||||
|
||||
# Check user role in Firestore to ensure account is active
|
||||
db = get_db()
|
||||
user_doc = db.collection('users').document(uid).get()
|
||||
|
||||
if not user_doc.exists:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="User authenticated but not found in database.",
|
||||
)
|
||||
|
||||
user = user_doc.to_dict()
|
||||
if user.get("role") == "member":
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Account is not activated. Please contact an administrator."
|
||||
)
|
||||
|
||||
return LoginResponse(uid=uid, id_token=auth_data['idToken'])
|
||||
|
||||
except httpx.HTTPStatusError as e:
|
||||
error_detail = e.response.json().get("error", {}).get("message", "Authentication failed.")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="User not found in the database.",
|
||||
)
|
||||
|
||||
user = user_doc.to_dict()
|
||||
if user.get("role") == "member":
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Account is not activated. Please contact an administrator."
|
||||
)
|
||||
|
||||
return UserRecord(**user)
|
||||
|
||||
except auth.InvalidIdTokenError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid Firebase ID token",
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=str(e),
|
||||
)
|
||||
detail=error_detail
|
||||
)
|
||||
@@ -1,14 +1,15 @@
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
from firebase_admin import auth
|
||||
from .firebase_config import get_db
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")
|
||||
http_bearer_scheme = HTTPBearer()
|
||||
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||
async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(http_bearer_scheme)):
|
||||
"""
|
||||
Verifies the Firebase ID token and retrieves the user document from Firestore.
|
||||
"""
|
||||
token = credentials.credentials
|
||||
try:
|
||||
decoded_token = auth.verify_id_token(token)
|
||||
uid = decoded_token['uid']
|
||||
|
||||
Reference in New Issue
Block a user