import base64 import hashlib import hmac import json import os import time from typing import Optional COOKIE_NAME = "converta_session" INTERNAL_HEADER = "x-internal-auth" def get_app_password() -> Optional[str]: value = os.environ.get("APP_PASSWORD") if value is None: return None value = value.strip() return value or None def _b64url_encode(data: bytes) -> str: return base64.urlsafe_b64encode(data).decode("ascii").rstrip("=") def _b64url_decode(data: str) -> bytes: padded = data + "=" * (-len(data) % 4) return base64.urlsafe_b64decode(padded.encode("ascii")) def _sign(payload_b64: str, secret: str) -> str: sig = hmac.new(secret.encode("utf-8"), payload_b64.encode("ascii"), hashlib.sha256).digest() return _b64url_encode(sig) def create_session_token(secret: str, ttl_seconds: int = 7 * 24 * 60 * 60) -> str: now = int(time.time()) payload = {"iat": now, "exp": now + ttl_seconds} payload_b64 = _b64url_encode(json.dumps(payload, separators=(",", ":")).encode("utf-8")) sig_b64 = _sign(payload_b64, secret) return f"{payload_b64}.{sig_b64}" def verify_session_token(token: str, secret: str) -> bool: try: payload_b64, sig_b64 = token.split(".", 1) except ValueError: return False expected = _sign(payload_b64, secret) if not hmac.compare_digest(expected, sig_b64): return False try: payload = json.loads(_b64url_decode(payload_b64).decode("utf-8")) except Exception: return False exp = payload.get("exp") if not isinstance(exp, int): return False if exp < int(time.time()): return False return True def constant_time_equals(a: str, b: str) -> bool: return hmac.compare_digest(a.encode("utf-8"), b.encode("utf-8"))