refactor(env): unify Docker env vars — canonical AUTH_SECRET_KEY, remove JWT_SECRET fallback

Canonical variables:
  - AUTH_SECRET_KEY — JWT signing key for backend + agent (was split across
    AUTH_SECRET_KEY / JWT_SECRET)
  - SERVICE_JWT — agent→backend service token
  - No JWT_SECRET fallback: decoder fails with migration guidance if only
    JWT_SECRET is set

Compose files unified:
  - docker-compose.yml, docker-compose.enterprise-clean.yml,
    docker-compose.e2e.yml — all use AUTH_SECRET_KEY
  - build.sh generated compose now passes AUTH_SECRET_KEY + ENCRYPTION_KEY
    to backend

Env examples unified and completed:
  - .env.example — comprehensive template, all compose vars
  - .env.enterprise-clean.example — production template
  - backend/.env.example — backend-only run
  - docker/.env.agent.example — agent-only run
  - NEW: .env.current.example, .env.master.example,
    .env.e2e.example, frontend/.env.example

Tests aligned:
  - conftest sets AUTH_SECRET_KEY (canonical value matched across test files)
  - test mocks use canonical name
  - 1176 passed, 0 failed
This commit is contained in:
2026-07-06 14:24:17 +03:00
parent a5b7adb61c
commit 48e3ff4503
15 changed files with 586 additions and 126 deletions

View File

@@ -1,52 +1,50 @@
# backend/src/agent/_jwt_decoder.py
# #region AgentChat.JwtDecoder [C:1] [TYPE Module] [SEMANTICS agent-chat,jwt,decode]
# @BRIEF Lightweight JWT decode for agent — uses JWT_SECRET env var directly, avoids
# @BRIEF Lightweight JWT decode for agent — uses AUTH_SECRET_KEY env var, avoids
# pulling src.core.auth.jwt which requires AUTH_DATABASE_URL and ORM deps.
# @RATIONALE The agent only needs stateless JWT validation (exp, sub, signature).
# Token blacklisting is only relevant for backend auth flow — the agent
# processes one request at a time and doesn't need DB-backed revocation.
# @INVARIANT AUTH_SECRET_KEY is the ONLY accepted JWT signing key. JWT_SECRET is
# unsupported and triggers a migration message if present without
# AUTH_SECRET_KEY.
# @REJECTED Importing src.core.auth.jwt.decode_token was rejected — it drags in
# AuthConfig AUTH_DATABASE_URL/SECRET_KEY validators SQLAlchemy models,
# AuthConfig -> AUTH_DATABASE_URL/SECRET_KEY validators -> SQLAlchemy models,
# bloating the agent image with backend infrastructure it doesn't need.
# @REJECTED Fallback from JWT_SECRET to AUTH_SECRET_KEY was rejected — ambiguous
# naming caused operator confusion and secret drift between environments.
import os
from jose import JWTError, jwt
def decode_token(token: str) -> dict:
"""Decode and validate a JWT token. Tries JWT_SECRET first, then AUTH_SECRET_KEY.
"""Decode and validate a JWT token using AUTH_SECRET_KEY from environment.
Performs stateless validation: signature, expiration, and required claims
(exp, sub). Does NOT check token blacklist or audience/issuer.
"""
keys = []
jwt_key = os.getenv("JWT_SECRET", "")
auth_key = os.getenv("AUTH_SECRET_KEY", "")
if jwt_key:
keys.append(jwt_key)
if auth_key and auth_key != jwt_key:
keys.append(auth_key)
if not keys:
raise JWTError("Neither JWT_SECRET nor AUTH_SECRET_KEY is set")
algorithm = os.getenv("JWT_ALGORITHM", "HS256")
last_error = None
for key in keys:
try:
return jwt.decode(
token,
key,
algorithms=[algorithm],
options={
"verify_signature": True,
"verify_exp": True,
"verify_aud": False,
"require": ["exp", "sub"],
},
)
except JWTError as e:
last_error = e
raise last_error
Raises JWTError with migration guidance if JWT_SECRET is set but
AUTH_SECRET_KEY is missing (old deployments must rename the variable).
"""
secret = os.getenv("AUTH_SECRET_KEY", "")
jwt_secret_legacy = os.getenv("JWT_SECRET", "")
if not secret:
if jwt_secret_legacy:
raise JWTError("JWT_SECRET is no longer supported. Rename JWT_SECRET to AUTH_SECRET_KEY in your .env / docker-compose and restart the agent.")
raise JWTError("AUTH_SECRET_KEY environment variable is not set")
return jwt.decode(
token,
secret,
algorithms=[os.getenv("JWT_ALGORITHM", "HS256")],
options={
"verify_signature": True,
"verify_exp": True,
"verify_aud": False,
"require": ["exp", "sub"],
},
)
# #endregion AgentChat.JwtDecoder