71 lines
2.7 KiB
Python
71 lines
2.7 KiB
Python
# app/api/dependencies.py
|
|
import logging
|
|
from typing import Optional
|
|
|
|
from fastapi import Depends, HTTPException, status
|
|
from fastapi.security import OAuth2PasswordBearer
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from jose import JWTError
|
|
|
|
from app.database import get_db
|
|
from app.core.security import verify_access_token
|
|
from app.crud import user as crud_user
|
|
from app.models import User as UserModel # Import the SQLAlchemy model
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Define the OAuth2 scheme
|
|
# tokenUrl should point to your login endpoint relative to the base path
|
|
# It's used by Swagger UI for the "Authorize" button flow.
|
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login") # Corrected path
|
|
|
|
async def get_current_user(
|
|
token: str = Depends(oauth2_scheme),
|
|
db: AsyncSession = Depends(get_db)
|
|
) -> UserModel:
|
|
"""
|
|
Dependency to get the current user based on the JWT token.
|
|
|
|
- Extracts token using OAuth2PasswordBearer.
|
|
- Verifies the token (signature, expiry).
|
|
- Fetches the user from the database based on the token's subject (email).
|
|
- Raises HTTPException 401 if any step fails.
|
|
|
|
Returns:
|
|
The authenticated user's database model instance.
|
|
"""
|
|
credentials_exception = HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Could not validate credentials",
|
|
headers={"WWW-Authenticate": "Bearer"},
|
|
)
|
|
|
|
payload = verify_access_token(token)
|
|
if payload is None:
|
|
logger.warning("Token verification failed (invalid, expired, or malformed).")
|
|
raise credentials_exception
|
|
|
|
email: Optional[str] = payload.get("sub")
|
|
if email is None:
|
|
logger.error("Token payload missing 'sub' (subject/email).")
|
|
raise credentials_exception # Token is malformed
|
|
|
|
# Fetch user from database
|
|
user = await crud_user.get_user_by_email(db, email=email)
|
|
if user is None:
|
|
logger.warning(f"User corresponding to token subject not found: {email}")
|
|
# Could happen if user deleted after token issuance
|
|
raise credentials_exception # Treat as invalid credentials
|
|
|
|
logger.debug(f"Authenticated user retrieved: {user.email} (ID: {user.id})")
|
|
return user
|
|
|
|
# Optional: Dependency for getting the *active* current user
|
|
# You might add an `is_active` flag to your User model later
|
|
# async def get_current_active_user(
|
|
# current_user: UserModel = Depends(get_current_user)
|
|
# ) -> UserModel:
|
|
# if not current_user.is_active: # Assuming an is_active attribute
|
|
# logger.warning(f"Authentication attempt by inactive user: {current_user.email}")
|
|
# raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Inactive user")
|
|
# return current_user |