You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add MCP well-known URI auth discovery per MCP Spec (#2527)
* Add MCP well-known URI auth discovery
Implements RFC 9728 Protected Resource Metadata discovery via
well-known URIs when WWW-Authenticate header is not present.
This completes ToolHive's implementation of the MCP specification
requirement that clients MUST support both discovery mechanisms.
Changes:
- Add tryWellKnownDiscovery() to discover auth via well-known URIs
- Add buildWellKnownURI() to construct RFC 9728 compliant URIs
- Add checkWellKnownURIExists() to validate URI accessibility
- Modify DetectAuthenticationFromServer() for well-known fallback
- Add comprehensive unit tests for all discovery paths
- Add regression tests for empty scope handling contracts
The implementation tries endpoint-specific URIs first, then root-level
URIs per MCP spec priority order. This enables authentication with
MCP-compliant servers that use well-known URIs per RFC 9728 Section 3
but don't send WWW-Authenticate headers.
Test coverage: 75.5% for pkg/auth/discovery
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Jon Christiansen <467023+theJC@users.noreply.github.com>
* Update per MR feedback regarding content-type returned from wellknown endpoint, also dont consider wellknown as existing if we cannot access due to unauthorized/401
* fix golangci-lint identified issues
* Add more tests for coverage
* Update comment to make clearer
* PR PR feedback, defensively control how much response draining we are willing to do
* Update test to be more effecient per PR feedback
---------
Signed-off-by: Jon Christiansen <467023+theJC@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
5.**Authorization Server Discovery**: Validates each server in metadata via OIDC discovery
87
+
6.**Issuer Mismatch Handling**: Accepts authoritative issuer from well-known endpoints per RFC 8414
88
+
89
+
**Phase 3: Fallback Discovery**
90
+
7.**URL-Derived**: Falls back to deriving from the remote URL (last resort)
61
91
62
92
### Authentication Branches
63
93
@@ -66,32 +96,40 @@ graph TD
66
96
A[Remote MCP Server Request] --> B{401 Response?}
67
97
B -->|No| C[No Authentication Required]
68
98
B -->|Yes| D{WWW-Authenticate Header?}
69
-
D -->|No| E[No Authentication Required]
70
99
D -->|Yes| F{Parse Header}
71
-
100
+
101
+
%% NEW: Well-known URI fallback when no WWW-Authenticate
102
+
D -->|No| WK1[Try Well-Known URI Discovery]
103
+
WK1 --> WK2{Try Endpoint-Specific URI}
104
+
WK2 -->|Found| WK4[Extract Auth Info]
105
+
WK2 -->|404| WK3{Try Root-Level URI}
106
+
WK3 -->|Found| WK4
107
+
WK3 -->|404| E[No Authentication Required]
108
+
WK4 --> K[Fetch Resource Metadata]
109
+
72
110
F --> G{Has Realm URL?}
73
111
G -->|Yes| H[Derive Issuer from Realm]
74
112
H --> I[OIDC Discovery]
75
-
113
+
76
114
F --> J{Has resource_metadata?}
77
-
J -->|Yes| K[Fetch Resource Metadata]
115
+
J -->|Yes| K
78
116
K --> L[Validate Auth Servers]
79
117
L --> M[Use First Valid Server]
80
-
118
+
81
119
F --> S{No Realm/Metadata?}
82
120
S -->|Yes| T[Probe Well-Known Endpoints]
83
121
T --> U{Found Valid Issuer?}
84
122
U -->|Yes| V[Use Discovered Issuer]
85
123
U -->|No| W[Derive from URL]
86
-
124
+
87
125
I --> N{Client Credentials?}
88
126
M --> N
89
127
V --> N
90
128
W --> N
91
129
N -->|No| O[Dynamic Registration]
92
130
N -->|Yes| P[OAuth Flow]
93
131
O --> P
94
-
132
+
95
133
P --> Q[Get Access Token]
96
134
Q --> R[Authenticated Request]
97
135
```
@@ -120,16 +158,66 @@ When `resource_metadata` URL is provided:
120
158
- Uses first valid server found
121
159
4.**Handle Issuer Mismatch**: Supports cases where metadata URL differs from actual issuer
122
160
123
-
## Well-Known Endpoint Discovery
161
+
## Well-Known URI Discovery (RFC 9728 & MCP Specification)
162
+
163
+
ToolHive implements the MCP specification's **Protected Resource Metadata Discovery Requirements**, which mandates trying well-known URIs when no WWW-Authenticate header is present.
164
+
165
+
### Discovery Process
166
+
167
+
**When to Trigger:**
168
+
- Server returns 401 Unauthorized
169
+
- No WWW-Authenticate header in response
170
+
- No manual `--remote-auth-issuer` configured
124
171
125
-
When no realm URL or resource metadata is provided ([`pkg/runner/remote_auth.go:175-211`](../pkg/runner/remote_auth.go#L175)):
ToolHive automatically discovers and authenticates without manual configuration
218
+
```
131
219
132
-
This approach handles cases where the OAuth provider's issuer differs from the server's public URL, such as when using CDN or worker deployments.
220
+
This approach handles cases where servers implement RFC 9728 well-known URI discovery but don't send WWW-Authenticate headers, making authentication completely automatic.
133
221
134
222
## Dynamic Client Registration Flow
135
223
@@ -325,7 +413,8 @@ The `oauth_config` section supports:
325
413
326
414
| Specification | Status | Implementation |
327
415
|--------------|--------|----------------|
328
-
| RFC 9728 (Protected Resource Metadata) | ✅ Compliant | Full implementation with validation |
0 commit comments