stacklok / stacklok/toolhive

Support Bearer Token Authentication for Remote MCP Servers

Open
#3,111 1 comment 0 reactions 1 assignee View on GitHub

@amirejaz is already working on this.

Since Dec 23, 2025.

api authentication cli enhancement go kubernetes operator registry
Dominant language
Go
Stars
2.2k
Forks
300
Avg merge
1d 15h
Merged PRs (30d)
184

Description

Problem

Currently, ToolHive only supports OAuth/OIDC-based authentication for remote MCP servers. However, we're seeing an increasing number of MCP servers that require direct bearer token authentication instead of (or in addition to) OAuth flows.

Current State

The current implementation in pkg/auth/remote/handler.go only handles OAuth-based authentication:

// Currently only OAuth-based authentication is supported
logger.Infof("Unsupported authentication type: %s", authInfo.Type)
return nil, nil

The authentication handler detects authentication requirements and only processes OAuth flows. When a server requires bearer token authentication, it's currently unsupported.

Proposed Solution

Add support for bearer token authentication alongside the existing OAuth/OIDC support. This should include support for CLI, API, and Kubernetes operator modes.

1. Configuration Support

Extend pkg/auth/remote/config.go to add bearer token fields:

  • BearerToken: Direct bearer token string (CLI mode)
  • BearerTokenFile: Path to file containing bearer token (CLI mode)
  • BearerTokenEnvVar: Environment variable name containing bearer token (CLI mode)

These should follow the same pattern as ClientSecret resolution (flag → file → env var priority) using the existing resolveSecret function pattern.

2. CLI Flags

Add new flags to cmd/thv/app/auth_flags.go following the existing --remote-auth-* pattern:

--remote-auth-bearer-token string
    Bearer token for remote server authentication

--remote-auth-bearer-token-file string
    Path to file containing bearer token (alternative to --remote-auth-bearer-token)

--remote-auth-bearer-token-env-var string
    Environment variable name containing bearer token (alternative to --remote-auth-bearer-token)
3. API Support

Extend pkg/api/v1/workload_types.go to add bearer token support to remoteOAuthConfig:

type remoteOAuthConfig struct {
    // ... existing fields ...
    BearerToken *secrets.SecretParameter `json:"bearer_token,omitempty"`
}

The API should leverage ToolHive secret references via SecretParameter (similar to how ClientSecret works). The bearer token will be converted to CLI format using ToCLIString() when building the config, following the same pattern as OAuth client secrets in pkg/api/v1/workload_service.go.

4. Kubernetes Operator Support

For Kubernetes/operator mode, bearer tokens should be stored in Kubernetes Secrets and referenced via SecretKeyRef (similar to how OAuth client secrets are handled in cmd/thv-operator/api/v1alpha1/mcpexternalauthconfig_types.go).

Add bearer token configuration to the appropriate CRD types (e.g., MCPRemoteProxy or a new remote auth config type) with SecretKeyRef support.

5. Authentication Handler Enhancement

Extend pkg/auth/remote/handler.go to:

  • Detect bearer token authentication requirements (from WWW-Authenticate headers or configuration)
  • Create a static token source that returns the bearer token
  • Support both static bearer tokens and potentially token sources for refresh scenarios
  • Implement bearer token source that implements oauth2.TokenSource interface for compatibility with existing middleware
6. Transport Integration

Ensure bearer tokens are properly injected into HTTP requests via the existing token injection middleware in pkg/transport/middleware/token_injection.go. The middleware already supports oauth2.TokenSource, so we need to create a bearer token source that implements this interface.

7. Registry Support

Extend the registry schema to support bearer token configuration in remote server definitions. Add a bearer_token_config section alongside oauth_config:

{
  "remote_servers": {
    "example-bearer": {
      "url": "https://api.example.com/mcp",
      "bearer_token_config": {
        "token_file": "/path/to/token",
        "token_env_var": "API_BEARER_TOKEN"
      }
    }
  }
}

Usage Examples

CLI Usage
# Direct bearer token
thv run https://remote-mcp-server.com \
  --remote-auth-bearer-token "my-bearer-token-here"

# Bearer token from file (recommended for security)
thv run https://remote-mcp-server.com \
  --remote-auth-bearer-token-file /path/to/token/file

# Bearer token from environment variable
export API_TOKEN="my-bearer-token"
thv run https://remote-mcp-server.com \
  --remote-auth-bearer-token-env-var API_TOKEN

# Bearer token can work alongside OAuth (if server supports both)
thv run https://remote-mcp-server.com \
  --remote-auth-bearer-token-file /path/to/token \
  --remote-auth-issuer https://auth.example.com
API Usage
{
  "name": "my-workload",
  "url": "https://remote-mcp-server.com",
  "oauth_config": {
    "bearer_token": {
      "name": "api-bearer-token",
      "target": "bearer_token"
    }
  }
}

The bearer token will be resolved from ToolHive secrets manager using the secret reference api-bearer-token and converted to CLI format api-bearer-token,target=bearer_token when building the config.

Kubernetes Operator Usage
apiVersion: toolhive.stacklok.dev/v1alpha1
kind: MCPRemoteProxy
metadata:
  name: example-proxy
spec:
  remoteURL: https://remote-mcp-server.com
  bearerToken:
    secretKeyRef:
      name: api-bearer-token-secret
      key: token
Registry Configuration
{
  "version": "1.0.0",
  "last_updated": "2025-01-12T00:00:00Z",
  "remote_servers": {
    "example-bearer": {
      "url": "https://api.example.com/mcp",
      "description": "Remote MCP server with bearer token authentication",
      "tier": "community",
      "status": "active",
      "transport": "sse",
      "bearer_token_config": {
        "token_file": "/etc/secrets/api-token",
        "token_env_var": "API_BEARER_TOKEN"
      }
    },
    "example-oauth": {
      "url": "https://oauth-mcp.example.com",
      "oauth_config": {
        "issuer": "https://auth.example.com"
      }
    }
  }
}

Implementation Considerations

  • Security: Bearer tokens should be treated as sensitive and handled securely (similar to client secrets)
    • No logging of token values
    • Support for file-based storage (recommended for CLI)
    • Environment variable support for containerized deployments
    • ToolHive secret references for API mode
    • Kubernetes Secrets for operator mode
  • Token Refresh: Consider token rotation/refresh mechanisms for long-lived bearer tokens (future enhancement)
  • Backward Compatibility: Maintain full backward compatibility with existing OAuth flows
  • Coexistence: Bearer token authentication should work alongside OAuth (server may support both)
  • Priority: When both bearer token and OAuth are configured, bearer token should take precedence (or allow explicit selection)
  • Discovery: Bearer token authentication may be detected from WWW-Authenticate headers, but should also be configurable explicitly
  • API Secret Resolution: Bearer tokens in API mode should leverage ToolHive secret references via SecretParameter, following the same pattern as ClientSecret in pkg/api/v1/workload_service.go (lines 200-203, 311-314)

Related Code

  • pkg/auth/remote/handler.go: Main authentication handler (lines 82-84 show current limitation)
  • pkg/auth/remote/config.go: Authentication configuration structure
  • pkg/transport/http.go: HTTP transport with token injection (line 115: SetTokenSource)
  • pkg/transport/middleware/token_injection.go: Token injection middleware (line 29: Authorization header injection)
  • cmd/thv/app/auth_flags.go: CLI flag definitions (lines 65-89: RemoteAuthFlags structure)
  • cmd/thv/app/run_flags.go: Flag processing and config building (lines 573-606: configureRemoteAuth)
  • pkg/api/v1/workload_types.go: API request types (lines 87-115: remoteOAuthConfig)
  • pkg/api/v1/workload_service.go: API service implementation (lines 200-203, 311-314: ClientSecret handling)
  • pkg/registry/registry/registry_types.go: Registry schema (lines 176-190: RemoteServerMetadata)
  • cmd/thv-operator/api/v1alpha1/mcpexternalauthconfig_types.go: Operator CRD types for external auth

Acceptance Criteria

  • Bearer token can be configured via CLI flags (--remote-auth-bearer-token, --remote-auth-bearer-token-file, --remote-auth-bearer-token-env-var)
  • Bearer token can be configured via API using ToolHive secret references (SecretParameter)
  • Bearer token can be configured via Kubernetes operator using SecretKeyRef
  • Bearer token can be configured via registry/runconfig
  • Bearer token is properly injected into HTTP requests to remote MCP servers via Authorization: Bearer <token> header
  • Bearer token authentication works alongside OAuth (when server supports both)
  • Bearer tokens are handled securely (no logging, proper secret management following resolveSecret pattern)
  • Bearer token source implements oauth2.TokenSource interface for compatibility with existing middleware
  • API bearer token resolution follows the same pattern as ClientSecret (using SecretParameter and ToCLIString())
  • Documentation updated to reflect bearer token support for CLI, API, and operator modes
  • Tests added for bearer token authentication flow in all modes (CLI, API, operator)
  • Registry schema updated to support bearer_token_config

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.