from datetime import datetime, timedelta from passlib.context import CryptContext from jose import jwt, JWTError from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from core.config import SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES from db.mongo import users_collection # OAuth2 setup — adjust tokenUrl if your API has a prefix like /api oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login") # Password hashing context pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") # Hash a plain password def hash_password(password: str) -> str: return pwd_context.hash(password) # Verify a plain password against the hash def verify_password(plain: str, hashed: str) -> bool: return pwd_context.verify(plain, hashed) # Create a JWT access token def create_access_token(data: dict, expires_delta: timedelta = None): to_encode = data.copy() expire = datetime.utcnow() + (expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)) to_encode.update({"exp": expire}) return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) async def get_current_user(token: str = Depends(oauth2_scheme)): logger.info(f"🔐 Authentication attempt with token: {token[:15]}...") # Log first part of token try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) logger.info(f"📄 Token payload: {payload}") email: str = payload.get("sub") if email is None: logger.error("❌ Invalid token: subject missing") raise HTTPException(status_code=401, detail="Invalid token: subject missing") except JWTError as e: logger.error(f"❌ JWTError while decoding token: {str(e)}") raise HTTPException(status_code=401, detail="Could not validate token") user = await users_collection.find_one({"email": email}) if not user: logger.error(f"❌ User not found for email: {email}") raise HTTPException(status_code=404, detail="User not found") logger.info(f"✅ Authenticated user: {user['email']}") return user