fix(agent): resolve missing imports — jose, auth config, AUTH_SECRET_KEY

- Added python-jose[cryptography] to requirements-agent.txt
- Made sqlalchemy.orm.Session and TokenBlacklist imports lazy in jwt.py
  so decode_token works without pulling ORM models into agent image
- Added src/core/auth/__init__.py, config.py, jwt.py to agent Dockerfile COPY
- Added AUTH_SECRET_KEY env var to agent compose (reads from JWT_SECRET)
This commit is contained in:
2026-07-06 12:13:26 +03:00
parent 627b75497c
commit 468bdb9000
6 changed files with 35 additions and 17 deletions

View File

@@ -37,3 +37,7 @@ psycopg>=3.1
# Retry/utility
tenacity>=8.0.0
requests>=2.32.0
# JWT token validation (used by src.core.auth.jwt.decode_token)
python-jose[cryptography]

View File

@@ -16,9 +16,7 @@ import hashlib
import uuid
from jose import jwt
from sqlalchemy.orm import Session
from ...models.auth import TokenBlacklist
from ..logger import belief_scope
from .config import auth_config
@@ -38,20 +36,18 @@ def create_access_token(data: dict, expires_delta: timedelta | None = None) -> s
if expires_delta:
expire = now + expires_delta
else:
expire = now + timedelta(
minutes=auth_config.ACCESS_TOKEN_EXPIRE_MINUTES
)
expire = now + timedelta(minutes=auth_config.ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({
"exp": expire,
"iat": now,
"jti": str(uuid.uuid4()),
"aud": auth_config.JWT_AUDIENCE,
"iss": auth_config.JWT_ISSUER,
})
encoded_jwt = jwt.encode(
to_encode, auth_config.SECRET_KEY, algorithm=auth_config.ALGORITHM
to_encode.update(
{
"exp": expire,
"iat": now,
"jti": str(uuid.uuid4()),
"aud": auth_config.JWT_AUDIENCE,
"iss": auth_config.JWT_ISSUER,
}
)
encoded_jwt = jwt.encode(to_encode, auth_config.SECRET_KEY, algorithm=auth_config.ALGORITHM)
return encoded_jwt
@@ -100,6 +96,7 @@ def decode_token(token: str) -> dict:
def _hash_token(token: str) -> str:
return hashlib.sha256(token.encode()).hexdigest()
# #endregion Auth.Jwt._HashToken
@@ -108,11 +105,15 @@ def _hash_token(token: str) -> str:
# @PRE db is a valid SQLAlchemy session.
# @POST Expired blacklist rows are removed.
# @SIDE_EFFECT Mutates token_blacklist table.
def _prune_blacklist(db: Session) -> None:
def _prune_blacklist(db: "Session") -> None:
from sqlalchemy.orm import Session # noqa: F811
from ...models.auth import TokenBlacklist
now = datetime.now(UTC)
db.query(TokenBlacklist).filter(TokenBlacklist.expires_at < now).delete()
db.commit()
# #endregion Auth.Jwt._PruneBlacklist
@@ -125,7 +126,10 @@ def _prune_blacklist(db: Session) -> None:
# @RELATION DEPENDS_ON -> [TokenBlacklist]
# @RELATION CALLS -> [Auth.Jwt._HashToken]
# @TEST_EDGE: already_expired -> skips blacklisting
def blacklist_token(token: str, db: Session) -> None:
def blacklist_token(token: str, db: "Session") -> None:
from sqlalchemy.orm import Session # noqa: F811
from ...models.auth import TokenBlacklist
with belief_scope("blacklist_token"):
_prune_blacklist(db)
try:
@@ -165,7 +169,10 @@ def blacklist_token(token: str, db: Session) -> None:
# @PRE db is a valid SQLAlchemy session.
# @POST Returns True if token is blacklisted, False otherwise.
# @RELATION DEPENDS_ON -> [TokenBlacklist]
def is_token_blacklisted(token: str, db: Session) -> bool:
def is_token_blacklisted(token: str, db: "Session") -> bool:
from sqlalchemy.orm import Session # noqa: F811
from ...models.auth import TokenBlacklist
with belief_scope("is_token_blacklisted"):
try:
payload = decode_token(token)

View File

@@ -307,6 +307,7 @@ services:
LLM_MODEL: \${LLM_MODEL:-gpt-4o}
FASTAPI_URL: http://backend:8000
JWT_SECRET: \${JWT_SECRET:-super-secret-key}
AUTH_SECRET_KEY: \${JWT_SECRET:-super-secret-key}
SERVICE_JWT: \${SERVICE_JWT:-agent-service-secret}
DATABASE_URL: postgresql+psycopg2://\${POSTGRES_USER:-postgres}:\${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}@\${POSTGRES_HOST:?Set POSTGRES_HOST}:\${POSTGRES_PORT:-5432}/\${POSTGRES_DB:-ss_tools}
GRADIO_SERVER_PORT: 7860
@@ -611,6 +612,7 @@ services:
LLM_MODEL: \${LLM_MODEL:-gpt-4o}
FASTAPI_URL: http://backend:8000
JWT_SECRET: \${JWT_SECRET:-super-secret-key}
AUTH_SECRET_KEY: \${JWT_SECRET:-super-secret-key}
SERVICE_JWT: \${SERVICE_JWT:-agent-service-secret}
DATABASE_URL: postgresql+psycopg2://\${POSTGRES_USER:-postgres}:\${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}@\${POSTGRES_HOST:?Set POSTGRES_HOST}:\${POSTGRES_PORT:-5432}/\${POSTGRES_DB:-ss_tools}
GRADIO_SERVER_PORT: 7860

View File

@@ -104,6 +104,7 @@ services:
LLM_MODEL: ${LLM_MODEL:-gpt-4o}
FASTAPI_URL: http://backend:8000
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET must be set — crash-early, no default fallback}
AUTH_SECRET_KEY: ${JWT_SECRET:?JWT_SECRET must be set — crash-early, no default fallback}
SERVICE_JWT: ${SERVICE_JWT:-agent-service-secret}
DATABASE_URL: postgresql+psycopg2://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env.enterprise-clean}@${POSTGRES_HOST:?Set POSTGRES_HOST in .env.enterprise-clean}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-ss_tools}
GRADIO_SERVER_PORT: 7860

View File

@@ -60,6 +60,7 @@ services:
LLM_MODEL: ${LLM_MODEL:-gpt-4o}
FASTAPI_URL: http://backend:8000
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET must be set — crash-early, no default fallback}
AUTH_SECRET_KEY: ${JWT_SECRET:?JWT_SECRET must be set — crash-early, no default fallback}
SERVICE_JWT: ${SERVICE_JWT:-agent-service-secret}
DATABASE_URL: postgresql+psycopg2://postgres:postgres@db:5432/ss_tools
GRADIO_SERVER_PORT: 7860

View File

@@ -45,6 +45,9 @@ COPY backend/src/core/__init__.py /app/backend/src/core/__init__.py
COPY backend/src/core/cot_logger.py /app/backend/src/core/cot_logger.py
COPY backend/src/core/logger.py /app/backend/src/core/logger.py
COPY backend/src/core/ws_log_handler.py /app/backend/src/core/ws_log_handler.py
COPY backend/src/core/auth/__init__.py /app/backend/src/core/auth/__init__.py
COPY backend/src/core/auth/config.py /app/backend/src/core/auth/config.py
COPY backend/src/core/auth/jwt.py /app/backend/src/core/auth/jwt.py
COPY backend/src/agent/ /app/backend/src/agent/
# Gradio server