Spaces:
Running
Running
| """ | |
| Session cookie utilities for HttpOnly cookie-based auth. | |
| Uses HMAC-signed cookies so tenant_id is never exposed to JavaScript. | |
| No additional dependencies required. | |
| """ | |
| from __future__ import annotations | |
| import base64 | |
| import hmac | |
| import json | |
| import logging | |
| import os | |
| import secrets | |
| import time | |
| from typing import Any, cast | |
| logger = logging.getLogger(__name__) | |
| _SESSION_SECRET: str | None = None | |
| _SESSION_TTL_S = 86400 # 24 hours | |
| def _get_secret() -> str: | |
| global _SESSION_SECRET | |
| if _SESSION_SECRET is None: | |
| _SESSION_SECRET = os.environ.get("SESSION_SECRET") or secrets.token_hex(32) | |
| logger.info("Session secret initialized") | |
| return _SESSION_SECRET | |
| def create_session_token(tenant_id: str) -> str: | |
| payload: dict[str, Any] = { | |
| "t": tenant_id, | |
| "e": int(time.time()) + _SESSION_TTL_S, | |
| } | |
| payload_b64 = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().rstrip("=") | |
| sig = hmac.new(_get_secret().encode(), payload_b64.encode(), "sha256").hexdigest()[:16] | |
| return f"{payload_b64}.{sig}" | |
| def verify_session_token(token: str) -> str | None: | |
| try: | |
| payload_b64, sig = token.rsplit(".", 1) | |
| expected = hmac.new(_get_secret().encode(), payload_b64.encode(), "sha256").hexdigest()[:16] | |
| if not hmac.compare_digest(sig, expected): | |
| return None | |
| padded = payload_b64 + "=" * (4 - len(payload_b64) % 4) | |
| data: dict[str, Any] = json.loads(base64.urlsafe_b64decode(padded)) | |
| if data["e"] < time.time(): | |
| return None | |
| return cast(str, data["t"]) | |
| except Exception: | |
| return None | |