WIP feat(mcp): support pre-registered OAuth clients via catalog metadata#444
Draft
saucow wants to merge 5 commits intodocker:mainfrom
Draft
WIP feat(mcp): support pre-registered OAuth clients via catalog metadata#444saucow wants to merge 5 commits intodocker:mainfrom
saucow wants to merge 5 commits intodocker:mainfrom
Conversation
This commit adds support for manually registering OAuth client credentials for MCP servers that don't support Dynamic Client Registration (DCR), along with comprehensive documentation of OAuth flows in Docker CE mode. Changes: - Add 'docker mcp oauth register' command for manual client registration - Supports both confidential and public OAuth clients - Stores credentials securely in Docker credential helpers - Includes validation for URLs and required fields - Add detailed OAuth CE mode documentation (572 lines) - Documents DCR flow, authorization, token storage, and refresh - Includes architecture diagrams, CLI examples, and troubleshooting - Provides file references with line numbers for code navigation - Covers security features (PKCE, token binding, credential helpers) - Fix linting issues in OAuth command handlers - Add explicit error handling for MarkFlagRequired calls - Rename unused context parameter to underscore - Apply gofmt formatting to imports The manual registration feature enables OAuth integration with providers that don't support RFC 7591 DCR, expanding compatibility with a wider range of OAuth providers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
In CE mode, Docker Desktop's secret mechanisms (jcat, Secrets Engine, se:// URIs) are not available. OAuth tokens stored by `docker mcp oauth authorize` in the credential helper were only accessible to remote servers. This adds a CE mode path that reads OAuth tokens from the credential helper and injects them as raw values into local container environment variables. Reuses the existing CredentialHelper.GetOAuthToken() -> getOAuthTokenCE() code path that already works for remote servers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…njection Catalog schema: - OAuthServerMetadata (RFC 8414 field naming) + OAuthRegistration (client_id only) - client_secret never in catalogs, user provides via secrets store - HasPreRegisteredOAuth() enables OAuth for non-remote servers OAuth registration: - Read client_secret from Secrets Engine at registration time, pass to Pinata - Read scopes from server_metadata.scopes_supported - Re-register DcrClient with client_secret at authorize time (CLI path) CE mode: - readCEModeOAuthSecrets() for container secret injection without se:// URIs - docker mcp oauth register command for manual client registration Container args: - Skip secrets without env field (OAuth infrastructure secrets) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Enables OAuth for MCP servers whose providers don't support Dynamic Client Registration (DCR) -- such as Google, Slack, and Microsoft. Catalog authors embed the OAuth
client_idand server metadata (authorization/token endpoints, scopes) in a new RFC 8414-aligned schema. Theclient_secretis never distributed in catalogs; users provide it via Docker's secrets store.Problem
The existing OAuth flow assumes providers support DCR: the gateway discovers OAuth endpoints via
/.well-known/oauth-authorization-serverand dynamically registers a client. Providers like Google and Slack don't support DCR, leaving teams with no way to set up OAuth through catalogs.Solution
New catalog schema
Two new optional sections on
OAuthProvider, following RFC 8414 field naming:The
client_secretis declared as a required secret in the server'ssecretssection. Users set it viadocker mcp secret set {server}.client_secret. The gateway reads it from the Secrets Engine and passes it to Pinata during DCR registration.How it flows
sequenceDiagram participant Admin as Catalog Admin participant User as Team Member participant GW as Gateway participant P as Pinata participant SE as Secrets Engine participant Google as Google OAuth Note over Admin: One-time: register Google OAuth app, push catalog with client_id + endpoints User->>GW: Pull catalog, create profile with server GW->>SE: Read client_secret (user set via docker mcp secret set) GW->>P: RegisterDCRClientPending with client_id + client_secret + endpoints + scopes Note over P: Create REGISTERED client directly (skip DCR) User->>P: Click Authorize (or docker mcp oauth authorize) P->>Google: Authorization URL with scopes + PKCE + access_type=offline + prompt=consent Google-->>P: Callback with code P->>Google: Exchange code (+ client_secret + PKCE) Google-->>P: access_token + refresh_token P->>SE: Store token Note over P: Background refresh loop maintains tokenCE mode support
For standalone environments without Docker Desktop:
docker mcp oauth registercommand for manual client registrationdocker mcp oauth authorizehandles the full flow (localhost callback, PKCE, token exchange)readCEModeOAuthSecrets()resolves tokens from the credential helper for container secret injection (nose://URI resolution in CE mode)Key changes
pkg/catalog/types.goOAuthServerMetadata(RFC 8414),OAuthRegistration,HasPreRegisteredOAuth()pkg/oauth/dcr_registration.goclient_secretfrom Secrets Engine, pass to Pinata with scopespkg/workingset/oauth.goserver_metadata.scopes_supportedcmd/docker-mcp/oauth/auth.goclient_secretat authorize time (CLI path)cmd/docker-mcp/oauth/register.godocker mcp oauth registercommand for CE modecmd/docker-mcp/server/enable.goHasPreRegisteredOAuth()check alongsideHasExplicitOAuthProviders()pkg/gateway/clientpool.goenvfield (OAuth infrastructure, not container env vars)pkg/gateway/secrets_ce.gopkg/gateway/configuration*.gopkg/gateway/run.gopkg/desktop/auth.goClientSecret+ScopesonRegisterDCRRequestBackward compatibility
HasExplicitOAuthProviders()(remote servers with DCR) continues to work as before.client_secretfield exists onRegisterDCRRequestbut is never populated from the catalog schema -- it comes from the Secrets Engine.