3333- Federates authentication to ** any OIDC-compliant IdP** via auto-discovery
3434 (no vendor lock-in, zero IdP-specific code)
3535- Reverse-proxies to your ** unmodified** upstream MCP server
36- - ** Stateless** — no database, no Redis , no sticky sessions; scale
36+ - ** Stateless by default ** — no database, no sticky sessions; scale
3737 horizontally by sharing one secret
38+ - ** Optional Redis** unlocks strict single-use authorization codes and
39+ refresh-rotation reuse detection (OAuth 2.1 §6.1) across replicas
40+ - Per-IP ** rate limiting** on every pre-auth endpoint, ` email_verified `
41+ enforcement on the IdP id_token, and ** Prometheus metrics** for every
42+ security-relevant event
3843
3944---
4045
@@ -116,6 +121,9 @@ All configuration via **environment variables**. Bold = required.
116121| ` REVOKE_BEFORE ` | (empty) | RFC3339 cutoff for bulk revocation (applies to access * and* refresh tokens) |
117122| ` PKCE_REQUIRED ` | ` true ` | Set ` false ` for clients that omit PKCE (Cursor, MCP Inspector, ChatGPT) |
118123| ` SHUTDOWN_TIMEOUT ` | ` 120s ` | Graceful shutdown; must be ≥ longest expected SSE stream |
124+ | ` REDIS_URL ` | (empty) | Optional. Enables single-use authz codes + refresh-rotation reuse detection. ` rediss:// ` for TLS |
125+ | ` REDIS_KEY_PREFIX ` | ` mcp-auth-proxy: ` | Key prefix for shared Redis; set to empty to opt out of namespacing |
126+ | ` RATE_LIMIT_ENABLED ` | ` true ` | Per-IP rate limiting on pre-auth endpoints. Disable only behind a WAF that already enforces it |
119127
120128---
121129
@@ -132,7 +140,7 @@ required to operate this service.
132140| ---| ---| ---|
133141| Client registration | ` client_id ` | 24h |
134142| Authorize session | IdP ` state ` parameter | 10min |
135- | Authorization code | ` code ` parameter | 5min |
143+ | Authorization code | ` code ` parameter | 60s |
136144| Access token | Opaque bearer | 1h |
137145| Refresh token | Opaque bearer | 7d |
138146
@@ -156,7 +164,8 @@ rollout notes, and K8s deployment shape.
156164| ` GET /authorize ` | PKCE authorization endpoint |
157165| ` GET /callback ` | OIDC callback from the IdP |
158166| ` POST /token ` | ` authorization_code ` + ` refresh_token ` grants |
159- | ` GET /healthz ` | Liveness / readiness probe |
167+ | ` GET /healthz ` | Liveness probe (always 200 while the process is up) |
168+ | ` GET /readyz ` | Readiness probe (503 when Redis is configured but unreachable) |
160169| ` * ` (any other path) | Reverse-proxied to ` UPSTREAM_MCP_URL ` after Bearer check |
161170| ` GET /metrics ` (port 9090) | Prometheus metrics |
162171
@@ -166,10 +175,20 @@ rollout notes, and K8s deployment shape.
166175
167176- ** Structured logs** — zap, JSON in production, console when run on a TTY.
168177 Every request carries a ` request_id ` in the log and in the
169- ` X-Request-Id ` response header.
170- - ** Metrics** — Prometheus on a dedicated port so it's never exposed
171- through the public listener.
172- - ** Health** — ` GET /healthz ` returns ` 200 OK ` .
178+ ` X-Request-Id ` response header. Inbound ` X-Request-Id ` is stripped to
179+ prevent log-forgery.
180+ - ** Metrics** — Prometheus on a dedicated port (` :9090 ` , separate listener,
181+ not exposed through the public router). Alongside the default Go
182+ runtime counters, the proxy emits:
183+ - ` mcp_auth_tokens_issued_total{grant_type} ` — access tokens minted
184+ - ` mcp_auth_access_denied_total{reason} ` — group / ` email_unverified `
185+ / ` refresh_family_revoked ` rejections
186+ - ` mcp_auth_replay_detected_total{kind} ` — ` code ` or ` refresh ` replays
187+ caught by the Redis-backed store
188+ - ` mcp_auth_rate_limited_total{endpoint} ` — httprate 429s by endpoint
189+ - ` mcp_auth_clients_registered_total ` — RFC 7591 registrations
190+ - ** Health** — ` GET /healthz ` (liveness) and ` GET /readyz ` (reflects
191+ Redis reachability when ` REDIS_URL ` is set).
173192
174193---
175194
0 commit comments