""" Security utilities for authentication. """ import hashlib from datetime import datetime, timedelta, timezone from typing import Optional import jwt from jwt.exceptions import PyJWTError from passlib.context import CryptContext from app.core.config import get_settings settings = get_settings() pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") def sha256_hash(password: str) -> str: """SHA-256 hash a raw password (matches client-side hashing).""" return hashlib.sha256(password.encode()).hexdigest() def verify_password(sha256_password: str, hashed_password: str) -> bool: """Verify a SHA-256 hashed password against its bcrypt hash.""" return pwd_context.verify(sha256_password, hashed_password) def get_password_hash(raw_password: str) -> str: """Hash a raw password: SHA-256 then bcrypt.""" return pwd_context.hash(sha256_hash(raw_password)) def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str: """Create a JWT access token.""" to_encode = data.copy() if expires_delta: expire = datetime.now(timezone.utc) + expires_delta else: expire = datetime.now(timezone.utc) + timedelta( minutes=settings.access_token_expire_minutes ) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode( to_encode, settings.jwt_secret, algorithm=settings.jwt_algorithm ) return encoded_jwt def decode_access_token(token: str) -> Optional[dict]: """Decode and verify a JWT access token.""" try: payload = jwt.decode( token, settings.jwt_secret, algorithms=[settings.jwt_algorithm] ) return payload except PyJWTError: return None