-
Notifications
You must be signed in to change notification settings - Fork 116
Fix remote MCP server authentication with issuer mismatch #1980
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
This fixes authentication failures when remote MCP servers return a different issuer URL than their public-facing URL in the OIDC discovery response. The fix adds a new priority level to the issuer discovery algorithm, placing well-known endpoint discovery before URL derivation. This allows accepting the authoritative issuer from the .well-known/openid-configuration endpoint even when it differs from the server URL, complying with RFC 8414. New discovery priority chain: 1. Configured issuer (if provided) 2. Realm-derived issuer (RFC 8414) 3. Resource metadata discovery (RFC 9728) 4. Well-known endpoint discovery (NEW - accepts actual issuer) 5. URL-derived issuer (fallback) Changes: - Add tryDiscoverFromWellKnown to attempt discovery before URL derivation - Accept the actual issuer from ValidateAndDiscoverAuthServer - Preserve HTTP scheme for localhost URLs in DeriveIssuerFromURL - Add path normalization to DeriveIssuerFromRealm for security - Add comprehensive test coverage for discovery priority chain - Fix linting issues with constants and parallel test execution The fix maintains backward compatibility while supporting real-world OAuth deployments where the issuer differs from the server endpoint. Fixes #1957
This adds detailed documentation about how ToolHive handles remote MCP server authentication, including compliance with various RFCs and the MCP authorization specification. The documentation covers: - Specification compliance (RFC 9728, RFC 8414, RFC 7591) - Authentication flow and discovery priority chain - Well-known endpoint discovery for issuer mismatch handling - Dynamic client registration - Security features and configuration options - Implementation details and key components This documentation helps developers understand the authentication architecture and serves as a reference for the implementation that was fixed in PR #1980.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1980 +/- ##
==========================================
+ Coverage 47.31% 47.53% +0.22%
==========================================
Files 233 233
Lines 28649 28691 +42
==========================================
+ Hits 13555 13638 +83
+ Misses 14083 14038 -45
- Partials 1011 1015 +4 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes OAuth authentication failures with remote MCP servers where the server's OIDC discovery response returns a different issuer URL than the public-facing server URL. The solution adds well-known endpoint discovery as a priority step before URL derivation, allowing acceptance of the authoritative issuer from the .well-known/openid-configuration
endpoint.
Key changes:
- Added
tryDiscoverFromWellKnown
function for OAuth issuer discovery without strict validation - Modified discovery priority chain to check well-known endpoints before URL derivation fallback
- Enhanced localhost HTTP scheme preservation for testing scenarios
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
File | Description |
---|---|
pkg/runner/remote_auth.go | Implements new well-known discovery step in OAuth issuer discovery chain |
pkg/runner/remote_auth_test.go | Comprehensive test suite for OAuth discovery priority chain and security scenarios |
pkg/runner/remote_auth_test_helpers_test.go | Test helper functions for mock server setup and test case processing |
pkg/runner/config_test.go | Replaces string literals with localhostStr constant for consistency |
pkg/auth/discovery/discovery.go | Adds localhost HTTP scheme preservation and path normalization security improvements |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
if err == nil { | ||
return issuer, scopes, authServerInfo, nil | ||
} | ||
logger.Infof("DEBUG: Could not discover from well-known endpoint: %v", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debug messages should use logger.Debugf()
instead of logger.Infof()
with "DEBUG:" prefix. This follows proper logging level conventions where debug information should be logged at debug level, not info level.
logger.Infof("DEBUG: Could not discover from well-known endpoint: %v", err) | |
logger.Debugf("Could not discover from well-known endpoint: %v", err) |
Copilot uses AI. Check for mistakes.
// This uses DiscoverActualIssuer which doesn't validate issuer match | ||
authServerInfo, err := discovery.ValidateAndDiscoverAuthServer(ctx, derivedURL) | ||
if err != nil { | ||
logger.Infof("DEBUG: ValidateAndDiscoverAuthServer failed for %s: %v", derivedURL, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debug messages should use logger.Debugf()
instead of logger.Infof()
with "DEBUG:" prefix. This follows proper logging level conventions where debug information should be logged at debug level, not info level.
logger.Infof("DEBUG: ValidateAndDiscoverAuthServer failed for %s: %v", derivedURL, err) | |
logger.Debugf("ValidateAndDiscoverAuthServer failed for %s: %v", derivedURL, err) |
Copilot uses AI. Check for mistakes.
Summary
This PR fixes issue #1957 where authentication with remote MCP servers was failing when the server returns a different issuer URL than its public-facing URL in the OIDC discovery response.
Problem
Some OAuth providers (like those using Cloudflare Workers) return an issuer URL in their
.well-known/openid-configuration
that differs from their public-facing server URL. ToolHive was previously deriving the issuer from the server URL and strictly validating against it, causing authentication failures.Solution
The fix adds a new priority level to the issuer discovery algorithm, placing well-known endpoint discovery before URL derivation. This allows accepting the authoritative issuer from the
.well-known/openid-configuration
endpoint even when it differs from the server URL, complying with RFC 8414 which states that the issuer in the metadata is authoritative.New Discovery Priority Chain
Changes
tryDiscoverFromWellKnown
function to attempt discovery before URL derivationValidateAndDiscoverAuthServer
DeriveIssuerFromURL
to preserve HTTP scheme for localhost URLs (supports testing)DeriveIssuerFromRealm
for securityTesting
Compatibility
The fix maintains backward compatibility while supporting real-world OAuth deployments where the issuer differs from the server endpoint.
Fixes #1957