"""Auth module — bcrypt password verification and session token management.""" import os import secrets import bcrypt # AUTH_USER is read at import time (used by app.py to compare against the submitted email). # AUTH_PASS_HASH is intentionally NOT cached here — verify_password() reads it fresh from # os.environ at call time so runtime env changes (e.g. secret rotation) are respected. AUTH_USER: str = os.environ.get("AUTH_USER", "admin@localhost") # In-memory session store: token -> email _sessions: dict[str, str] = {} def verify_password(password: str) -> bool: """Return True if password matches the bcrypt hash in AUTH_PASS_HASH env var. Reads AUTH_PASS_HASH from env at call time so runtime env changes are respected. Returns False if no hash is configured, if password is empty, or on any error. """ if not password: return False hash_str = os.environ.get("AUTH_PASS_HASH", "") if not hash_str: return False try: return bcrypt.checkpw(password.encode(), hash_str.encode()) except (ValueError, TypeError): return False def create_session_token(email: str) -> str: """Generate a session token, store it mapped to email, and return the token.""" token = secrets.token_hex(32) _sessions[token] = email return token def validate_session_token(token: str) -> str | None: """Return the email for a valid token, or None if token is empty/unknown.""" if not token: return None return _sessions.get(token) def invalidate_session_token(token: str) -> None: """Remove a session token from the store.""" _sessions.pop(token, None)