Skip to content

Conversation

@dask-58
Copy link
Contributor

@dask-58 dask-58 commented Dec 20, 2025

Description

This PR fixes issues with OAuth providers that utilize a split-domain architecture (where the API domain differs from the Authorization domain) and do not support Dynamic Client Registration, as of now, for GitHub.

The Problem:
Currently, the library relies on standard discovery mechanisms that fail for GitHub:

  1. RFC 7591 (Dynamic Registration): When no Client ID is found, the library attempts to dynamically register with the server. GitHub returns 404 Not Found as it requires manual app creation.
  2. Metadata Discovery: The library attempts to fetch OAuth metadata from the Resource Server (e.g., api.githubcopilot.com/.well-known/...). Since GitHub's Authorization Server is hosted on github.com, this discovery fails, causing the library to incorrectly guess the auth URL as https://api.githubcopilot.com/authorize (which is invalid).

The Solution:
I have introduced a Provider Preset pattern to transport/oauth.go:

  • Added a OAuthProvider struct to define known endpoint configurations and capabilities (e.g., SupportsDynamicRegistration).
  • Added client.GitHubProvider as a built-in preset.
  • Updated getServerMetadata to bypass HTTP discovery and use the preset endpoints immediately if configured.
  • Updated RegisterClient to fail gracefully if the provider declares that dynamic registration is unsupported.
  • Updated examples/oauth_client/main.go to demonstrate a working connection to the GitHub MCP server.
  • Updated examples/oauth_client/README.md for documentation.

Fixes #643

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 (Verified via examples/oauth_client with valid GitHub credentials)
  • I have updated the documentation accordingly (Updated example code)

Additional Information

Tested locally using a registered GitHub OAuth App. The fix correctly redirects to github.com/login/oauth/authorize instead of the broken api.githubcopilot.com URL, and successfully completes the token exchange.

Summary by CodeRabbit

  • New Features

    • Add explicit OAuth provider option so integrations can specify a provider instead of relying on discovery.
    • Include a GitHub provider preset and enforce provider capabilities (e.g., dynamic registration) when configured.
  • Documentation

    • Update OAuth client example and README to demonstrate GitHub-specific setup, scopes, and callback guidance.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 20, 2025

Walkthrough

Adds an explicit OAuth provider model and GitHub preset, re-exports provider aliases at the client level, extends OAuthConfig with a Provider field, changes discovery and registration logic to honor configured providers, and updates the OAuth example and README for GitHub usage.

Changes

Cohort / File(s) Summary
OAuth provider model
client/transport/oauth_provider.go
Adds transport.OAuthProvider type with AuthorizationEndpoint, TokenEndpoint, and SupportsDynamicRegistration fields; adds transport.GitHubProvider preset configured for GitHub endpoints with dynamic registration disabled.
OAuth config & logic
client/transport/oauth.go
Adds Provider *OAuthProvider to OAuthConfig; getServerMetadata uses configured Provider to populate metadata (bypassing RFC 8414 discovery); RegisterClient rejects dynamic registration if Provider exists and SupportsDynamicRegistration is false.
Client re-exports
client/oauth.go
Adds public aliases: type OAuthProvider = transport.OAuthProvider and var GitHubProvider = transport.GitHubProvider to re-export transport-level provider constructs.
Examples & docs
examples/oauth_client/main.go, examples/oauth_client/README.md
Example changed to use client.GitHubProvider, scopes updated to read:user, PKCE disabled, and README rewritten for GitHub-specific flow and configuration notes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review conditional discovery in getServerMetadata and its interactions with existing discovery paths.
  • Verify RegisterClient guard correctly enforces SupportsDynamicRegistration.
  • Confirm GitHubProvider endpoints and example changes align with GitHub OAuth behavior.

Possibly related PRs

Suggested labels

type: bug

Suggested reviewers

  • ezynda3
  • giridhar-murthy-glean
  • pottekkat
  • rwjblue-glean

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: Add OAuth Provider Presets to support GitHub OAuth' accurately describes the main change: introducing OAuth provider presets with GitHub as the initial implementation.
Description check ✅ Passed The PR description comprehensively covers all required template sections: problem statement, solution, type of change selection, checklist completion, and verification details.
Linked Issues check ✅ Passed The PR addresses all core requirements from issue #643: it fixes GitHub OAuth connectivity by introducing provider presets, bypassing broken discovery mechanisms, preventing invalid dynamic registration attempts, and updating example code with working configuration.
Out of Scope Changes check ✅ Passed All changes are directly scoped to resolving the GitHub OAuth connectivity issue: provider abstraction, preset configuration, discovery bypass logic, registration guards, and documentation/example updates.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a2de4e and caf1c7e.

📒 Files selected for processing (1)
  • examples/oauth_client/README.md (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (6)
examples/oauth_client/README.md (6)

13-19: Clear explanation of provider configuration strategy.

The new "Standard vs Manual Provider Configuration" section effectively communicates when and why to use preset providers (e.g., for split-domain OAuth architectures) versus automatic discovery. This aligns well with the PR's objective to support providers like GitHub that don't follow standard RFC 8414 metadata discovery patterns.


3-3: Documentation accurately reflects GitHub-specific preset support.

The introduction and features section clearly establish that this example now targets GitHub OAuth specifically, with reference to the client.GitHubProvider preset. This is consistent with the PR objective.

Also applies to: 7-8


36-36: PKCE statement corrected from previous review.

The statement now accurately reflects that GitHub OAuth Apps support PKCE optionally, and that it is disabled "for simplicity" rather than because it's unsupported. This addresses the previous review comment and correctly documents the client's configuration.


50-59: Verify serverURL format and callback configuration.

Line 53 specifies the serverURL as https://api.githubcopilot.com/v1/mcp, which differs from the standard format seen in typical MCP examples. Line 59 correctly instructs the user to configure the GitHub OAuth App callback URL as http://localhost:8085/oauth/callback.

Please confirm that:

  • The serverURL endpoint format (with /v1/mcp path) is correct for the GitHub MCP server.
  • The callback URL matches the one hardcoded in main.go (if any).

63-65: Scopes documented as required by GitHub.

The OAuth Scopes section now documents read:user as the scope required by GitHub. Please verify that this scope is:

  • Actually requested in the main.go example's OAuthConfig.
  • Sufficient for the GitHub MCP server integration being demonstrated.

34-34: GitHub authorization flow documentation is accurate and consistent with implementation.

The README correctly documents the main.go example:

  • Uses client.GitHubProvider as stated on line 34
  • Implements read:user scope as documented on line 35
  • Disables PKCE matching the actual code configuration
  • References the correct GitHub authorization endpoint
  • Callback handling aligns with implementation details

All verification points confirmed.


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

🧹 Nitpick comments (6)
examples/oauth_client/main.go (3)

35-35: Clarify PKCE comment accuracy.

The comment states "Disable PKCE for GitHub (not supported)" but GitHub does support PKCE—it's optional, not unsupported. Consider updating the comment to reflect that PKCE is disabled for simplicity or that it's optional for GitHub OAuth Apps, rather than implying it's unsupported.

🔎 Suggested comment clarification
-		PKCEEnabled:  false, // Disable PKCE for GitHub (not supported)
+		PKCEEnabled:  false, // PKCE is optional for GitHub OAuth Apps

18-18: Consider aligning serverURL with GitHub endpoint.

The README shows serverURL = "https://api.githubcopilot.com/v1/mcp" but this file still uses the generic "https://api.example.com/v1/mcp". Since this example is now GitHub-specific (with GitHubProvider configured), aligning the serverURL with the documented GitHub endpoint would make the example more immediately useful.

🔎 Suggested alignment
-	serverURL = "https://api.example.com/v1/mcp"
+	serverURL = "https://api.githubcopilot.com/mcp/"

120-125: Consider conditional PKCE generation.

PKCE code verifier and challenge are generated even though PKCEEnabled is set to false on Line 35. While this doesn't cause errors (the values are simply ignored), it's unnecessary computation. Consider generating these values only when PKCE is enabled.

🔎 Suggested conditional generation
-		// Generate PKCE code verifier and challenge
-		codeVerifier, err := client.GenerateCodeVerifier()
-		if err != nil {
-			log.Fatalf("Failed to generate code verifier: %v", err)
-		}
-		codeChallenge := client.GenerateCodeChallenge(codeVerifier)
+		// Generate PKCE code verifier and challenge if enabled
+		var codeVerifier, codeChallenge string
+		// Note: You'd need to check if PKCE is enabled in the handler
+		// This is a simplified example - in practice, the handler would know its config
+		codeVerifier, err := client.GenerateCodeVerifier()
+		if err != nil {
+			log.Fatalf("Failed to generate code verifier: %v", err)
+		}
+		codeChallenge = client.GenerateCodeChallenge(codeVerifier)

Note: Since the example doesn't expose PKCEEnabled from the handler, this refactor may require additional changes to check the configuration state.

client/transport/oauth.go (1)

560-563: Consider enhancing error message with actionable guidance.

The error message correctly prevents dynamic registration when the provider doesn't support it. Consider making it more helpful by suggesting what the user should do instead (e.g., manually register an OAuth app with the provider).

🔎 Suggested error message enhancement
-		return errors.New("dynamic client registration is not supported by this provider")
+		return errors.New("dynamic client registration is not supported by this provider; please manually register an OAuth application and provide ClientID/ClientSecret")
client/transport/oauth_provider.go (2)

13-13: Minor wording suggestion in comment.

The comment says "uses split domains for API and generic OAuth" which is slightly unclear. Consider "uses split domains for API and OAuth authorization" or "uses split domains (API domain ≠ authorization domain)" for clarity.

🔎 Suggested comment clarification
-// GitHub does not support RFC 7591 dynamic registration and uses split domains for API and generic OAuth.
+// GitHub does not support RFC 7591 dynamic registration and uses split domains for API and OAuth authorization.

14-18: Consider protecting GitHubProvider from accidental modification.

GitHubProvider is declared as a package-level var, making it mutable. If user code accidentally modifies its fields, it would affect all subsequent users of the preset. Consider one of these approaches:

  1. Document that the preset should not be modified
  2. Provide a function that returns a new instance each time
  3. Use a private variable and expose it through a getter function
🔎 Suggested protection using a getter function
-var GitHubProvider = &OAuthProvider{
+var githubProvider = &OAuthProvider{
 	AuthorizationEndpoint:       "https://github.com/login/oauth/authorize",
 	TokenEndpoint:               "https://github.com/login/oauth/access_token",
 	SupportsDynamicRegistration: false,
 }
+
+// GitHubProvider returns the preset configuration for GitHub OAuth.
+// It returns a pointer to a shared instance; do not modify the returned value.
+func GitHubProvider() *OAuthProvider {
+	return githubProvider
+}

Alternatively, return a new copy each time:

-var GitHubProvider = &OAuthProvider{
-	AuthorizationEndpoint:       "https://github.com/login/oauth/authorize",
-	TokenEndpoint:               "https://github.com/login/oauth/access_token",
-	SupportsDynamicRegistration: false,
-}
+// GitHubProvider returns the preset configuration for GitHub OAuth.
+func GitHubProvider() *OAuthProvider {
+	return &OAuthProvider{
+		AuthorizationEndpoint:       "https://github.com/login/oauth/authorize",
+		TokenEndpoint:               "https://github.com/login/oauth/access_token",
+		SupportsDynamicRegistration: false,
+	}
+}

Note: This would require updating the re-export in client/oauth.go and the example usage.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a429ab3 and 0a2de4e.

📒 Files selected for processing (5)
  • client/oauth.go (1 hunks)
  • client/transport/oauth.go (3 hunks)
  • client/transport/oauth_provider.go (1 hunks)
  • examples/oauth_client/README.md (3 hunks)
  • examples/oauth_client/main.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Order imports: standard library first, then third-party, then local packages (goimports enforces this)
Follow Go naming conventions: exported identifiers in PascalCase; unexported in camelCase; acronyms uppercase (HTTP, JSON, MCP)
Error handling: return sentinel errors, wrap with fmt.Errorf("context: %w", err), and check with errors.Is/As
Prefer explicit types and strongly-typed structs; avoid using any except where protocol flexibility is required (e.g., Arguments any)
All exported types and functions must have GoDoc comments starting with the identifier name; avoid inline comments unless necessary
Functions that are handlers or long-running must accept context.Context as the first parameter
Ensure thread safety for shared state using sync.Mutex and document thread-safety requirements in comments
For JSON: use json struct tags with omitempty for optional fields; use json.RawMessage for flexible/deferred parsing

Files:

  • client/transport/oauth_provider.go
  • client/transport/oauth.go
  • client/oauth.go
  • examples/oauth_client/main.go
🧬 Code graph analysis (4)
client/transport/oauth_provider.go (1)
client/oauth.go (1)
  • OAuthProvider (26-26)
client/transport/oauth.go (2)
client/oauth.go (1)
  • OAuthProvider (26-26)
client/transport/oauth_provider.go (1)
  • OAuthProvider (6-10)
client/oauth.go (1)
client/transport/oauth_provider.go (2)
  • OAuthProvider (6-10)
  • GitHubProvider (14-18)
examples/oauth_client/main.go (3)
client/oauth.go (2)
  • TokenStore (17-17)
  • GitHubProvider (29-29)
client/transport/oauth.go (1)
  • TokenStore (54-65)
client/transport/oauth_provider.go (1)
  • GitHubProvider (14-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (6)
client/oauth.go (1)

25-29: LGTM! Clean re-exports.

The OAuthProvider type alias and GitHubProvider variable re-exports follow Go conventions and provide convenient access to transport package entities. GoDoc comments properly start with the identifier names as required by the coding guidelines.

client/transport/oauth.go (2)

40-42: LGTM! Provider field is well-documented.

The optional Provider field is properly documented and uses a pointer type to allow nil for the default discovery behavior. This follows Go best practices for optional configuration.


364-373: Reconsider setting Issuer to the API domain when tokens may validate issuer claims.

Per OpenID Connect standards, the issuer identifier MUST exactly match the iss claim value in the token. Setting Issuer to h.baseURL (the API domain like https://api.githubcopilot.com) creates a mismatch if tokens are issued with the actual OAuth issuer (https://github.com).

This matters because issuer validation occurs after signature verification, where the iss claim is compared to the issuer property from discovery. If this AuthServerMetadata.Issuer field is used downstream for token validation or if it's exposed in discovery documents, it could cause legitimate tokens to be rejected during validation.

The comment notes RFC 8414 issuers don't match API domains for split-domain providers like GitHub—which is true. However, the solution should preserve the correct OAuth issuer value. Consider:

  • Setting Issuer to the actual OAuth authority (https://github.com for GitHub)
  • Documenting why the endpoints differ from the issuer
  • Verifying that token validation code uses the actual token's iss claim rather than relying on this metadata field
client/transport/oauth_provider.go (1)

3-10: LGTM! Well-designed provider configuration type.

The OAuthProvider struct is clean, well-documented, and appropriately scoped. The GoDoc clearly explains the purpose for providers that don't support RFC 8414 or have split-domain architectures. All fields follow Go naming conventions.

examples/oauth_client/README.md (2)

3-19: LGTM! Clear documentation of provider configuration.

The introduction and provider configuration section clearly explains the RFC 8414 discovery mechanism and when manual provider configuration is needed. The GitHub-specific focus aligns well with the code changes and provides helpful context for users.


53-66: LGTM! Configuration section is clear and helpful.

The configuration section provides accurate GitHub-specific endpoint information and includes a helpful reminder about configuring the OAuth App callback URL. The scope documentation is also clear and appropriate.

@dask-58
Copy link
Contributor Author

dask-58 commented Dec 20, 2025

Closing in favor of #637. I didn't see there was already an active draft for this feature.

@dask-58 dask-58 closed this Dec 20, 2025
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.

bug: Cannot Connect to GitHub MCP Server Using OAuth

1 participant