Skip to content

fix(mcp): skip OAuth discovery when static Authorization header is configured#1357

Merged
Aaronontheweb merged 6 commits into
devfrom
fix/mcp-static-auth-overridden-by-oauth-probe
Jun 8, 2026
Merged

fix(mcp): skip OAuth discovery when static Authorization header is configured#1357
Aaronontheweb merged 6 commits into
devfrom
fix/mcp-static-auth-overridden-by-oauth-probe

Conversation

@Aaronontheweb

Copy link
Copy Markdown
Collaborator

Problem

When an operator configures a static Authorization header on an HTTP MCP server via netclaw mcp add --header "Authorization: Bearer ...", the daemon blocks the connection entirely with an "Awaiting Auth" status if the server responds to the OAuth discovery probe with a 401 + WWW-Authenticate header containing resource_metadata=.

This means users who want to authenticate via a static bearer token are forced into an interactive OAuth flow that they did not request and may not be able to complete.

Root Cause

In McpClientManager.CreateClientAsync() (lines 513-528), the code only checked for cached OAuth tokens (_oauthService.GetTokenSet(name)) before running the OAuth discovery probe. It did not check whether the user had configured a static Authorization header on the server entry (entry.Headers).

If OAuth metadata was found, the daemon returned null and set status to AwaitingAuth — never reaching CreateTransport() where the configured headers would be sent.

Fix

Added a check for existing Authorization headers in entry.Headers before running the OAuth discovery probe:

var hasTokens = _oauthService.GetTokenSet(name) is not null;
var hasStaticHeaders = entry.Headers?.Any(h => 
    h.Key.Equals("Authorization", StringComparison.OrdinalIgnoreCase)) == true;

// Skip OAuth discovery if the user has already configured static auth headers.
if (!hasTokens && !hasStaticHeaders)
{
    var metadata = await _oauthService.TryDiscoverMetadataAsync(name, entry.Url, ct);
    // ... existing OAuth blocking logic
}

If static auth headers are configured, discovery is skipped and the connection proceeds normally with the configured bearer token sent through to the server.

Testing

Added McpOAuthHeaderConflictTests.cs with 3 regression tests:

  1. StaticAuthHeader_WhenServerReturnsOAuthMetadata_StillConnects — the bug reproduction. Without the fix, this fails with client == null because OAuth blocking aborts the connection even though a static header is configured.
  2. StaticAuthHeader_WhenServerReturnsNoOAuthMetadata_ConnectsNormally — positive control validating the test harness works.
  3. CachedOAuthTokens_AreCheckedBeforeOAuthDiscovery — validates pre-existing cached tokens are still respected.

All 57 MCP tests pass with zero regressions.

Fixes #1350

…nfigured

When an operator configures a static Authorization header on an HTTP
MCP server via --header, the daemon was blocking the connection entirely
with an 'Awaiting Auth' status if the server responded to the OAuth
discovery probe with a 401 + WWW-Authenticate containing resource_metadata=
(GitHub issue #1350).

This happened because CreateClientAsync only checked for cached OAuth
tokens before running the discovery probe, ignoring whether the user
had already configured static auth headers.

Fix: skip OAuth discovery if the server entry has any Authorization
header configured. The static header will be sent through to the server
as normal.

Also added:
- McpOAuthHeaderConflictTests.cs: 3 regression tests reproducing the bug
  and verifying the fix (one would fail without the fix)
- CachedOAuthTokens_AreCheckedBeforeOAuthDiscovery: validates that
  pre-existing cached tokens are still respected

Fixes #1350
Comment thread src/Netclaw.Daemon.Tests/Mcp/McpOAuthHeaderConflictTests.cs Fixed
Comment thread src/Netclaw.Daemon.Tests/Mcp/McpOAuthHeaderConflictTests.cs Fixed
Comment thread src/Netclaw.Daemon.Tests/Mcp/McpOAuthHeaderConflictTests.cs Fixed
@Aaronontheweb Aaronontheweb added bug Something isn't working mcp Model context protocol server / client issues. tests All issues related to testing, quality assurance, and smoke testing. labels Jun 8, 2026
Extract shared setup into CreateManager helper, use `using var` for
manager disposal, reduce CTS timeout from 2 min to 15 sec, and add
Assert.NotNull guard on the reflected _tokens field.
Broaden the check from Authorization-only to any configured headers.
The OAuth probe doesn't send user-configured headers, so its 401 is
misleading for servers that use X-API-Key, custom tokens, or any
non-Authorization auth mechanism. See #1350.

Added NonAuthorizationHeader_WhenServerReturnsOAuthMetadata_StillConnects
test covering the X-API-Key case.
Instead of skipping the probe when headers exist, always run it so
metadata is cached for the runtime fallback in BuildConnectionFailureStatus.
Only block with AwaitingAuth when the user has no configured headers —
if headers are present, let the real connection attempt (which carries
the user's credentials) decide whether auth succeeds or fails.

This avoids guessing which headers are auth-related and ensures the
runtime fallback correctly identifies OAuth-required servers even when
the user's credentials turn out to be wrong.
… probe test

Add --require-auth flag to the smoke MCP server: unauthenticated
requests get 401 + WWW-Authenticate with resource_metadata pointing
back to the server's own OAuth discovery endpoints. The server serves
the full metadata chain (resource-metadata, well-known/oauth-authorization-server)
so the OAuth probe resolves without hitting an external provider.

The e2e test ConfiguredHeader_WhenOAuthProbeReturnsMetadata_StillReachesServer
now uses this mode — both the OAuth probe and the MCP transport hit the
same real server with no fakes. Proves the static Authorization header
reaches the server even when the probe discovers OAuth metadata.

Reverted the oauthHttpOverride on McpSmokeHarness (no longer needed).
@Aaronontheweb Aaronontheweb marked this pull request as ready for review June 8, 2026 15:48

@Aaronontheweb Aaronontheweb left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Aaronontheweb Aaronontheweb merged commit ba3d0ca into dev Jun 8, 2026
21 checks passed
@Aaronontheweb Aaronontheweb deleted the fix/mcp-static-auth-overridden-by-oauth-probe branch June 8, 2026 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working mcp Model context protocol server / client issues. tests All issues related to testing, quality assurance, and smoke testing.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP server with static bearer token blocked by OAuth discovery probe

1 participant