""" Authentication utilities — password hashing and JWT session tokens. Passwords are stored as bcrypt hashes in home/{username}/auth.json. Sessions are JWT cookies signed with JWT_SECRET from settings. Usage: set_password("scott", "mypassword") # admin setup check_credentials("scott", "mypassword") # login validation create_token("scott") # returns JWT string decode_token(token) # returns username or raises """ import json import logging from datetime import datetime, timedelta, timezone from pathlib import Path import bcrypt import jwt from config import settings logger = logging.getLogger(__name__) COOKIE_NAME = "cortex_session" ALGORITHM = "HS256" # --------------------------------------------------------------------------- # Password helpers # --------------------------------------------------------------------------- def _auth_path(username: str) -> Path: return settings.home_root() / username / "auth.json" def set_password(username: str, password: str) -> None: """Hash and store a password for a user. Creates auth.json if needed.""" hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode() _auth_path(username).write_text(json.dumps({"password_hash": hashed}) + "\n") logger.info("password set for user: %s", username) def check_credentials(username: str, password: str) -> bool: """Return True if username+password are valid, False otherwise.""" path = _auth_path(username) if not path.exists(): return False try: data = json.loads(path.read_text()) stored = data.get("password_hash", "").encode() return bcrypt.checkpw(password.encode(), stored) except Exception: return False # --------------------------------------------------------------------------- # JWT helpers # --------------------------------------------------------------------------- def create_token(username: str) -> str: """Return a signed JWT encoding the username.""" expire = datetime.now(timezone.utc) + timedelta(days=settings.jwt_expire_days) payload = {"sub": username, "exp": expire} return jwt.encode(payload, settings.jwt_secret, algorithm=ALGORITHM) def decode_token(token: str) -> str: """Decode a JWT and return the username. Raises jwt.InvalidTokenError on failure.""" payload = jwt.decode(token, settings.jwt_secret, algorithms=[ALGORITHM]) return payload["sub"]