Files
Apple ef3473db21 snapshot: NODE1 production state 2026-02-09
Complete snapshot of /opt/microdao-daarion/ from NODE1 (144.76.224.179).
This represents the actual running production code that has diverged
significantly from the previous main branch.

Key changes from old main:
- Gateway (http_api.py): expanded from ~40KB to 164KB with full agent support
- Router: new /v1/agents/{id}/infer endpoint with vision + DeepSeek routing
- Behavior Policy: SOWA v2.2 (3-level: FULL/ACK/SILENT)
- Agent Registry: config/agent_registry.yml as single source of truth
- 13 agents configured (was 3)
- Memory service integration
- CrewAI teams and roles

Excluded from snapshot: venv/, .env, data/, backups, .tgz archives

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 08:46:46 -08:00

187 lines
5.8 KiB
Python

"""
Service-to-Service Authentication
=================================
JWT-based authentication between internal services.
Usage:
from service_auth import create_service_token, verify_service_token, require_service_auth
# Create token for service
token = create_service_token("router", "router")
# Verify in endpoint
@app.get("/protected")
@require_service_auth(allowed_roles=["router", "gateway"])
async def protected_endpoint():
return {"status": "ok"}
"""
import os
import jwt
import time
from typing import List, Optional, Dict, Any
from functools import wraps
from fastapi import HTTPException, Header, Request
# Configuration
JWT_SECRET = os.getenv("JWT_SECRET", "change-me-in-production")
JWT_ALGORITHM = "HS256"
JWT_AUDIENCE = os.getenv("SERVICE_AUD", "microdao-internal")
JWT_ISSUER = os.getenv("SERVICE_ISS", "microdao")
# Service roles and permissions
SERVICE_ROLES = {
"gateway": ["gateway", "router", "worker", "parser"],
"router": ["router", "worker"],
"worker": ["worker"],
"memory": ["memory"],
"control-plane": ["control-plane"],
"parser": ["parser"],
"ingest": ["ingest"]
}
# Service-to-service access matrix
SERVICE_ACCESS = {
"gateway": ["memory", "control-plane", "router"],
"router": ["memory", "control-plane", "swapper"],
"worker": ["memory", "router"],
"parser": ["memory"],
"ingest": ["memory"]
}
def create_service_token(service_id: str, service_role: str, expires_in: int = 900) -> str:
"""
Create JWT token for service-to-service authentication.
Args:
service_id: Unique service identifier (e.g., "router", "gateway")
service_role: Service role (e.g., "router", "gateway")
expires_in: Token expiration in seconds (default: 1 hour)
Returns:
JWT token string
"""
now = int(time.time())
payload = {
"sub": service_id,
"role": service_role,
"aud": JWT_AUDIENCE,
"iss": JWT_ISSUER,
"iat": now,
"exp": now + expires_in,
"service_id": service_id,
"service_role": service_role
}
token = jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)
return token
def verify_service_token(token: str) -> Dict[str, Any]:
"""
Verify service JWT token.
Returns:
Decoded token payload
Raises:
HTTPException: If token is invalid
"""
try:
payload = jwt.decode(
token,
JWT_SECRET,
algorithms=[JWT_ALGORITHM],
audience=JWT_AUDIENCE,
issuer=JWT_ISSUER
)
return payload
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token expired")
except jwt.InvalidTokenError as e:
raise HTTPException(status_code=401, detail=f"Invalid token: {e}")
def require_service_auth(allowed_roles: List[str] = None, allowed_services: List[str] = None):
"""
Decorator to require service authentication.
Args:
allowed_roles: List of allowed service roles
allowed_services: List of allowed service IDs
"""
def decorator(func):
@wraps(func)
async def wrapper(request: Request, *args, **kwargs):
# Get Authorization header
auth_header = request.headers.get("Authorization", "")
if not auth_header.startswith("Bearer "):
raise HTTPException(
status_code=401,
detail="Missing or invalid Authorization header"
)
token = auth_header.replace("Bearer ", "")
try:
payload = verify_service_token(token)
service_id = payload.get("service_id")
service_role = payload.get("role")
# Check if service is allowed
if allowed_roles and service_role not in allowed_roles:
raise HTTPException(
status_code=403,
detail=f"Service role '{service_role}' not allowed"
)
if allowed_services and service_id not in allowed_services:
raise HTTPException(
status_code=403,
detail=f"Service '{service_id}' not allowed"
)
# Add service info to request state
request.state.service_id = service_id
request.state.service_role = service_role
return await func(request, *args, **kwargs)
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=401, detail=f"Authentication failed: {e}")
return wrapper
return decorator
def get_service_token() -> str:
"""
Get service token for current service (from environment).
"""
service_id = os.getenv("SERVICE_ID")
service_role = os.getenv("SERVICE_ROLE", service_id)
if not service_id:
raise ValueError("SERVICE_ID environment variable not set")
return create_service_token(service_id, service_role)
# FastAPI dependency for service auth
async def verify_service(request: Request, authorization: str = Header(None)):
"""FastAPI dependency for service authentication"""
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Missing Authorization header")
token = authorization.replace("Bearer ", "")
payload = verify_service_token(token)
request.state.service_id = payload.get("service_id")
request.state.service_role = payload.get("role")
return payload