Commit 9578bb7
authored
Add root-level OAuth discovery endpoint to prevent request hangs (#673)
OAuth discovery requests to `/.well-known/oauth-authorization-server`
hang instead of returning 404, causing Codex rmcp client to timeout
after 15s per server. Gateway only registers handler at
`/mcp/.well-known/oauth-authorization-server`.
## Changes
- **Route registration**: Add handler at
`/.well-known/oauth-authorization-server` in both unified
(`transport.go`) and routed (`routed.go`) modes
- **Handler reuse**: Both paths now use existing
`handleOAuthDiscovery()` that returns 404
- **Test coverage**: Added test cases for root-level and MCP-prefixed
paths (GET/POST methods)
## Implementation
```go
// OAuth discovery endpoints - return 404 since we don't use OAuth
// Standard path for OAuth discovery (per RFC 8414)
mux.Handle("/.well-known/oauth-authorization-server", withResponseLogging(handleOAuthDiscovery()))
// MCP-prefixed path for backward compatibility
mux.Handle("/mcp/.well-known/oauth-authorization-server", withResponseLogging(handleOAuthDiscovery()))
```
Result: OAuth discovery now fails fast with immediate 404 instead of
timing out.
> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more
addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by
firewall rules:
>
> - `example.com`
> - Triggering command: `/tmp/go-build3835057396/b270/launcher.test
/tmp/go-build3835057396/b270/launcher.test
-test.testlogfile=/tmp/go-build3835057396/b270/testlog.txt
-test.paniconexit0 -test.timeout=10m0s -test.v=true g_.a dler at
/.well-known/oauth-authorization-server for both unified and routed
modes
- Prevents req--norc x_amd64/vet` (dns block)
> - `invalid-host-that-does-not-exist-12345.com`
> - Triggering command: `/tmp/go-build3835057396/b258/config.test
/tmp/go-build3835057396/b258/config.test
-test.testlogfile=/tmp/go-build3835057396/b258/testlog.txt
-test.paniconexit0 -test.timeout=10m0s -test.v=true
ache/go/1.25.6/x64/src/runtime/cgo` (dns block)
> - `nonexistent.local`
> - Triggering command: `/tmp/go-build3835057396/b270/launcher.test
/tmp/go-build3835057396/b270/launcher.test
-test.testlogfile=/tmp/go-build3835057396/b270/testlog.txt
-test.paniconexit0 -test.timeout=10m0s -test.v=true g_.a dler at
/.well-known/oauth-authorization-server for both unified and routed
modes
- Prevents req--norc x_amd64/vet` (dns block)
> - `slow.example.com`
> - Triggering command: `/tmp/go-build3835057396/b270/launcher.test
/tmp/go-build3835057396/b270/launcher.test
-test.testlogfile=/tmp/go-build3835057396/b270/testlog.txt
-test.paniconexit0 -test.timeout=10m0s -test.v=true g_.a dler at
/.well-known/oauth-authorization-server for both unified and routed
modes
- Prevents req--norc x_amd64/vet` (dns block)
> - `this-host-does-not-exist-12345.com`
> - Triggering command: `/tmp/go-build3835057396/b279/mcp.test
/tmp/go-build3835057396/b279/mcp.test
-test.testlogfile=/tmp/go-build3835057396/b279/testlog.txt
-test.paniconexit0 -test.timeout=10m0s -test.v=true g_.a
64/src/crypto/internal/boring/bbig/big.go
df11d1ebad5122b89293a9cf3b8353f09e1d0c3b3841ebf6471/log.json -g"
"-lresolv" ernal/sys -I 64/pkg/tool/linux_amd64/vet` (dns block)
>
> If you need me to access, download, or install something from one of
these locations, you can either:
>
> - Configure [Actions setup
steps](https://gh.io/copilot/actions-setup-steps) to set up my
environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this
repository's [Copilot coding agent
settings](https://github.com/github/gh-aw-mcpg/settings/copilot/coding_agent)
(admins only)
>
> </details>
<!-- START COPILOT ORIGINAL PROMPT -->
<details>
<summary>Original prompt</summary>
>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>OAuth discovery requests hang instead of returning
404</issue_title>
> <issue_description>## Summary
>
> OAuth discovery requests to `/.well-known/oauth-authorization-server`
hang indefinitely instead of returning a quick 404 response. This causes
Codex MCP connections to timeout after 15 seconds per server.
>
> ## Problem
>
> The Codex rmcp client (Rust MCP client) attempts OAuth discovery at
the **standard path**:
> ```
> GET http://host.docker.internal/.well-known/oauth-authorization-server
> ```
>
> But the MCP Gateway registers the OAuth handler at:
> ```
> /mcp/.well-known/oauth-authorization-server
> ```
>
> Since there's no handler at `/.well-known/...`, the request has no
matching route and hangs instead of returning 404.
>
> ## Evidence
>
> ### Smoke-codex workflow run:
https://github.com/github/gh-aw/actions/runs/21688558782
>
> **Error pattern:**
> ```
> DEBUG session_init: codex_rmcp_client::auth_status: OAuth discovery
requests failed for
> http://host.docker.internal:80/mcp/playwright: error sending request
for url
> (http://host.docker.internal/.well-known/oauth-authorization-server)
>
> Caused by:
> operation timed out
> ```
>
> **Result:**
> - OAuth discovery times out (15 seconds per server)
> - 4 of 6 MCP servers fail to connect
> - Only tavily and safeoutputs succeed (race condition - they complete
before timeout exhausts resources)
>
> **Firewall logs confirm traffic reaches gateway:**
> ```
> ▼ 11 requests | 11 allowed | 0 blocked | 1 unique domain
> | Domain | Allowed | Denied |
> |---------------------|---------|--------|
> | host.docker.internal | 11 | 0 |
> ```
>
> ### Comparison with working run
>
> In run 21653900083 (before chroot mode), using IP address 172.30.0.1,
OAuth discovery was NOT attempted and all 6 MCP servers connected
successfully:
> ```
> ready: ["safeoutputs", "safeinputs", "github", "playwright", "tavily",
"serena"]
> ```
>
> ## Current Route Registration
>
> From `internal/server/transport.go`:
>
> | Route | Handler |
> |-------|---------|
> | `/mcp/.well-known/oauth-authorization-server` | OAuth handler
(returns 404) |
> | `/mcp/` and `/mcp` | StreamableHTTPHandler |
> | `/health` | Health check |
> | `/close` | Graceful shutdown |
> | `/.well-known/*` | **NO HANDLER** ← causes hang |
>
> ## Proposed Fix
>
> Add a handler for OAuth discovery at the standard path (without
`/mcp/` prefix):
>
> ```go
> // In internal/server/transport.go, add alongside existing routes:
> mux.HandleFunc("/.well-known/oauth-authorization-server", func(w
http.ResponseWriter, r *http.Request) {
> http.NotFound(w, r)
> })
> ```
>
> This ensures OAuth discovery requests get an immediate 404 response
instead of hanging.
>
> ## Impact
>
> This fix would:
> 1. Make OAuth discovery fail fast (instant 404 instead of 15s timeout)
> 2. Allow all 6 MCP servers to connect successfully in Codex workflows
> 3. Fix smoke-codex CI failures
>
> ## Related
>
> - gh-aw PR github/gh-aw-mcpg#13792: Removed hardcoded IP from Codex
config (firewall fix - separate issue)</issue_description>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>
</details>
<!-- START COPILOT CODING AGENT SUFFIX -->
- Fixes #672
<!-- START COPILOT CODING AGENT TIPS -->
---
✨ Let Copilot coding agent [set things up for
you](https://github.com/github/gh-aw-mcpg/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.4 files changed
Lines changed: 80 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
82 | 82 | | |
83 | 83 | | |
84 | 84 | | |
85 | | - | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
86 | 89 | | |
87 | 90 | | |
88 | 91 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
553 | 553 | | |
554 | 554 | | |
555 | 555 | | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
63 | | - | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
64 | 67 | | |
65 | 68 | | |
66 | 69 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
233 | 233 | | |
234 | 234 | | |
235 | 235 | | |
236 | | - | |
| 236 | + | |
237 | 237 | | |
238 | 238 | | |
239 | 239 | | |
240 | 240 | | |
241 | 241 | | |
242 | | - | |
| 242 | + | |
243 | 243 | | |
244 | 244 | | |
245 | 245 | | |
246 | 246 | | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
247 | 259 | | |
248 | 260 | | |
249 | 261 | | |
| |||
0 commit comments