Skip to content
Closed
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
29 changes: 29 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,32 @@ RERANKER_BATCH_SIZE=10
BACKEND_IMAGE=ghcr.io/manavgup/rag_modulo/backend:latest
FRONTEND_IMAGE=ghcr.io/manavgup/rag_modulo/frontend:latest
TEST_IMAGE=ghcr.io/manavgup/rag_modulo/backend:latest

# ================================
# SPIFFE/SPIRE IDENTITY (Agent/Machine Identity)
# ================================
# Enable SPIFFE workload identity for agents and services
# See: https://spiffe.io/docs/latest/spire-about/spire-concepts/
SPIFFE_ENABLED=false

# Authentication mode for migration (disabled|optional|preferred|required)
# - disabled: No SPIFFE support (current default)
# - optional: Accept both user JWT and SPIFFE JWT-SVID
# - preferred: Prefer SPIFFE, log warning on legacy JWT
# - required: Only SPIFFE JWT-SVIDs accepted for workloads
SPIFFE_AUTH_MODE=disabled

# SPIRE Agent Workload API socket path
SPIFFE_ENDPOINT_SOCKET=unix:///run/spire/agent/api.sock

# SPIFFE trust domain for this environment
SPIFFE_TRUST_DOMAIN=rag-modulo.local

# Log warning when legacy JWT is used (for migration tracking)
SPIFFE_LEGACY_JWT_WARNING=false

# Default SVID TTL in seconds (default: 3600 = 1 hour)
SPIFFE_SVID_TTL_SECONDS=3600

# Allowed JWT-SVID audiences (comma-separated)
SPIFFE_JWT_AUDIENCES=rag-modulo,mcp-gateway
77 changes: 75 additions & 2 deletions backend/core/authentication_middleware.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
"""Authentication middleware for FastAPI application.

This module provides middleware for handling JWT-based authentication,
including support for development/testing modes and mock user creation.
including support for development/testing modes, mock user creation,
and SPIFFE JWT-SVID authentication for AI agents.

SPIFFE Integration:
This middleware supports SPIFFE JWT-SVIDs for agent authentication.
When a Bearer token with a SPIFFE ID in the 'sub' claim is detected,
it validates the token and creates an agent principal instead of a user.

Reference: docs/architecture/spire-integration-architecture.md
"""

import logging
Expand All @@ -19,6 +27,10 @@
from core.config import get_settings
from core.mock_auth import create_mock_user_data, ensure_mock_user_exists, is_bypass_mode_active, is_mock_token
from core.request_context import RequestContext
from core.spiffe_auth import (
get_spiffe_authenticator,
is_spiffe_jwt_svid,
)

# Get settings safely for middleware
settings = get_settings()
Expand Down Expand Up @@ -206,9 +218,64 @@ def _handle_mock_token(self, request: Request, token: str) -> bool: # pylint: d
logger.info("AuthMiddleware: Using mock test token")
return True

def _handle_spiffe_jwt_svid(self, request: Request, token: str) -> bool:
"""Handle SPIFFE JWT-SVID authentication for agents.

This method validates a SPIFFE JWT-SVID and sets up the agent
principal in the request state.

Args:
request: The FastAPI request object.
token: The SPIFFE JWT-SVID token.

Returns:
True if SPIFFE JWT-SVID was handled successfully.
"""
try:
authenticator = get_spiffe_authenticator()
principal = authenticator.validate_jwt_svid(token)

if principal is None:
logger.warning("AuthMiddleware: Invalid SPIFFE JWT-SVID")
return False

# Check if the agent is expired
if principal.is_expired():
logger.warning("AuthMiddleware: Expired SPIFFE JWT-SVID for %s", principal.spiffe_id)
return False

# Set agent principal in request state
request.state.agent = principal
request.state.identity_type = "agent"

# Also set a unified principal representation for compatibility
agent_data = {
"identity_type": "agent",
"spiffe_id": principal.spiffe_id,
"agent_type": principal.agent_type.value,
"agent_id": principal.agent_id,
"capabilities": [cap.value for cap in principal.capabilities],
"audiences": principal.audiences,
}
request.state.user = agent_data # For backward compatibility
RequestContext.set_user(agent_data)

logger.info(
"AuthMiddleware: SPIFFE JWT-SVID validated successfully. Agent: %s (type: %s)",
principal.spiffe_id,
principal.agent_type.value,
)
return True
except Exception as e:
logger.warning("AuthMiddleware: Error validating SPIFFE JWT-SVID - %s", e)
return False

def _handle_jwt_token(self, request: Request, token: str) -> bool:
"""Handle JWT token authentication and cache user data.

This method handles both traditional user JWTs and SPIFFE JWT-SVIDs.
It detects the token type and delegates to the appropriate handler.

Args:
request: The FastAPI request object.
token: The JWT token.
Expand All @@ -221,16 +288,22 @@ def _handle_jwt_token(self, request: Request, token: str) -> bool:
if is_mock_token(token):
return self._handle_mock_token(request, token)

# Verify JWT using the verify_jwt_token function
# Check if this is a SPIFFE JWT-SVID (agent authentication)
if is_spiffe_jwt_svid(token):
return self._handle_spiffe_jwt_svid(request, token)

# Verify JWT using the verify_jwt_token function (user authentication)
payload = verify_jwt_token(token)
user_data = {
"identity_type": "user",
"id": payload.get("sub"),
"email": payload.get("email"),
"name": payload.get("name"),
"uuid": payload.get("uuid"),
"role": payload.get("role"),
}
request.state.user = user_data
request.state.identity_type = "user"
# Cache user data in request context to eliminate N+1 queries
RequestContext.set_user(user_data)
logger.info("AuthMiddleware: JWT token validated successfully. User: %s", request.state.user)
Expand Down
Loading
Loading