Skip to content

OAuth Protected Resource Metadata Discovery Not Following RFC 9728 §3.1 #15754

Description

@vrv

What happened?

When connecting to an MCP server supporting the oauth-protected-resource protocol and has a path component in its URL, gemini-cli fails to authenticate with an error like:

✕ Failed to authenticate with MCP server: Protected resource https://example.com does not match expected https://example.com/path/to/mcp

Root Cause

Per RFC 9728 §3.1:
The protected resource metadata for a resource is obtained by sending an HTTP GET request to a path formed by inserting a well-known URI string into the resource URL between the host and path components [...] If the resource identifier value contains a path component, any terminating "/" character is removed and the remaining path is appended to the well-known URI.

E.g., for a resource at https://example.com/api/resource, the correct well-known URL is:

https://example.com/.well-known/oauth-protected-resource/api/resource

Not the root:

https://example.com/.well-known/oauth-protected-resource

This is to support multi-tenant resource configurations.

Current Behavior

gemini-cli currently tries root-based discovery first, then falls back to path-based discovery:

  1. Client requests https://example.com/.well-known/oauth-protected-resource (root)
  2. RFC 9728-compliant server responds with {"resource": "https://example.com", ...}
  3. Client tries to validate that resource matches the original URL (https://example.com/api/resource)
  4. Validation fails: "https://example.com does not match https://example.com/api/resource"

I believe this is not a bug in the server. Per RFC 9728, the server at the root well-known endpoint correctly returns the root resource.

I believe the bug is that gemini-cli should not be requesting the root endpoint for a resource with a path, at least as the first option.

What did you expect to happen?

Expected Behavior

gemini-cli should try path-based discovery first (RFC 9728 compliant), then optionally fall back to root-based discovery for backwards compatibility with non-compliant servers.

Reference Implementation

The official MCP TypeScript SDK uses path-based discovery by default with no root fallback:

// https://github.com/modelcontextprotocol/typescript-sdk/blob/6b4d99f10b975d65392bb777cc8cb1151c20c972/packages/server/src/server/auth/router.ts#L242
export function getOAuthProtectedResourceMetadataUrl(serverUrl: URL): string {
const u = new URL(serverUrl.href);
const rsPath = u.pathname && u.pathname !== '/' ? u.pathname : '';
return new URL(/.well-known/oauth-protected-resource${rsPath}, u).href;
}

Affected Code

packages/core/src/mcp/oauth-utils.ts: buildWellKnownUrls() and discoverOAuthConfig()

Client information

Client Information

Run gemini to enter the interactive CLI, then run the /about command.

> /about
CLI Version                                               0.23.0-preview.3                                                                                            │
│ Git Commit                                                703d2e0dc                                                                                                   │
│ Model                                                     auto-gemini-3                                                                                               │
│ Sandbox                                                   no sandbox                                                                                                  │
│ OS                                                        darwin                                                                                                      │
│ Auth Method                                               OAuth                                                                                                       │
│ User Email                                                vrv@d8e.ai```

</details>

### Login information

_No response_

### Anything else we need to know?

I've implemented a fix which I will link, which switches the order in which these paths are tried, starting with path-based first and then falling back to the root.  I don't believe the latter is necessary according to the spec, but leaving it in unless you tell me not to, since I assume some servers might be doing the wrong thing and I don't want to break them :)

Thanks!

Metadata

Metadata

Assignees

Labels

area/securityIssues related to securitypriority/p1Important and should be addressed in the near term.🔒 maintainer only⛔ Do not contribute. Internal roadmap item.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions