""" Authentication dependency for auth service services/auth/app/core/auth.py """ from fastapi import Depends, HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from jose import JWTError, jwt import structlog from app.core.config import settings from app.core.database import get_db from app.models.users import User logger = structlog.get_logger() security = HTTPBearer() async def get_current_user( credentials: HTTPAuthorizationCredentials = Depends(security), db: AsyncSession = Depends(get_db) ) -> User: """ Dependency to get the current authenticated user """ credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) try: # Decode JWT token payload = jwt.decode( credentials.credentials, settings.JWT_SECRET_KEY, algorithms=[settings.JWT_ALGORITHM] ) # Get user identifier from token user_id: str = payload.get("sub") if user_id is None: logger.warning("Token payload missing 'sub' field") raise credentials_exception logger.info(f"Authenticating user: {user_id}") except JWTError as e: logger.warning(f"JWT decode error: {e}") raise credentials_exception try: # Get user from database result = await db.execute( select(User).where(User.id == user_id) ) user = result.scalar_one_or_none() if user is None: logger.warning(f"User not found for ID: {user_id}") raise credentials_exception if not user.is_active: logger.warning(f"Inactive user attempted access: {user_id}") raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Inactive user" ) logger.info(f"User authenticated: {user.email} (tenant: {user.tenant_id})") return user except Exception as e: logger.error(f"Error getting user: {e}") raise credentials_exception async def get_current_active_user( current_user: User = Depends(get_current_user) ) -> User: """ Dependency to get the current active user """ if not current_user.is_active: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Inactive user" ) return current_user def verify_password(plain_password: str, hashed_password: str) -> bool: """Verify a password against its hash""" from passlib.context import CryptContext pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") return pwd_context.verify(plain_password, hashed_password) def get_password_hash(password: str) -> str: """Generate password hash""" from passlib.context import CryptContext pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") return pwd_context.hash(password) def create_access_token(data: dict, expires_delta=None): """Create JWT access token""" from datetime import datetime, timedelta to_encode = data.copy() if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM) return encoded_jwt def create_refresh_token(data: dict): """Create JWT refresh token""" from datetime import datetime, timedelta to_encode = data.copy() expire = datetime.utcnow() + timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM) return encoded_jwt