34. API Key to JWT Exchange Pattern
Date: 2025-01-28Status
AcceptedCategory
Authentication & AuthorizationContext
Legacy systems and simple integrations use API keys for authentication due to simplicity and stateless nature. However, our architecture standardizes on JWTs (ADR-0032) for consistent authentication and authorization. This creates tension between legacy support and JWT standardization. Current challenges:- Kong stores API keys separately from Keycloak (inconsistent)
- No user attribution for API key requests
- API keys don’t integrate with OpenFGA authorization
- Cannot enforce same permission model as user JWTs
Decision
We will implement an API Key to JWT Exchange Pattern where API keys are stored in Keycloak and exchanged for short-lived JWTs on each request.Architecture
Core Principles
- Exchange, Not Replace: API keys exchanged for JWTs, not standalone auth
- Keycloak Storage: API keys stored in Keycloak user/client attributes
- Transparent to Backend: MCP Server only sees JWTs
- User Attribution: API keys linked to user or service principal identity
- Standard Validation: JWT validation applies same rules
- Rotation Support: Keys can be rotated without changing client code
API Key Storage in Keycloak
User Attributes:Exchange Flow
- Client → Kong:
Authorization: ApiKey sk_live_abc123xyz - Kong Plugin: Extract key, hash with bcrypt, query Keycloak for match
- If found: Call Keycloak token endpoint, get JWT
- Replace header:
Authorization: Bearer <JWT> - Kong → MCP Server: Standard JWT validation
Configuration
Consequences
Positive Consequences
- JWT standardization maintained, consistent authorization
- User attribution, centralized management in Keycloak
- Audit trail, rotation support, expiration support
Negative Consequences
- Custom Kong plugin (Lua development)
- Exchange latency (20-50ms Keycloak call)
- Cache complexity, migration required
Mitigation Strategies
- Cache key→user mappings (5-min TTL, >90% hit rate target)
- Connection pooling to Keycloak
- Gradual migration with fallback period
Alternatives Considered
- Standalone API Keys: Rejected - violates JWT standardization
- Kong-Only Storage: Rejected - inconsistent with Keycloak authority
- No API Keys: Rejected - too disruptive for legacy integrations
- Long-Lived JWTs: Rejected - cannot revoke, security risk
Implementation
APIKeyManager (src/mcp_server_langgraph/auth/api_keys.py):
deployments/kong/custom-plugins/kong-apikey-jwt-exchange.lua):
src/mcp_server_langgraph/api/api_keys.py):
- POST
/api/v1/api-keys- Create - POST
/api/v1/api-keys/validate- Validate & exchange (Kong calls this) - GET
/api/v1/api-keys- List - DELETE
/api/v1/api-keys/{key_id}- Revoke
References
- Related ADRs: ADR-0031, ADR-0032, ADR-0035
- External: API Key Best Practices, bcrypt