youtube-summarizer/backend/core/dependencies.py

122 lines
3.8 KiB
Python

"""FastAPI dependency injection for authentication and common services."""
from typing import Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from backend.core.database import get_db
from backend.models.user import User
from backend.services.auth_service import AuthService
from jose import JWTError, jwt
import logging
logger = logging.getLogger(__name__)
# Security scheme for JWT tokens
security = HTTPBearer()
def get_auth_service() -> AuthService:
"""Get AuthService instance."""
return AuthService()
async def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security),
db: Session = Depends(get_db),
auth_service: AuthService = Depends(get_auth_service)
) -> User:
"""
Validate JWT token and return current user.
Args:
credentials: HTTP Bearer token from request header
db: Database session
auth_service: Authentication service instance
Returns:
User: Current authenticated user
Raises:
HTTPException: 401 if token is invalid or user not found
"""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
token = credentials.credentials
if not token:
raise credentials_exception
# Decode and validate token
payload = auth_service.decode_access_token(token)
if payload is None:
raise credentials_exception
user_id: str = payload.get("sub")
if user_id is None:
raise credentials_exception
# Get user from database
user = db.query(User).filter(User.id == user_id).first()
if user is None:
raise credentials_exception
return user
except JWTError as jwt_error:
if "expired" in str(jwt_error).lower():
logger.warning("JWT token expired")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Token expired",
headers={"WWW-Authenticate": "Bearer"},
)
logger.warning(f"Invalid JWT token: {jwt_error}")
raise credentials_exception
except Exception as e:
logger.error(f"Error validating user token: {e}")
raise credentials_exception
async def get_current_user_optional(
credentials: Optional[HTTPAuthorizationCredentials] = Depends(HTTPBearer(auto_error=False)),
db: Session = Depends(get_db),
auth_service: AuthService = Depends(get_auth_service)
) -> Optional[User]:
"""
Optionally validate JWT token and return current user.
Returns None if no token provided or token is invalid.
Args:
credentials: Optional HTTP Bearer token from request header
db: Database session
auth_service: Authentication service instance
Returns:
Optional[User]: Current authenticated user or None
"""
if not credentials:
return None
try:
token = credentials.credentials
if not token:
return None
# Decode and validate token
payload = auth_service.decode_access_token(token)
if payload is None:
return None
user_id: str = payload.get("sub")
if user_id is None:
return None
# Get user from database
user = db.query(User).filter(User.id == user_id).first()
return user
except Exception as e:
logger.debug(f"Optional auth validation failed: {e}")
return None