Skip to content

Fix OAuth metadata discovery for path-based server URLs#718

Open
roygabriel wants to merge 4 commits intomark3labs:mainfrom
roygabriel:fix/697
Open

Fix OAuth metadata discovery for path-based server URLs#718
roygabriel wants to merge 4 commits intomark3labs:mainfrom
roygabriel:fix/697

Conversation

@roygabriel
Copy link

@roygabriel roygabriel commented Feb 15, 2026

Description

MCP servers deployed at sub-paths (e.g., https://server.smithery.ai/googledrive) fail OAuth discovery because the client strips the path component from the server URL when constructing the OAuth base URL. This causes well-known metadata requests to hit wrong endpoints and DCR to fail with 404s.

Three bugs fixed:

  1. Path strippingstreamable_http.go and sse.go discard the URL path when setting the OAuth base URL, losing the sub-path entirely.
  2. Wrong well-known URL construction oauth.go appends .well-known/ to the end of the base URL instead of inserting it between host and path per RFC 8414 Section 3.
  3. No WWW-Authenticate parsing 401 handlers return OAuthAuthorizationRequiredError without parsing the resource_metadata hint from the WWW-Authenticate header per RFC 9728 Section 5.1.

Fixes #697

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • MCP spec compatibility implementation
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring (no functional changes)
  • Performance improvement
  • Tests only (no functional changes)
  • Other (please describe):

Checklist

  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the documentation accordingly

MCP Spec Compliance

Additional Information

Files changed:

File Changes
client/transport/oauth.go ProtectedResourceMetadataURL config field, buildWellKnownURL helper (RFC 8414), extractResourceMetadataURL helper (RFC 9728), SetProtectedResourceMetadataURL method, updated getServerMetadata discovery flow
client/transport/streamable_http.go Include path in base URL, parse WWW-Authenticate at 401 sites
client/transport/sse.go Include path in base URL, parse WWW-Authenticate at 401 sites
client/transport/oauth_test.go 5 new tests (12 table-driven cases)
client/transport/streamable_http_oauth_test.go 2 new tests
client/transport/sse_oauth_test.go 2 new tests

Go version: 1.25.7 linux/amd64

Verification: All existing tests pass, go vet clean, golangci-lint reports 0 issues.

Known gap: RFC 9728 Section 3.3 states that clients should validate retrieved protected resource metadata (e.g., confirming the resource field matches the server URL). This PR does not add that validation. The existing codebase did not perform it either, so this is not a regression. It could be addressed in a follow-up.

Summary by CodeRabbit

  • New Features

    • Allow explicit config and runtime discovery/storage of protected resource metadata URLs for OAuth
    • Extract metadata URLs from WWW-Authenticate challenges
    • Construct well-known discovery endpoints that respect server sub-paths
  • Bug Fixes

    • Improved discovery priority, fallback logic, and error propagation during metadata retrieval and token flows
    • OAuth flows now update metadata guidance on 401 responses
  • Tests

    • Expanded coverage for URL construction, header parsing, discovery fallbacks, and token refresh edge cases

MCP servers deployed at sub-paths (e.g. /googledrive) fail OAuth
discovery because the client strips the path when constructing the
OAuth base URL. This causes well-known metadata requests to hit
the wrong endpoints and DCR to fail with 404s.

Three fixes applied:

1. Include the URL path when setting the OAuth base URL in both
   StreamableHTTP and SSE transports (was discarding everything
   after the host).

2. Construct well-known URLs per RFC 8414 Section 3 by inserting
   the suffix between host and path instead of appending to the
   base URL (buildWellKnownURL helper).

3. Parse the resource_metadata parameter from WWW-Authenticate
   headers on 401 responses per RFC 9728 Section 5.1 and store
   it for subsequent metadata discovery.

Also adds ProtectedResourceMetadataURL to OAuthConfig for explicit
configuration and SetProtectedResourceMetadataURL on OAuthHandler
for runtime discovery.
Table-driven tests for buildWellKnownURL (various path depths,
suffixes, edge cases) and extractResourceMetadataURL (standard
headers, multiple params, empty/malformed input).

Integration tests verifying getServerMetadata succeeds with
path-based URLs, explicit ProtectedResourceMetadataURL config,
and runtime-discovered metadata URLs.

Transport-level tests confirming WWW-Authenticate parsing on 401
responses and base URL path preservation for both StreamableHTTP
and SSE transports.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 15, 2026

Walkthrough

Adds RFC 8414/9728-compliant OAuth metadata discovery: new ProtectedResourceMetadataURL config field and runtime setter, helpers to build well-known URLs and parse WWW-Authenticate resource_metadata, and discovery logic updated to prefer protected-resource metadata. SSE and StreamableHTTP extract and store PRM from 401 responses.

Changes

Cohort / File(s) Summary
Core OAuth Implementation
client/transport/oauth.go
Added ProtectedResourceMetadataURL field and SetProtectedResourceMetadataURL; implemented buildWellKnownURL and extractResourceMetadataURL; changed discovery to prefer explicit/runtime PRM, use RFC-8414 well-known URL construction, and propagate URL/build/fetch errors.
Core OAuth Tests
client/transport/oauth_test.go
Added tests for well-known URL construction, WWW-Authenticate resource_metadata parsing, protected-resource-first discovery, fallback flows, and extensive token refresh edge cases (GitHub 200-with-error, empty tokens, rotation, single-use, error handling).
SSE Transport
client/transport/sse.go
Preserve server path in OAuth baseURL; on 401, extract resource_metadata from WWW-Authenticate and call SetProtectedResourceMetadataURL before returning OAuthAuthorizationRequiredError.
SSE OAuth Tests
client/transport/sse_oauth_test.go
Added tests validating 401 WWW-Authenticate extraction of PRM URL and baseURL path preservation during SSE OAuth initialization.
StreamableHTTP Transport
client/transport/streamable_http.go
Preserve path in OAuth baseURL; on 401 responses extract resource_metadata from WWW-Authenticate and update OAuth handler via SetProtectedResourceMetadataURL.
StreamableHTTP OAuth Tests
client/transport/streamable_http_oauth_test.go
Added tests verifying 401 WWW-Authenticate extraction of PRM URL and baseURL path preservation for StreamableHTTP OAuth flows.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • Issue #697: Fixes RFC 8414/9728 discovery for servers with path components — this PR implements path-preserving well-known URL construction and PRM-first discovery.
  • Issue #688: Implements parsing resource_metadata from WWW-Authenticate and storing it on the OAuth handler for protected-resource discovery — matches this PR's runtime extraction and setter.

Possibly related PRs

  • PR #296: Extends the OAuth transport; this PR builds on that area by adding PRM discovery and URL builders.
  • PR #669: Modifies server metadata discovery ordering in oauth.go, overlapping discovery logic changes.
  • PR #661: Touches 401 handling in transports; related to this PR's WWW-Authenticate parsing and handler updates.

Suggested labels

type: bug, area: mcp spec

Suggested reviewers

  • ezynda3
  • giridhar-murthy-glean
  • pottekkat
  • rwjblue-glean
🚥 Pre-merge checks | ✅ 5 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: fixing OAuth metadata discovery for path-based server URLs, which is the primary objective of this PR.
Description check ✅ Passed The PR description comprehensively covers the three bugs being fixed, cites relevant RFCs, lists all changed files with their purposes, includes verification steps, and acknowledges a known gap per MCP spec compliance.
Linked Issues check ✅ Passed The PR fully addresses all coding objectives from issue #697: preserving path components in OAuth base URLs, constructing RFC 8414-compliant well-known URLs with proper insertion, parsing WWW-Authenticate headers for resource_metadata extraction, and implementing comprehensive test coverage with 9 new tests.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the three bugs described in issue #697: path preservation, RFC 8414/9728 compliance, and WWW-Authenticate parsing. No unrelated modifications detected.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
client/transport/oauth.go (1)

469-538: ⚠️ Potential issue | 🟠 Major

Error from failed metadata fetch leaks through successful fallback paths.

When fetchMetadataFromURL fails with a transport error (setting h.metadataFetchErr), subsequent fallback methods may succeed and set h.serverMetadata. However, when getServerMetadata completes, it checks h.metadataFetchErr at lines 541-542 and returns the stale error despite successful recovery. This occurs across multiple fallback chains (lines 475→480, 516→527, 527→533).

Clear h.metadataFetchErr before attempting each fallback to prevent accumulated errors from masking successful recovery:

Example from first fallback
 		authServerWellKnown, wkErr := buildWellKnownURL(baseURL, "oauth-authorization-server")
 		if wkErr != nil {
 			h.metadataFetchErr = fmt.Errorf("failed to build auth server URL: %w", wkErr)
 			return
 		}
+		h.metadataFetchErr = nil
 		h.fetchMetadataFromURL(ctx, authServerWellKnown)
 		if h.serverMetadata != nil {
 			return
 		}
+		h.metadataFetchErr = nil
 		metadata, defErr := h.getDefaultEndpoints(baseURL)

Apply this pattern before each fallback attempt (lines ~475, ~480, ~516, ~527, ~533).

🤖 Fix all issues with AI agents
In `@client/transport/oauth.go`:
- Around line 322-326: The field protectedResourceMetadataURL is written by
SetProtectedResourceMetadataURL and read by getServerMetadata without
synchronization, causing a data race; fix by guarding all accesses with the
existing mutex mu (or convert the field to an atomic.Value) — acquire mu.Lock()
in SetProtectedResourceMetadataURL before setting protectedResourceMetadataURL
and mu.Unlock() after, and in getServerMetadata ensure you read
protectedResourceMetadataURL while holding mu (including any read performed
inside the metadataOnce.Do closure) so both concurrent writes and reads are
synchronized; update any helper reads of that field to use the same mu to avoid
races.
🧹 Nitpick comments (3)
client/transport/sse_oauth_test.go (1)

243-280: Consider using testify/assert and testify/require per coding guidelines.

The new tests use manual t.Fatalf/t.Errorf assertions, while the coding guidelines specify using testify/assert and testify/require. Also, line 253 silently discards the error from SaveToken — use require.NoError instead.

Suggested diff
+	require.NoError(t, tokenStore.SaveToken(context.Background(), &Token{
-	_ = tokenStore.SaveToken(context.Background(), &Token{
 		AccessToken: "stale-token",
 		TokenType:   "Bearer",
 		ExpiresAt:   time.Now().Add(1 * time.Hour),
-	})
+	}))

As per coding guidelines, **/*_test.go: "Testing: use testify/assert and testify/require".

client/transport/streamable_http_oauth_test.go (1)

222-261: Same feedback as SSE test: prefer testify and don't discard SaveToken error.

Line 232 silently discards the SaveToken error. Use require.NoError to catch unexpected failures. Also consider using testify/assert and testify/require per coding guidelines.

As per coding guidelines, **/*_test.go: "Testing: use testify/assert and testify/require".

client/transport/oauth.go (1)

354-371: Simplistic WWW-Authenticate parsing may break on edge cases.

The strings.Cut-based parser works for straightforward headers but would incorrectly match resource_metadata=" if it appeared as a substring of another parameter's value (e.g., inside a quoted realm). This is unlikely in practice for OAuth/Bearer challenges, but a slightly more robust approach would match on a word boundary (e.g., ensure the character before resource_metadata is a space or comma).

For now this is acceptable given the narrow use case; flagging for awareness.

SetProtectedResourceMetadataURL can be called concurrently from
multiple 401 handlers while getServerMetadata reads the field inside
metadataOnce.Do. Protect both the write and read with the existing
mu RWMutex to prevent a data race.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
client/transport/oauth.go (1)

474-491: ⚠️ Potential issue | 🟠 Major

Stale metadataFetchErr from an intermediate fetch can shadow a successful fallback.

fetchMetadataFromURL (line 480) may set h.metadataFetchErr on a hard failure (network error, decode error). If that happens but h.serverMetadata remains nil, the code falls through to getDefaultEndpoints (line 485) which succeeds and sets h.serverMetadata. However, getServerMetadata checks h.metadataFetchErr != nil first at line 546, so the successfully-obtained metadata is never returned.

The same pattern recurs at lines 515-543 (after PRM-based auth server discovery).

🐛 Proposed fix: clear metadataFetchErr before each fallback
 	if resp.StatusCode != http.StatusOK {
 		authServerWellKnown, wkErr := buildWellKnownURL(baseURL, "oauth-authorization-server")
 		if wkErr != nil {
 			h.metadataFetchErr = fmt.Errorf("failed to build auth server URL: %w", wkErr)
 			return
 		}
 		h.fetchMetadataFromURL(ctx, authServerWellKnown)
 		if h.serverMetadata != nil {
 			return
 		}
 		// If that also fails, fall back to default endpoints
+		h.metadataFetchErr = nil // clear intermediate errors before fallback
 		metadata, defErr := h.getDefaultEndpoints(baseURL)

Apply the same pattern before each subsequent fallback step in the PRM-based discovery path (lines 526-543):

 	h.fetchMetadataFromURL(ctx, authServerWellKnown)
 	if h.serverMetadata != nil {
 		return
 	}

 	// If OAuth Authorization Server Metadata discovery fails, try OpenID Connect discovery
+	h.metadataFetchErr = nil
 	oidcWellKnown, wkErr := buildWellKnownURL(authServerURL, "openid-configuration")
 	...
 	h.fetchMetadataFromURL(ctx, oidcWellKnown)
 	if h.serverMetadata != nil {
 		return
 	}

 	// If both discovery methods fail, use default endpoints
+	h.metadataFetchErr = nil
 	metadata, defErr := h.getDefaultEndpoints(authServerURL)

Alternatively, swap the check order at line 546 so serverMetadata takes precedence:

-	if h.metadataFetchErr != nil {
-		return nil, h.metadataFetchErr
-	}
-	return h.serverMetadata, nil
+	if h.serverMetadata != nil {
+		return h.serverMetadata, nil
+	}
+	return nil, h.metadataFetchErr
🧹 Nitpick comments (1)
client/transport/oauth.go (1)

360-374: Fragile WWW-Authenticate parsing may miss valid headers.

The parsing is case-sensitive (resource_metadata="), but RFC 9728 / RFC 7235 treat auth-param names as case-insensitive. A server sending Resource_Metadata="..." or adding whitespace around = would not be matched. Consider a case-insensitive search or a small regex.

♻️ Optional: case-insensitive matching
 func extractResourceMetadataURL(wwwAuthenticate string) string {
 	if wwwAuthenticate == "" {
 		return ""
 	}
-	const param = `resource_metadata="`
-	_, after, found := strings.Cut(wwwAuthenticate, param)
+	lower := strings.ToLower(wwwAuthenticate)
+	const param = `resource_metadata="`
+	idx := strings.Index(lower, param)
+	if idx == -1 {
+		return ""
+	}
+	after := wwwAuthenticate[idx+len(param):]
-	if !found {
-		return ""
-	}
 	value, _, found := strings.Cut(after, `"`)
 	if !found {
 		return ""
 	}
 	return value
 }

RFC 7235 treats auth-param names as case-insensitive. Lower-case
the header before searching for the parameter name while preserving
original URL casing in the extracted value.
@roygabriel
Copy link
Author

Fixed in 8f11e4a. extractResourceMetadataURL now lower-cases the header before matching the param name (RFC 7235 case-insensitivity) while preserving the original URL casing in the extracted value. Added a test case for mixed-case param names.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
client/transport/oauth.go (1)

477-493: ⚠️ Potential issue | 🟡 Minor

fetchMetadataFromURL silently swallows errors when the response is non-200, but metadataFetchErr from a prior failed fetch leaks into the fallback check.

When the PRM fetch at line 468 returns non-200, control falls to line 477. fetchMetadataFromURL is called at line 482. If that also returns non-200, fetchMetadataFromURL (line 574-575) silently returns without setting serverMetadata — but it also doesn't clear metadataFetchErr. If an earlier step (e.g., the PRM request creation or the PRM HTTP call itself) had set metadataFetchErr, the check on line 483 (h.serverMetadata != nil) correctly falls through, but the stale metadataFetchErr persists. The final fallback on line 487 (getDefaultEndpoints) would succeed and set serverMetadata, and then at line 548 the non-nil metadataFetchErr from an earlier step is returned despite serverMetadata being valid.

Consider clearing h.metadataFetchErr before calling fetchMetadataFromURL on fallback paths, or restructuring so that metadataFetchErr is only authoritative when serverMetadata is nil.

Proposed fix
 		// If we can't get the protected resource metadata, try OAuth Authorization Server discovery
 		if resp.StatusCode != http.StatusOK {
 			authServerWellKnown, wkErr := buildWellKnownURL(baseURL, "oauth-authorization-server")
 			if wkErr != nil {
 				h.metadataFetchErr = fmt.Errorf("failed to build auth server URL: %w", wkErr)
 				return
 			}
+			h.metadataFetchErr = nil // Clear any earlier transient error before fallback attempt
 			h.fetchMetadataFromURL(ctx, authServerWellKnown)

And similarly before the other fetchMetadataFromURL calls (lines 523, 534). Alternatively, change the return condition:

-	if h.metadataFetchErr != nil {
+	if h.serverMetadata == nil && h.metadataFetchErr != nil {
 		return nil, h.metadataFetchErr
 	}
🤖 Fix all issues with AI agents
In `@client/transport/oauth.go`:
- Around line 517-545: The fallback chain may leave h.metadataFetchErr set from
a prior fetch attempt even after a later fetch succeeds; before each call to
fetchMetadataFromURL (the calls that use authServerWellKnown and oidcWellKnown)
clear h.metadataFetchErr (set to nil) so a previous error doesn't persist, and
also ensure when h.serverMetadata is assigned (after getDefaultEndpoints or a
successful fetch) any prior metadataFetchErr is cleared; update the logic around
fetchMetadataFromURL, buildWellKnownURL, getDefaultEndpoints, and
getServerMetadata to rely on h.serverMetadata for success and only set
metadataFetchErr when no valid metadata is available.
🧹 Nitpick comments (2)
client/transport/oauth.go (2)

360-376: extractResourceMetadataURL fails when resource_metadata is not the first parameter and earlier parameters contain quoted values with embedded " (escaped quotes).

The strings.Cut after lowercasing finds the first occurrence of resource_metadata=" in the header. If an earlier parameter's quoted value contains an escaped or nested occurrence of that substring, you'd extract the wrong value. More practically, if the parameter value itself contains a " that is \"-escaped per RFC 7230, the closing-quote strings.Cut on line 371 would terminate prematurely.

That said, real-world WWW-Authenticate headers for OAuth are simple enough that this is unlikely to be an issue in practice. Worth a comment noting the limitation.

Additionally, the function is unexported but has no unit test coverage for edge cases like multiple auth schemes (Bearer …, Basic …) or missing closing quote. The PR summary mentions tests exist — consider adding a case for a malformed/missing closing quote to ensure the empty-string fallback works.


424-546: metadataOnce.Do makes discovery non-retryable after transient failures.

This is pre-existing behavior, but the expanded fallback chain makes it more consequential: if a transient network error occurs during any step inside the sync.Once closure, metadataFetchErr is permanently set and no future call can retry. For a long-lived client, this means a single blip during startup permanently breaks OAuth. Consider using a pattern like sync.Once with a reset capability (e.g., replace with a manual sync.Mutex + done bool that can be cleared on transient errors) in a follow-up.

Comment on lines +517 to 545
// Try OAuth Authorization Server Metadata first (RFC 8414)
authServerWellKnown, wkErr := buildWellKnownURL(authServerURL, "oauth-authorization-server")
if wkErr != nil {
h.metadataFetchErr = fmt.Errorf("failed to build auth server well-known URL: %w", wkErr)
return
}
h.fetchMetadataFromURL(ctx, authServerWellKnown)
if h.serverMetadata != nil {
return
}

// If OAuth Authorization Server Metadata discovery fails, try OpenID Connect discovery
h.fetchMetadataFromURL(ctx, authServerURL+"/.well-known/openid-configuration")
oidcWellKnown, wkErr := buildWellKnownURL(authServerURL, "openid-configuration")
if wkErr != nil {
h.metadataFetchErr = fmt.Errorf("failed to build OIDC well-known URL: %w", wkErr)
return
}
h.fetchMetadataFromURL(ctx, oidcWellKnown)
if h.serverMetadata != nil {
return
}

// If both discovery methods fail, use default endpoints based on the authorization server URL
metadata, err := h.getDefaultEndpoints(authServerURL)
if err != nil {
h.metadataFetchErr = fmt.Errorf("failed to get default endpoints: %w", err)
metadata, defErr := h.getDefaultEndpoints(authServerURL)
if defErr != nil {
h.metadataFetchErr = fmt.Errorf("failed to get default endpoints: %w", defErr)
return
}
h.serverMetadata = metadata
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Same stale-error concern applies to the auth-server → OIDC → default fallback chain.

Lines 523 and 534 call fetchMetadataFromURL which may set metadataFetchErr on request-creation or HTTP errors. If the first call (RFC 8414) fails with an HTTP error that sets metadataFetchErr, and the second call (OIDC) succeeds, metadataFetchErr is never cleared, so getServerMetadata would return an error despite having valid metadata. Same pattern as noted above.

Proposed fix — clear error before each fallback attempt
 		authServerWellKnown, wkErr := buildWellKnownURL(authServerURL, "oauth-authorization-server")
 		if wkErr != nil {
 			h.metadataFetchErr = fmt.Errorf("failed to build auth server well-known URL: %w", wkErr)
 			return
 		}
+		h.metadataFetchErr = nil
 		h.fetchMetadataFromURL(ctx, authServerWellKnown)
 		if h.serverMetadata != nil {
 			return
 		}

 		// If OAuth Authorization Server Metadata discovery fails, try OpenID Connect discovery
 		oidcWellKnown, wkErr := buildWellKnownURL(authServerURL, "openid-configuration")
 		if wkErr != nil {
 			h.metadataFetchErr = fmt.Errorf("failed to build OIDC well-known URL: %w", wkErr)
 			return
 		}
+		h.metadataFetchErr = nil
 		h.fetchMetadataFromURL(ctx, oidcWellKnown)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Try OAuth Authorization Server Metadata first (RFC 8414)
authServerWellKnown, wkErr := buildWellKnownURL(authServerURL, "oauth-authorization-server")
if wkErr != nil {
h.metadataFetchErr = fmt.Errorf("failed to build auth server well-known URL: %w", wkErr)
return
}
h.fetchMetadataFromURL(ctx, authServerWellKnown)
if h.serverMetadata != nil {
return
}
// If OAuth Authorization Server Metadata discovery fails, try OpenID Connect discovery
h.fetchMetadataFromURL(ctx, authServerURL+"/.well-known/openid-configuration")
oidcWellKnown, wkErr := buildWellKnownURL(authServerURL, "openid-configuration")
if wkErr != nil {
h.metadataFetchErr = fmt.Errorf("failed to build OIDC well-known URL: %w", wkErr)
return
}
h.fetchMetadataFromURL(ctx, oidcWellKnown)
if h.serverMetadata != nil {
return
}
// If both discovery methods fail, use default endpoints based on the authorization server URL
metadata, err := h.getDefaultEndpoints(authServerURL)
if err != nil {
h.metadataFetchErr = fmt.Errorf("failed to get default endpoints: %w", err)
metadata, defErr := h.getDefaultEndpoints(authServerURL)
if defErr != nil {
h.metadataFetchErr = fmt.Errorf("failed to get default endpoints: %w", defErr)
return
}
h.serverMetadata = metadata
// Try OAuth Authorization Server Metadata first (RFC 8414)
authServerWellKnown, wkErr := buildWellKnownURL(authServerURL, "oauth-authorization-server")
if wkErr != nil {
h.metadataFetchErr = fmt.Errorf("failed to build auth server well-known URL: %w", wkErr)
return
}
h.metadataFetchErr = nil
h.fetchMetadataFromURL(ctx, authServerWellKnown)
if h.serverMetadata != nil {
return
}
// If OAuth Authorization Server Metadata discovery fails, try OpenID Connect discovery
oidcWellKnown, wkErr := buildWellKnownURL(authServerURL, "openid-configuration")
if wkErr != nil {
h.metadataFetchErr = fmt.Errorf("failed to build OIDC well-known URL: %w", wkErr)
return
}
h.metadataFetchErr = nil
h.fetchMetadataFromURL(ctx, oidcWellKnown)
if h.serverMetadata != nil {
return
}
// If both discovery methods fail, use default endpoints based on the authorization server URL
metadata, defErr := h.getDefaultEndpoints(authServerURL)
if defErr != nil {
h.metadataFetchErr = fmt.Errorf("failed to get default endpoints: %w", defErr)
return
}
h.serverMetadata = metadata
🤖 Prompt for AI Agents
In `@client/transport/oauth.go` around lines 517 - 545, The fallback chain may
leave h.metadataFetchErr set from a prior fetch attempt even after a later fetch
succeeds; before each call to fetchMetadataFromURL (the calls that use
authServerWellKnown and oidcWellKnown) clear h.metadataFetchErr (set to nil) so
a previous error doesn't persist, and also ensure when h.serverMetadata is
assigned (after getDefaultEndpoints or a successful fetch) any prior
metadataFetchErr is cleared; update the logic around fetchMetadataFromURL,
buildWellKnownURL, getDefaultEndpoints, and getServerMetadata to rely on
h.serverMetadata for success and only set metadataFetchErr when no valid
metadata is available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OAuth metadata discovery fails for servers with path components (RFC 8414/9728 compliance)

1 participant