Skip to main content

34. API Key to JWT Exchange Pattern

Date: 2025-01-28

Status

Accepted

Category

Authentication & Authorization

Context

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

  1. Exchange, Not Replace: API keys exchanged for JWTs, not standalone auth
  2. Keycloak Storage: API keys stored in Keycloak user/client attributes
  3. Transparent to Backend: MCP Server only sees JWTs
  4. User Attribution: API keys linked to user or service principal identity
  5. Standard Validation: JWT validation applies same rules
  6. Rotation Support: Keys can be rotated without changing client code

API Key Storage in Keycloak

User Attributes:

Exchange Flow

  1. Client → Kong: Authorization: ApiKey sk_live_abc123xyz
  2. Kong Plugin: Extract key, hash with bcrypt, query Keycloak for match
  3. If found: Call Keycloak token endpoint, get JWT
  4. Replace header: Authorization: Bearer <JWT>
  5. 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

  1. Standalone API Keys: Rejected - violates JWT standardization
  2. Kong-Only Storage: Rejected - inconsistent with Keycloak authority
  3. No API Keys: Rejected - too disruptive for legacy integrations
  4. Long-Lived JWTs: Rejected - cannot revoke, security risk

Implementation

APIKeyManager (src/mcp_server_langgraph/auth/api_keys.py):
Kong Plugin (deployments/kong/custom-plugins/kong-apikey-jwt-exchange.lua):
API Endpoints (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