Skip to content

MCP Server OAuth Discovery doesn't work. #5669

@gazzadownunder

Description

@gazzadownunder

Follow up issue for:

MCP auth doesn't work with Keycloak. #4611 and merge #5458

Describe the bug

The Goose Client doesn't follow the correct OAuth Discovery process flow correctly.

PR #511 (merged Nov 5, 2025) DOES implement RFC 9728 (Protected Resource Metadata Discovery) in the rmcp library. The PR:

  1. ✅ Adds discover_resource_metadata_url() method
  2. ✅ Implements two-step discovery:
    • First: Queries /.well-known/oauth-protected-resource on MCP server
    • Then: Extracts authorization_servers array
    • Finally: Queries authorization server metadata
  3. ✅ Includes fallback discovery paths
  4. ✅ Targets version 0.8.5

Current flow

The rmcp library's OAuthState::new():

  • ✅ Receives the 401 response (handled by transport layer)
  • SKIPS Step 2 (protected resource metadata)
  • ❌ Directly queries {mcp_server_url}/.well-known/oauth-authorization-server (Step 3, but at wrong URL)
  • ❌ Assumes MCP server IS the authorization server

The Problem

Version 0.8.5 is NOT yet published to crates.io!

Conclusion

  1. ❌ The fix is NOT yet published to crates.io as version 0.8.5

  2. ❌ Goose is using the older 0.8.3 which lacks RFC 9728 support

  3. ❌ OAuth code might require updating to point to the authorisation server, rather than the MCP server

  4. ✅ Once rmcp 0.8.5 is published, Goose can update
    Current Operation:

  5. Client tries to connect → gets 401 ✅

  6. rmcp 0.8.3 doesn't use protected-resource metadata ✅

  7. Tries /.well-known/oauth-authorization-server on MCP server ✅

  8. Fails because it doesn't have authorization_servers ✅

MCP Specification Compliance Analysis

Overview

This appendix compares Goose's OAuth implementation with the MCP OAuth 2.0 Authorization Specification to identify compliance gaps and differences.

Summary of Findings (Updated 2025-11-11)

Status: ❌ Non-Compliant - OAuth discovery does not follow MCP specification

Goose's OAuth implementation has two separate flows:

  1. MCP servers (mod.rs) - Uses rmcp library for OAuth
  2. Databricks (providers/oauth.rs) - Custom Databricks-specific implementation

Critical Issue: The MCP OAuth flow (via rmcp library v0.8.3) is NOT compliant with the MCP OAuth specification. It implements RFC 8414 (Authorization Server Metadata) but does NOT implement RFC 9728 (Protected Resource Metadata), which is required by the MCP specification.

Detailed Comparison

1. OAuth Discovery Endpoints (MCP Servers)

Aspect MCP Specification rmcp Library (v0.8.3) Goose/rmcp Implementation Compliant?
Protected Resource Discovery /.well-known/oauth-protected-resource (RFC 9728) ❌ Not implemented ❌ Skipped (mod.rs:76) NO
Authorization Server Discovery /.well-known/oauth-authorization-server (RFC 8414) ✅ Implemented ✅ Queries {mcp_server_url}/.well-known/oauth-authorization-server ⚠️ PARTIAL
Discovery sequence Two-step: resource → auth server Single-step: auth server only Single-step: queries MCP server directly NO
WWW-Authenticate header parsing Extract protected resource metadata URL ✅ Detects 401 ❌ Ignores metadata URL NO

Analysis:

The MCP specification requires a two-step discovery process:

  1. Step 1: Client connects to MCP server → receives 401 Unauthorized with WWW-Authenticate header

    HTTP/1.1 401 Unauthorized
    WWW-Authenticate: Bearer realm="MCP Server",
      oauth_protected_resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
  2. Step 2: Client requests protected resource metadata from MCP server

    GET https://mcp.example.com/.well-known/oauth-protected-resource
    
    Response:
    {
      "resource": "https://mcp.example.com",
      "authorization_servers": ["https://auth.example.com"]
    }
  3. Step 3: Client requests authorization server metadata from the authorization server (NOT the MCP server)

    GET https://auth.example.com/.well-known/oauth-authorization-server
    
    Response:
    {
      "authorization_endpoint": "https://auth.example.com/authorize",
      "token_endpoint": "https://auth.example.com/token",
      ...
    }

What rmcp/Goose Actually Does (mod.rs:76):

let mut oauth_state = OAuthState::new(mcp_server_url, None).await?;

@zhaohuabing @romixch @botengyao @gazzadownunder @alexhancock

Metadata

Metadata

Assignees

Labels

mcpMCP/Extension relatedp2Priority 2 - Medium

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions