""" Authentication utilities for password hashing and JWT token management. """ from datetime import datetime, timedelta from typing import Optional from jose import JWTError, jwt from passlib.context import CryptContext from app.config import settings # Password hashing context pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") def hash_password(password: str) -> str: """ Hash a plain password using bcrypt. Args: password: Plain text password Returns: Hashed password string Example: >>> hash_password("mypassword123") '$2b$12$KIXqZ9Zu8...' """ return pwd_context.hash(password) def verify_password(plain_password: str, hashed_password: str) -> bool: """ Verify a plain password against a hashed password. Args: plain_password: Password entered by user hashed_password: Hashed password from database Returns: True if password matches, False otherwise Example: >>> verify_password("mypassword123", "$2b$12$KIXqZ9Zu8...") True """ return pwd_context.verify(plain_password, hashed_password) def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str: """ Create a JWT access token. Args: data: Dictionary of data to encode in token (usually user_id and email) expires_delta: Optional custom expiration time Returns: Encoded JWT token string Example: >>> create_access_token({"sub": "user-id", "email": "user@example.com"}) 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' """ 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.secret_key, algorithm=settings.algorithm) return encoded_jwt def decode_access_token(token: str) -> Optional[dict]: """ Decode and verify a JWT token. Args: token: JWT token string Returns: Decoded token data if valid, None if invalid Example: >>> decode_access_token("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...") {'sub': 'user-id', 'email': 'user@example.com', 'exp': 1234567890} """ try: payload = jwt.decode(token, settings.secret_key, algorithms=[settings.algorithm]) return payload except JWTError: return None