68. Gateway-Level Authentication with Traefik ForwardAuth
Date: 2025-12-09Status
AcceptedCategory
SecurityContext
Authentication in the MCP Server LangGraph stack was handled at the application level:- Each service (MCP Server, Builder, Playground) independently validates JWT tokens from Keycloak
- Observability UIs (Grafana, Qdrant Dashboard, Traefik Dashboard) have no authentication
- This creates security gaps and duplicated authentication logic
Problems with Previous Approach
| Issue | Impact |
|---|---|
| Security Gap | Observability dashboards expose sensitive data without auth |
| Duplicated Logic | Each service implements JWT validation independently |
| Inconsistent UX | Some routes require auth, others don’t |
| No SSO | Users must authenticate separately per service |
Decision
Implement gateway-level authentication using Traefik’s ForwardAuth middleware with a dedicated authentication proxy service.Architecture
Route Classification
| Category | Routes | Auth Required |
|---|---|---|
| Public | /authn/* (Keycloak), */health*, */ready* | No |
| Protected - Apps | /mcp/*, /build/*, /play/* | Yes |
| Protected - Observability | /dashboards/*, /telemetry/*, /gateway/* | Yes |
| Protected - Data | /authz/*, /vectors/*, /dashboard/* | Yes |
Implementation Components
1. traefik-forward-auth Service
Using thomseddon/traefik-forward-auth:2. Traefik Middleware Configuration
Apply middleware to protected routes:3. Public Route Bypass
Health/readiness endpoints bypass auth by not including theforward-auth middleware.
Protected Routes Summary
| Route | Service | Middleware Chain |
|---|---|---|
/gateway | Traefik Dashboard | forward-auth@docker,gateway-rewrite |
/authz | OpenFGA API | forward-auth@docker,openfga-strip |
/vectors | Qdrant API | forward-auth@docker,qdrant-strip |
/dashboard | Qdrant Dashboard | forward-auth@docker |
/mcp | MCP Server API | forward-auth@docker,mcp-strip |
/dashboards | Grafana | forward-auth@docker |
/telemetry | Alloy | forward-auth@docker,alloy-strip |
/build | Builder | forward-auth@docker,builder-strip |
/play | MCP Playground | forward-auth@docker,playground-strip |
Test Users
Pre-configured users in Keycloak test realm:| User | Password | |
|---|---|---|
admin | admin | - |
alice | alice123 | alice@example.com |
bob | bob123 | bob@example.com |
Consequences
Positive
- Centralized Authentication: Single point of auth enforcement
- Consistent Security: All protected routes use same auth flow
- SSO Experience: One login works across all services
- Reduced Code: Remove JWT validation from individual services
- Observability Protection: Grafana, Qdrant UI now protected
Negative
- Additional Service: traefik-forward-auth adds complexity
- Single Point of Failure: Auth proxy down = all protected routes fail
- Cookie-Based: Session management adds state
- Local Dev Complexity: Need to handle auth in development
Neutral
- Migration Required: Existing services need middleware configuration
- Testing Changes: Integration tests need auth awareness
Alternatives Considered
1. OAuth2 Proxy
More feature-rich but heavier weight. Better for complex scenarios.2. Application-Level Only
Current approach. Simple but duplicates logic and leaves gaps.3. Keycloak Gatekeeper
Deprecated (louketo-proxy). Not recommended.4. Kong/Ambassador
Full API gateway. Overkill for test infrastructure.Related ADRs
- ADR-0007: Authentication Provider Pattern
- ADR-0031: Keycloak Authoritative Identity
- ADR-0067: Grafana LGTM Stack Migration
Files Changed
docker-compose.test.yml- Added traefik-forward-auth service and middlewaretests/integration/gateway/test_traefik_auth_middleware.py- Integration testsadr/adr-0068-gateway-level-authentication.md- This ADR