fix(cloud): extract cf_session cookie by name from X-CF-Session header

This commit is contained in:
pyr0ball 2026-03-10 09:22:08 -07:00
parent 72320315e2
commit 97b695c3e3
2 changed files with 13 additions and 5 deletions

View file

@ -11,6 +11,7 @@ All Peregrine pages call get_db_path() instead of DEFAULT_DB directly to
transparently support both local and cloud deployments. transparently support both local and cloud deployments.
""" """
import os import os
import re
import hmac import hmac
import hashlib import hashlib
from pathlib import Path from pathlib import Path
@ -20,6 +21,12 @@ import streamlit as st
from scripts.db import DEFAULT_DB from scripts.db import DEFAULT_DB
CLOUD_MODE: bool = os.environ.get("CLOUD_MODE", "").lower() in ("1", "true", "yes") CLOUD_MODE: bool = os.environ.get("CLOUD_MODE", "").lower() in ("1", "true", "yes")
def _extract_session_token(cookie_header: str) -> str:
"""Extract cf_session value from a Cookie header string."""
m = re.search(r'(?:^|;)\s*cf_session=([^;]+)', cookie_header)
return m.group(1).strip() if m else ""
CLOUD_DATA_ROOT: Path = Path(os.environ.get("CLOUD_DATA_ROOT", "/devl/menagerie-data")) CLOUD_DATA_ROOT: Path = Path(os.environ.get("CLOUD_DATA_ROOT", "/devl/menagerie-data"))
DIRECTUS_JWT_SECRET: str = os.environ.get("DIRECTUS_JWT_SECRET", "") DIRECTUS_JWT_SECRET: str = os.environ.get("DIRECTUS_JWT_SECRET", "")
SERVER_SECRET: str = os.environ.get("CF_SERVER_SECRET", "") SERVER_SECRET: str = os.environ.get("CF_SERVER_SECRET", "")
@ -64,13 +71,14 @@ def resolve_session(app: str = "peregrine") -> None:
if st.session_state.get("user_id"): if st.session_state.get("user_id"):
return return
token = st.context.headers.get("x-cf-session", "") cookie_header = st.context.headers.get("x-cf-session", "")
if not token: session_jwt = _extract_session_token(cookie_header)
if not session_jwt:
st.error("Session token missing. Please log in at circuitforge.tech.") st.error("Session token missing. Please log in at circuitforge.tech.")
st.stop() st.stop()
try: try:
user_id = validate_session_jwt(token) user_id = validate_session_jwt(session_jwt)
except Exception as exc: except Exception as exc:
st.error(f"Invalid session — please log in again. ({exc})") st.error(f"Invalid session — please log in again. ({exc})")
st.stop() st.stop()

View file

@ -27,7 +27,7 @@ def test_resolve_session_sets_db_path(tmp_path, monkeypatch):
patch.object(cs, "st") as mock_st, \ patch.object(cs, "st") as mock_st, \
patch.object(cs, "CLOUD_DATA_ROOT", tmp_path): patch.object(cs, "CLOUD_DATA_ROOT", tmp_path):
mock_st.session_state = mock_state mock_st.session_state = mock_state
mock_st.context.headers = {"x-cf-session": "valid.jwt.token"} mock_st.context.headers = {"x-cf-session": "cf_session=valid.jwt.token"}
cs.resolve_session("peregrine") cs.resolve_session("peregrine")
assert mock_state["user_id"] == "user-uuid-123" assert mock_state["user_id"] == "user-uuid-123"
@ -46,7 +46,7 @@ def test_resolve_session_creates_user_dir(tmp_path, monkeypatch):
patch.object(cs, "st") as mock_st, \ patch.object(cs, "st") as mock_st, \
patch.object(cs, "CLOUD_DATA_ROOT", tmp_path): patch.object(cs, "CLOUD_DATA_ROOT", tmp_path):
mock_st.session_state = mock_state mock_st.session_state = mock_state
mock_st.context.headers = {"x-cf-session": "valid.jwt.token"} mock_st.context.headers = {"x-cf-session": "cf_session=valid.jwt.token"}
cs.resolve_session("peregrine") cs.resolve_session("peregrine")
assert (tmp_path / "new-user" / "peregrine").is_dir() assert (tmp_path / "new-user" / "peregrine").is_dir()