Skip to content

Commit 8968460

Browse files
Remove WWW-Authenticate: Bearer challenge from MCP 401 responses
The Bearer challenge (RFC 6750) triggers the MCP 2025 spec's mandatory OAuth 2.0 Protected Resource Metadata discovery flow in compliant clients such as the GitHub Copilot CLI add-server wizard. The wizard probes /.well-known/*, finds no authorization server metadata, and falls back to prompting the user for OAuth client credentials they don't have. This server uses opaque mcp_... API tokens stored in the database — not OAuth. Removing the challenge stops compliant clients from entering the OAuth flow while still returning the correct 401 status for unauthenticated requests. Update tests: AssertUnauthorizedMcpChallengeAsync now explicitly asserts that WWW-Authenticate is absent, documenting the intentional behaviour. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 21e1638 commit 8968460

2 files changed

Lines changed: 12 additions & 7 deletions

File tree

EssentialCSharp.Web.Tests/McpTests.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,10 @@ private static async Task AssertUnauthorizedMcpChallengeAsync(HttpResponseMessag
208208
{
209209
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.Unauthorized);
210210
await Assert.That(response.Headers.Location).IsNull();
211-
await Assert.That(response.Headers.TryGetValues("WWW-Authenticate", out IEnumerable<string>? values)).IsTrue();
212-
await Assert.That(values?.Any(value => value.Contains("Bearer", StringComparison.OrdinalIgnoreCase)) ?? false).IsTrue();
211+
// WWW-Authenticate: Bearer must NOT be present. Emitting a Bearer challenge triggers the
212+
// MCP 2025 spec's OAuth 2.0 Protected Resource Metadata discovery flow in compliant
213+
// clients (e.g. GitHub Copilot CLI), causing them to prompt the user for OAuth credentials
214+
// even though this server uses opaque API tokens, not OAuth.
215+
await Assert.That(response.Headers.Contains("WWW-Authenticate")).IsFalse();
213216
}
214217
}

EssentialCSharp.Web/Auth/McpApiKeyAuthenticationHandler.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ public class McpApiKeyAuthenticationHandler(
2121
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
2222
{
2323
Response.StatusCode = StatusCodes.Status401Unauthorized;
24-
if (!Response.Headers.ContainsKey("WWW-Authenticate"))
25-
{
26-
Response.Headers.Append("WWW-Authenticate", "Bearer");
27-
}
28-
24+
// Intentionally omit WWW-Authenticate: Bearer.
25+
// RFC 6750 "Bearer" challenges trigger the MCP 2025 spec's mandatory OAuth 2.0
26+
// Protected Resource Metadata discovery flow in compliant clients (e.g. GitHub
27+
// Copilot CLI). This server uses opaque mcp_... API tokens — not OAuth — so
28+
// advertising a Bearer challenge would cause clients to attempt OAuth discovery,
29+
// fail to find authorization server metadata, and prompt the user for OAuth
30+
// credentials they don't have.
2931
return Task.CompletedTask;
3032
}
3133

0 commit comments

Comments
 (0)