-
Notifications
You must be signed in to change notification settings - Fork 138
Implement outgoing authentication strategies for vMCP #2451
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
Conversation
7e7e3b1 to
3131412
Compare
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 refactors the outgoing authentication system from an OutgoingAuthenticator interface to an OutgoingAuthRegistry interface. The key changes involve:
- Renaming and simplifying the authentication interface to focus on strategy registration/retrieval
- Moving authentication logic directly to Strategy implementations called per-request
- Introducing concrete strategy implementations (UnauthenticatedStrategy, HeaderInjectionStrategy)
- Adding factory functions for creating authentication registries from configuration
- Updating documentation and comments to reflect the new interface naming
Reviewed Changes
Copilot reviewed 18 out of 20 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| pkg/vmcp/types.go | Updated comment to reference OutgoingAuthRegistry interface |
| pkg/vmcp/doc.go | Updated interface documentation and usage examples with new naming and simplified API |
| pkg/vmcp/auth/auth.go | Renamed OutgoingAuthenticator to OutgoingAuthRegistry, removed AuthenticateRequest method |
| pkg/vmcp/auth/outgoing_registry.go | Renamed DefaultOutgoingAuthenticator to DefaultOutgoingAuthRegistry |
| pkg/vmcp/auth/outgoing_registry_test.go | Renamed test suite for registry (was outgoing_authenticator_test.go) |
| pkg/vmcp/auth/strategies/unauthenticated.go | Added UnauthenticatedStrategy implementation for no-auth backends |
| pkg/vmcp/auth/strategies/unauthenticated_test.go | Added comprehensive tests for UnauthenticatedStrategy |
| pkg/vmcp/auth/strategies/header_injection.go | Added HeaderInjectionStrategy for static header injection with CRLF validation |
| pkg/vmcp/auth/strategies/header_injection_test.go | Added comprehensive tests for HeaderInjectionStrategy |
| pkg/vmcp/auth/factory/outgoing.go | Added factory function for creating OutgoingAuthRegistry from configuration |
| pkg/vmcp/auth/factory/incoming.go | Moved incoming auth middleware factory to factory package |
| pkg/vmcp/auth/factory/incoming_test.go | Added tests for incoming auth middleware factory |
| pkg/vmcp/client/client.go | Updated client to use OutgoingAuthRegistry and added authRoundTripper for per-request auth |
| pkg/vmcp/client/client_test.go | Added comprehensive tests for auth integration in client |
| pkg/vmcp/client/mocks/mock_outgoing_registry.go | Generated mock for OutgoingAuthRegistry interface |
| pkg/vmcp/aggregator/cli_discoverer.go | Updated to accept auth config and populate backend auth fields |
| pkg/vmcp/aggregator/cli_discoverer_test.go | Updated tests to pass nil auth config |
| cmd/vmcp/app/commands.go | Updated to use factory package and create OutgoingAuthRegistry |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2451 +/- ##
==========================================
+ Coverage 55.33% 55.38% +0.05%
==========================================
Files 278 281 +3
Lines 26470 26607 +137
==========================================
+ Hits 14646 14736 +90
- Misses 10479 10516 +37
- Partials 1345 1355 +10 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@claude review this PR. Don't be overly verbose. Focus on security aspects as well as how well the code integrates into the vMCP gateway flow. |
|
Claude finished @jhrozek's task —— View job PR Review: Outgoing Authentication Strategies
|
…cycle Move incoming authentication factory from pkg/vmcp/auth/ to pkg/vmcp/auth/factory/ subfolder to improve code organization. This separates factory code from core authentication types and middleware.
Rename OutgoingAuthenticator to OutgoingAuthRegistry to better reflect its responsibility as a strategy registry rather than an authenticator. The interface now focuses solely on strategy management (registration and retrieval), while authentication is performed directly by Strategy implementations. This separation improves performance by eliminating indirection in the hot path (per-request authentication) and clarifies the single responsibility of each component: the registry manages strategies, strategies perform authentication.
Introduces pkg/vmcp/auth/factory to break the circular dependency between pkg/vmcp/auth and pkg/vmcp/auth/strategies. The import cycle occurred because: - auth package needed to import strategies to instantiate them - strategies package imported auth for Identity and context helpers The factory package sits at the composition layer and can import both auth (for interfaces) and strategies (for implementations) without creating cycles.
Refactors HTTPBackendClient to accept an OutgoingAuthRegistry and apply authentication strategies to all backend requests via a new authRoundTripper middleware. Authentication is now resolved and validated once at client creation time rather than per-request, improving performance and enabling early error detection for misconfigurations. The authRoundTripper clones requests to preserve immutability before applying authentication, ensuring thread-safety and preventing unintended side effects.
The CLI backend discoverer now accepts authentication configuration and applies it to discovered backends during the discovery process. This change enables per-backend authentication by: - Adding authConfig parameter to NewCLIBackendDiscoverer constructor - Implementing resolveAuthConfig() to select backend-specific or default authentication settings with proper precedence - Populating Backend.AuthStrategy and Backend.AuthMetadata fields during backend creation Authentication configuration follows this precedence: 1. Backend-specific configuration (cfg.Backends[backendID]) 2. Default configuration (cfg.Default) 3. No authentication (if neither is configured) The populated authentication fields are later consumed when converting Backend instances to BackendTarget for use by the HTTP client's authRoundTripper.
Finalizes the end-to-end authentication flow by connecting the authentication factory, backend discoverer, and HTTP client in the serve command. This enables vMCP proxy to authenticate requests to downstream MCP servers using configured authentication strategies. The serve command now: - Creates outgoing authenticator from configuration using the factory - Provides authentication config to backend discoverer for setup - Supplies authenticator to HTTP client for request signing - Uses factory for incoming authentication middleware (consistency) This completes the authentication architecture where configuration flows through the factory to create strategies that are applied by the client's round tripper to outgoing requests. Also simplifies redundant type annotation in client variable declaration for consistency with Go style conventions.
Replace the pattern of passing nil authenticators with an explicit UnauthenticatedStrategy that implements the Strategy interface as a no-op. This makes the intent clear in configuration and improves type safety by eliminating nil checks. The strategy is appropriate for backends on trusted networks or where authentication is handled at the network layer. Configuration now explicitly declares "strategy: unauthenticated" instead of relying on implicit nil behavior.
Add HeaderInjectionStrategy for injecting static header values into backend requests. This general-purpose strategy supports any HTTP header with any static value, enabling flexible authentication schemes like API keys, bearer tokens, and custom auth headers. The strategy extracts header_name and api_key from metadata configuration and validates them to prevent CRLF injection attacks using pkg/validation functions. Validation occurs at configuration time for fail-fast behavior.
Limit validTypes to strategies actually implemented in this PR: - unauthenticated - header_injection Comment out unimplemented strategies with TODO to add them as they are implemented in future PRs. This prevents accepting configuration for strategies that don't exist yet.
Update example configuration files to use only implemented authentication strategies (unauthenticated and header_injection). Changes: - Replace pass_through with unauthenticated in defaults - Show header_injection example for backends - Comment out unimplemented strategies (pass_through, token_exchange, service_account) with TODOs - Add clear notes about which strategies are currently implemented This ensures example configs are valid and can be used immediately without validation errors.
Rename the misleading 'api_key' field to 'header_value' to better reflect that this strategy can inject any HTTP header value, not just API keys. This improves semantic clarity and matches the general- purpose nature of the strategy.
Add dedicated rawHeaderInjectionAuth struct and update
transformBackendAuthStrategy to properly parse header_injection
configuration from YAML files.
Previously, the header_injection strategy used a generic metadata field
in YAML, but the transform function had no case to handle it, resulting
in empty metadata maps. This follows the established pattern used by
token_exchange and service_account strategies.
The YAML format is now consistent across all strategies:
backends:
github:
type: header_injection
header_injection:
header_name: Authorization
header_value: Bearer xxx
QueryCapabilities was manually creating BackendTarget but omitted AuthStrategy and AuthMetadata fields, causing all backends to fall back to unauthenticated strategy during capability queries. Replace manual struct creation with BackendToTarget() helper to ensure all fields (including auth) are properly copied from Backend to BackendTarget. This bug prevented per-backend authentication from working during the initial capability discovery phase, even though auth was correctly configured by the discoverer.
c117b5d to
f1dd6a2
Compare

This PR implements the outgoing authentication infrastructure for Virtual MCP Server, enabling vMCP to authenticate requests to downstream backend MCP servers using configurable authentication strategies.
Overview
This work builds upon the incoming authentication foundation (merged in #2393) by adding the outgoing authentication boundary where vMCP authenticates TO backend servers. The implementation follows a strategy pattern with a registry-based architecture that separates configuration-time setup from request-time authentication for optimal performance.
Commit Breakdown
Reorganize incoming auth factory into subfolder - Moves incoming authentication factory code to
pkg/vmcp/auth/factory/subfolder to prepare for adding outgoing authentication factory and prevent package organization issues.Add factory package to resolve auth import cycle - Creates
pkg/vmcp/auth/factorypackage to break circular dependencies between auth interfaces and strategy implementations, allowing the factory to compose both without import cycles.Refactor outgoing auth to separate registry from strategy - Renames
OutgoingAuthenticatortoOutgoingAuthRegistryand removes theAuthenticateRequest()method, clarifying that the registry manages strategies while Strategy implementations perform actual authentication, improving performance by eliminating indirection.Integrate authentication registry into HTTP backend client - Wires authentication into
HTTPBackendClientviaauthRoundTrippermiddleware that resolves strategies at client creation time (cold path) and applies them per-request (hot path), enabling fail-fast validation and 67% reduction in per-request overhead.Apply auth configuration in backend discoverer - Extends the CLI backend discoverer to accept authentication configuration and populate
Backend.AuthStrategyandBackend.AuthMetadatafields with proper precedence (backend-specific > default > unauthenticated).Complete outgoing authentication integration in serve command - Connects all components in the serve command by creating the auth registry from configuration, passing it to the discoverer and HTTP client, completing the end-to-end authentication flow.
Add explicit unauthenticated strategy for vMCP - Introduces
UnauthenticatedStrategyas an explicit no-op implementation to replace nil authenticator patterns, improving type safety and making intent clear in configuration.Implement HeaderInjection authentication strategy - Adds the first concrete strategy implementation that injects static HTTP headers for API key and bearer token authentication, with CRLF injection protection and comprehensive test coverage.
Architecture
The implementation uses a Registry + Strategy pattern:
OutgoingAuthRegistry): Manages strategy registration and lookupStrategyinterface): Performs authentication by modifying HTTP requestsauth/factory): Composes registry with strategies from configurationauthRoundTripper): Applies strategies to outgoing requestsPerformance characteristics:
Testing
Next Steps
Future PRs will add additional authentication strategies:
Related to Epic #150 (Backend Authentication).