from datetime import datetime, timedelta, timezone from typing import Optional from fastapi import Depends, HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from jose import JWTError, jwt from app.config import get_settings from app.database import get_db settings = get_settings() bearer_scheme = HTTPBearer(auto_error=False) def create_access_token(data: dict) -> str: payload = data.copy() expire = datetime.now(timezone.utc) + timedelta(hours=settings.jwt_expire_hours) payload.update({"exp": expire, "iat": datetime.now(timezone.utc)}) return jwt.encode(payload, settings.jwt_secret, algorithm=settings.jwt_algorithm) def decode_token(token: str) -> Optional[dict]: try: return jwt.decode( token, settings.jwt_secret, algorithms=[settings.jwt_algorithm], ) except JWTError: return None async def get_current_user( credentials: Optional[HTTPAuthorizationCredentials] = Depends(bearer_scheme), db=Depends(get_db), ): """FastAPI dependency: validates JWT and returns the current user doc.""" if not credentials: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated", headers={"WWW-Authenticate": "Bearer"}, ) payload = decode_token(credentials.credentials) if not payload: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired token", headers={"WWW-Authenticate": "Bearer"}, ) user = await db["users"].find_one({"google_id": payload.get("sub")}) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found", ) return user