import secrets from datetime import UTC, datetime, timedelta import bcrypt import jwt from app.core.config import settings # Code alphabet excludes look-alikes (0/O, 1/I/l) to reduce paste errors. _CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" def hash_password(password: str) -> str: return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8") def verify_password(password: str, hashed: str) -> bool: try: return bcrypt.checkpw(password.encode("utf-8"), hashed.encode("utf-8")) except ValueError: return False def generate_code(length: int = 8) -> str: return "".join(secrets.choice(_CODE_ALPHABET) for _ in range(length)) def create_access_token(subject: str, expires_minutes: int | None = None) -> str: minutes = ( expires_minutes if expires_minutes is not None else settings.jwt_access_token_expire_minutes ) now = datetime.now(UTC) payload = { "sub": subject, "iat": now, "exp": now + timedelta(minutes=minutes), } return jwt.encode(payload, settings.jwt_secret, algorithm=settings.jwt_algorithm) def decode_access_token(token: str) -> dict: return jwt.decode( token, settings.jwt_secret, algorithms=[settings.jwt_algorithm], )