Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions app/api/agentops/auth/environment.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import os
from agentops.api.log_config import logger

# generate an AUTH_COOKIE_SECRET with:
# Generate an AUTH_COOKIE_SECRET with:
# import secrets; print(secrets.token_hex(32))
_DEV_AUTH_COOKIE_SECRET = "your_cookie_signing_secret"
AUTH_COOKIE_SECRET = os.getenv("AUTH_COOKIE_SECRET", _DEV_AUTH_COOKIE_SECRET)
AUTH_COOKIE_SECRET: str | None = os.getenv("AUTH_COOKIE_SECRET")

AUTH_COOKIE_NAME = os.getenv("AUTH_COOKIE_NAME", "session_id")
AUTH_JWT_ALGO = "HS256" # this is for our internal JWT on the session cookie
Expand All @@ -24,8 +23,8 @@
SUPABASE_JWT_SECRET: str = os.getenv("JWT_SECRET_KEY")


if AUTH_COOKIE_SECRET == _DEV_AUTH_COOKIE_SECRET:
logger.warning("[agentops.auth.environment] Using an unsafe AUTH_COOKIE_SECRET")
if not AUTH_COOKIE_SECRET:
logger.error("[agentops.auth.environment] AUTH_COOKIE_SECRET is not configured")

if not SUPABASE_JWT_SECRET:
logger.warning("[agentops.auth.environment] No JWT_SECRET_KEY set")
Expand Down
6 changes: 6 additions & 0 deletions app/api/agentops/auth/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ def _encode_session_cookie(session: Session) -> str:
Currently this just includes the session_id in order to avoid storing any
sensitive information in the cookie.
"""
if not AUTH_COOKIE_SECRET:
raise AuthException("AUTH_COOKIE_SECRET environment variable is not configured.")

session_id = str(session.session_id)
return jwt.encode({"session_id": session_id}, AUTH_COOKIE_SECRET, algorithm=AUTH_JWT_ALGO)

Expand All @@ -153,6 +156,9 @@ def _decode_session_cookie(cookie: str) -> Session | None:
Raises AuthException if the cookie is invalid or expired.
Returns None if the session is not found.
"""
if not AUTH_COOKIE_SECRET:
raise AuthException("AUTH_COOKIE_SECRET environment variable is not configured.")

try:
data = jwt.decode(cookie, AUTH_COOKIE_SECRET, algorithms=[AUTH_JWT_ALGO])
return Session.get(data['session_id'])
Expand Down
40 changes: 40 additions & 0 deletions app/api/tests/auth/test_cookie_secret.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Regression tests for required session-cookie signing configuration."""

from uuid import uuid4

import jwt
import pytest

from agentops.auth import environment
from agentops.auth import views
from agentops.auth.exceptions import AuthException
from agentops.auth.session import Session


def test_missing_cookie_secret_has_no_committed_fallback(monkeypatch):
"""Cookie signing must fail closed when AUTH_COOKIE_SECRET is absent."""
monkeypatch.delenv("AUTH_COOKIE_SECRET", raising=False)
monkeypatch.setattr(environment, "AUTH_COOKIE_SECRET", None)
monkeypatch.setattr(views, "AUTH_COOKIE_SECRET", None)

assert environment.AUTH_COOKIE_SECRET != "your_cookie_signing_secret"
session = Session(session_id=uuid4(), user_id=uuid4())

with pytest.raises(AuthException, match="AUTH_COOKIE_SECRET"):
views._encode_session_cookie(session)
with pytest.raises(AuthException, match="AUTH_COOKIE_SECRET"):
views._decode_session_cookie("not-a-token")


def test_configured_cookie_secret_signs_and_verifies_session_cookie(monkeypatch):
"""A deployment-provided secret continues to support cookie authentication."""
secret = "test-only-cookie-secret"
monkeypatch.setattr(views, "AUTH_COOKIE_SECRET", secret)
session = Session(session_id=uuid4(), user_id=uuid4())
monkeypatch.setattr(Session, "get", classmethod(lambda cls, session_id: session))

token = views._encode_session_cookie(session)

decoded = jwt.decode(token, secret, algorithms=[views.AUTH_JWT_ALGO])
assert decoded["session_id"] == str(session.session_id)
assert views._decode_session_cookie(token) is session