import hashlib import secrets from datetime import datetime, timedelta from typing import Optional, Any, Union import jwt from app.core.config import settings def get_password_hash(password: str) -> str: """ Hashes a password with PBKDF2-HMAC-SHA256 using Python's standard library. Requires NO C compilers or external dependencies. """ salt = secrets.token_hex(16) key = hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), salt.encode("utf-8"), 100000) return f"pbkdf2:sha256:100000${salt}${key.hex()}" def verify_password(plain_password: str, hashed_password: str) -> bool: """Verifies a plain password against the stored hash.""" if not hashed_password: return False if "$" not in hashed_password: # Fallback comparison for plain text return plain_password == hashed_password try: parts = hashed_password.split("$") if len(parts) == 3 and parts[0].startswith("pbkdf2:"): _, salt, key_hex = parts check_key = hashlib.pbkdf2_hmac("sha256", plain_password.encode("utf-8"), salt.encode("utf-8"), 100000) return secrets.compare_digest(check_key.hex(), key_hex) return plain_password == hashed_password except Exception: return False def create_access_token(subject: Union[str, Any], expires_delta: Optional[timedelta] = None, extra_claims: Optional[dict] = None) -> str: """Creates a signed JWT token.""" if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) to_encode = { "exp": expire, "sub": str(subject), "user_id": subject } if extra_claims: to_encode.update(extra_claims) encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM) return encoded_jwt def decode_access_token(token: str) -> Optional[dict]: """Decodes a JWT token.""" try: payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) return payload except Exception: return None