Skip to content

Conversation

jhrozek
Copy link
Contributor

@jhrozek jhrozek commented Oct 3, 2025

Add a token exchange client library that enables ToolHive to exchange incoming access tokens for backend-specific tokens when proxying MCP server requests. The implementation follows RFC 8693 and is compatible with standard oauth2.TokenSource interfaces.

Fixes #2064

Add a token exchange client library that enables ToolHive to exchange
incoming access tokens for backend-specific tokens when proxying MCP
server requests. The implementation follows RFC 8693 and is compatible
with standard oauth2.TokenSource interfaces.

Fixes #2064
@jhrozek
Copy link
Contributor Author

jhrozek commented Oct 3, 2025

@claude review this PR. Make sure to review it from the security perspective and read RFC 8693 before the review.

Copy link
Contributor

claude bot commented Oct 3, 2025

Claude finished @jhrozek's task —— View job


OAuth 2.0 Token Exchange (RFC 8693) Security Review

I've completed a comprehensive security review of this Token Exchange implementation. Here are my findings:

Review Tasks:

  • Read RFC 8693 specification to understand Token Exchange standard (Using existing knowledge)
  • Review pkg/auth/tokenexchange/exchange.go implementation
  • Review pkg/auth/tokenexchange/exchange_test.go test coverage
  • Analyze security aspects (input validation, token handling, error disclosure)
  • Check RFC 8693 compliance
  • Verify proper integration with oauth2.TokenSource interfaces
  • Provide comprehensive security review feedback

🔒 Security Analysis Summary

Overall Assessment: SECURE
This implementation demonstrates excellent security practices and RFC 8693 compliance. No critical security vulnerabilities identified.

🟢 Security Strengths

1. Credential Protection Excellence

  • Token Redaction: All sensitive data properly redacted in string representations (String() methods)
    • exchange.go:101-116: Subject tokens shown as [REDACTED]
    • exchange.go:129-142: Access/refresh tokens redacted in responses
    • exchange.go:151-159: Client secrets properly redacted
  • Test Coverage: Comprehensive credential leakage tests (exchange_test.go:555-616)

2. Robust Input Validation

  • Required Field Validation:
    • exchange.go:189-209: Config validation with proper URL parsing
    • exchange.go:327-329: Subject token requirement enforced
  • URL Validation: Uses url.Parse() for endpoint validation (line 203-206)

3. DoS Protection Measures

  • Response Size Limiting: maxResponseBodySize = 1MB (line 34)
    • Properly implemented with io.LimitReader (exchange.go:403)
    • Test coverage in exchange_test.go:524-553
  • HTTP Timeouts: 30-second default timeout configured (line 31)

4. Error Handling Security

  • Information Disclosure Prevention:
    • Generic error messages that don't leak sensitive implementation details
    • OAuth errors properly parsed and formatted (exchange.go:43-69)
    • Debug logging only for non-sensitive information
  • No Credential Leakage in Errors: Tested extensively (exchange_test.go:555-616)

🟡 Areas for Security Consideration

1. SSRF Protection Awareness

  • Current State: No explicit SSRF prevention in the library itself
  • Assessment: This is acceptable for a library design - SSRF protection should be at the application layer
  • Recommendation: Document that applications should validate TokenURL against allowlists
  • Test Coverage: Good test showing library doesn't prevent/facilitate SSRF (exchange_test.go:1025-1072)

2. Transport Security

  • Current: Relies on standard http.Client for TLS configuration
  • Recommendation: Document TLS requirements for production use
  • Note: Applications should configure HTTPClient with proper TLS settings

🟢 RFC 8693 Compliance Analysis

Required Parameters ✅

  • grant_type: Correctly set to urn:ietf:params:oauth:grant-type:token-exchange
  • subject_token: Required and validated
  • subject_token_type: Defaults to access_token as per RFC

Optional Parameters ✅

  • audience: Properly supported
  • scope: Array correctly converted to space-separated string
  • resource: Supported
  • actor_token/actor_token_type: Delegation scenarios supported

Response Handling ✅

  • All RFC 8693 response fields properly parsed
  • expires_in correctly converted to oauth2.Token.Expiry
  • Minimal response support (only access_token + token_type)

🟢 Integration Assessment

oauth2.TokenSource Compatibility ✅

  • Properly implements Token() (*oauth2.Token, error) interface
  • Correct token expiry handling
  • Refresh token support

Design Patterns ✅

  • Clean separation of concerns
  • Testable design with dependency injection (SubjectTokenProvider function)
  • Proper context propagation for cancellation

📋 Minor Enhancement Suggestions

  1. Documentation Enhancement: Add godoc examples for common usage patterns
  2. TLS Guidance: Document recommended TLS configurations for production
  3. SSRF Guidance: Add security documentation about URL validation requirements

🧪 Test Coverage Analysis

Excellent test coverage with 26 comprehensive test scenarios covering:

  • ✅ Happy path scenarios
  • ✅ Error conditions and edge cases
  • ✅ RFC compliance verification
  • ✅ Security boundary testing (credential leakage, DoS protection)
  • ✅ Network failure handling
  • ✅ Malformed response handling
  • ✅ Context cancellation

🚀 Conclusion

This is a high-quality, security-conscious implementation of RFC 8693. The code demonstrates:

  • Strong security practices with comprehensive credential protection
  • Excellent RFC compliance
  • Robust error handling and input validation
  • Comprehensive test coverage including security scenarios
  • Clean, maintainable design

Recommendation: APPROVE - This implementation meets enterprise security standards and is ready for production use.


Job Run: Link

@JAORMX JAORMX requested a review from Copilot October 3, 2025 13:29
Copy link
Contributor

@Copilot Copilot AI left a 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 implements OAuth 2.0 Token Exchange (RFC 8693) support for ToolHive by adding a token exchange client library. The implementation allows exchanging incoming access tokens for backend-specific tokens when proxying MCP server requests, following RFC 8693 standards and being compatible with standard oauth2.TokenSource interfaces.

  • Adds a comprehensive token exchange client library with Config, TokenSource implementation, and HTTP request handling
  • Implements proper error handling for OAuth errors, network failures, and malformed responses
  • Includes extensive test coverage for happy path, error scenarios, edge cases, and security considerations

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
pkg/auth/tokenexchange/exchange.go Core implementation of OAuth 2.0 token exchange with Config, TokenSource, and HTTP handling
pkg/auth/tokenexchange/exchange_test.go Comprehensive test suite covering success cases, error handling, and edge cases

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +198 to +201
if c.ClientID == "" {
return fmt.Errorf("ClientID is required")
}

Copy link
Preview

Copilot AI Oct 3, 2025

Choose a reason for hiding this comment

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

The validation requires ClientID but the implementation supports public clients (no client credentials). This creates an inconsistency since line 373-378 in addClientAuthentication allows empty ClientID/ClientSecret. Consider making ClientID optional or documenting why it's required for this specific use case.

Suggested change
if c.ClientID == "" {
return fmt.Errorf("ClientID is required")
}
// ClientID is optional to support public clients (RFC 8693, section 2.2)
// If your use case requires a ClientID, ensure it is set in the Config.

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is technically right, but the IDPs I looked all support only confidential clients.

HOWEVER, this made me realize that the authentication I used (using the form fields) is technically supported, but both keycloak and okta docs seem to suggest that it's preferred to use Authorization: Bearer base64(client_id:client_secret).

Copy link

codecov bot commented Oct 3, 2025

Codecov Report

❌ Patch coverage is 75.55556% with 55 lines in your changes missing coverage. Please review.
✅ Project coverage is 48.53%. Comparing base (5285ed4) to head (dc9bd0f).
⚠️ Report is 20 commits behind head on main.

Files with missing lines Patch % Lines
pkg/auth/tokenexchange/exchange.go 75.55% 47 Missing and 8 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2082      +/-   ##
==========================================
+ Coverage   48.20%   48.53%   +0.33%     
==========================================
  Files         233      239       +6     
  Lines       29229    30231    +1002     
==========================================
+ Hits        14089    14673     +584     
- Misses      14106    14448     +342     
- Partials     1034     1110      +76     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

Add a basic implementation of token exchange
1 participant