"""Signed cookie configuration (TASK-03-02, D-041, D-056, R-AUTH-01, G-031). Returns kwargs for Starlette SessionMiddleware (itsdangerous HMAC-SHA256 signed cookies — D-056, stateless, no sessions table). The cookie name is `praxis_op` (distinct from any future learner cookie). R-AUTH-01 / G-031 reframe: the PRIMARY mitigation for a sniffed operator cookie is the k-anonymity defense-in-depth — the cohort dashboard reads only k-anonymized aggregates, so a sniffed cookie leaks NO learner PII. The `PRAXIS_COOKIE_SECURE` flag is the SECONDARY mitigation (operational convenience for when TLS arrives). It defaults to true; the HTTP pilot (LXC, no TLS — D-030) sets it to false with a logged WARNING. """ from __future__ import annotations import os import secrets from loguru import logger _COOKIE_MAX_AGE_S = 28800 # 8h (D-041) def _env_bool(key: str, default: bool) -> bool: raw = os.environ.get(key, "").strip().lower() if raw in ("true", "1", "yes", "on"): return True if raw in ("false", "0", "no", "off"): return False return default def get_session_middleware_kwargs() -> dict: """Return kwargs for Starlette SessionMiddleware. If PRAXIS_COOKIE_SECRET is unset, generate an ephemeral random secret and log a WARNING (dev only — sessions won't survive a restart and this MUST NOT be used in pilot/production). """ secret = os.environ.get("PRAXIS_COOKIE_SECRET", "").strip() if not secret: secret = secrets.token_urlsafe(48) logger.warning( "PRAXIS_COOKIE_SECRET not set — generated an ephemeral random secret. " "Sessions will NOT survive a server restart. This is dev-only; set " "PRAXIS_COOKIE_SECRET (>=32 bytes) for pilot/production." ) secure = _env_bool("PRAXIS_COOKIE_SECURE", True) if not secure: logger.warning( "Cookie Secure flag disabled (PRAXIS_COOKIE_SECURE=false) — HTTP pilot " "mode (R-AUTH-01). Do not use in production. NOTE (G-031): the primary " "R-AUTH-01 mitigation is k-anon defense-in-depth (cohort dashboard reads " "only k-anonymized aggregates → sniffed cookie leaks no PII); this flag " "is the secondary mitigation." ) return { "secret_key": secret, "session_cookie": "praxis_op", "max_age": _COOKIE_MAX_AGE_S, "https_only": secure, "same_site": "strict", "path": "/", } __all__ = ["get_session_middleware_kwargs"]