"""Security primitives: password hashing, JWT, CSRF, rate limiting, audit logging.""" import hashlib import secrets import time from datetime import datetime, timedelta, timezone from typing import Optional import jwt import bcrypt as _bcrypt from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.config import settings # passlib 1.7.4 is incompatible with bcrypt >= 4.1; use bcrypt directly. # Hashes remain standard $2b$ bcrypt, fully inter-operable. _BCRYPT_ROUNDS = 12 def hash_password(p: str) -> str: pw = p.encode("utf-8")[:72] # bcrypt input limit return _bcrypt.hashpw(pw, _bcrypt.gensalt(rounds=_BCRYPT_ROUNDS)).decode("ascii") def verify_password(plain: str, hashed: str) -> bool: try: return _bcrypt.checkpw(plain.encode("utf-8")[:72], hashed.encode("ascii")) except Exception: return False # Sensitive values that must never appear in logs or JSON responses. SENSITIVE_KEYS = {"password", "client_secret", "refresh_token", "authorization_code", "access_token", "secret", "token", "csrf", "new_password", "old_password"} def hash_password(p: str) -> str: return pwd_context.hash(p) def verify_password(plain: str, hashed: str) -> bool: try: return pwd_context.verify(plain, hashed) except Exception: return False def create_access_token(user_id: int, expires_minutes: Optional[int] = None) -> str: payload = { "sub": str(user_id), "iat": int(time.time()),