""" JWT signing/verification and the require_auth decorator used by route blueprints. Accounts come from auth-hub (see services/auth_hub_client.py); this module only handles this app's own session token. """ import datetime import jwt from flask import request, g, jsonify from functools import wraps from config import JWT_SECRET, JWT_EXPIRY_DAYS from db import query_one def sign_token(user_id: str) -> str: now = datetime.datetime.utcnow() payload = { "sub": user_id, "iat": now, "exp": now + datetime.timedelta(days=JWT_EXPIRY_DAYS), } return jwt.encode(payload, JWT_SECRET, algorithm="HS256") def verify_token(token: str) -> dict: payload = jwt.decode(token, JWT_SECRET, algorithms=["HS256"]) return {"user_id": payload["sub"]} def require_auth(f): @wraps(f) def wrapper(*args, **kwargs): auth = request.headers.get("Authorization", "") if not auth.startswith("Bearer "): return jsonify({"error": "missing or malformed Authorization header"}), 401 token = auth[7:].strip() try: data = verify_token(token) except jwt.ExpiredSignatureError: return jsonify({"error": "token expired"}), 401 except jwt.InvalidTokenError: return jsonify({"error": "invalid token"}), 401 g.user_id = data["user_id"] # Ensure the user record still exists in the database (it may have been # deleted by a database reset). If not, ask the user to re-login. if not query_one("SELECT 1 FROM users WHERE id = ?", [g.user_id]): return jsonify({"error": "user not found — please re-login"}), 401 return f(*args, **kwargs) return wrapper