Skip to content

Commit 0456157

Browse files
authored
CLI commands not working for remote MCP OAuth servers (#169)
1 parent 744cfe1 commit 0456157

File tree

6 files changed

+23
-926
lines changed

6 files changed

+23
-926
lines changed

cmd/docker-mcp/oauth/auth.go

Lines changed: 7 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -3,142 +3,24 @@ package oauth
33
import (
44
"context"
55
"fmt"
6-
"os"
76

8-
"github.com/docker/mcp-gateway/pkg/catalog"
97
"github.com/docker/mcp-gateway/pkg/desktop"
10-
"github.com/docker/mcp-gateway/pkg/oauth"
118
)
129

1310
func Authorize(ctx context.Context, app string, scopes string) error {
1411
client := desktop.NewAuthClient()
15-
// Check if DCR client exists
16-
dcrClient, err := client.GetDCRClient(ctx, app)
17-
if err != nil {
18-
// Not a DCR provider - handle traditional OAuth flow for built-in providers
19-
authResponse, err := client.PostOAuthApp(ctx, app, scopes, false)
20-
if err != nil {
21-
return err
22-
}
23-
24-
// Check if the response contains a valid browser URL
25-
if authResponse.BrowserURL == "" {
26-
return fmt.Errorf("OAuth provider does not exist")
27-
}
28-
29-
fmt.Printf("Opening your browser for authentication. If it doesn't open automatically, please visit: %s\n", authResponse.BrowserURL)
30-
return nil
31-
}
32-
33-
// This is a DCR provider - check if it needs setup (atomic DCR)
34-
fmt.Fprintf(os.Stderr, "[MCP-DCR] Found DCR client for %s, state: %s\n", app, dcrClient.State)
35-
if dcrClient.State == "unregistered" {
36-
// Unregistered DCR provider - needs atomic discovery + DCR + auth
37-
fmt.Printf("first-time oauth setup for %s\n", app)
38-
return performAtomicDCRAndAuthorize(ctx, app, scopes)
39-
}
40-
41-
// DCR client exists and is ready - proceed with normal authorization
42-
return authorizeRemoteMCPServer(ctx, app, scopes, dcrClient)
43-
}
44-
45-
// performAtomicDCRAndAuthorize performs discovery, DCR, and authorization atomically
46-
func performAtomicDCRAndAuthorize(ctx context.Context, serverName string, scopes string) error {
47-
// Get catalog to find server configuration
48-
cat, err := catalog.GetWithOptions(ctx, true, nil)
49-
if err != nil {
50-
return fmt.Errorf("failed to get catalog: %w", err)
51-
}
52-
53-
server, found := cat.Servers[serverName]
54-
if !found {
55-
return fmt.Errorf("server %s not found in catalog", serverName)
56-
}
57-
58-
// Get server URL
59-
serverURL := server.Remote.URL
60-
if serverURL == "" {
61-
serverURL = server.SSEEndpoint
62-
if serverURL == "" {
63-
return fmt.Errorf("server %s has no remote URL configured", serverName)
64-
}
65-
}
66-
67-
fmt.Fprintf(os.Stderr, "[MCP-DCR] Starting OAuth discovery for server: %s\n", serverName)
68-
fmt.Printf("discovering oauth requirements for %s\n", serverName)
69-
70-
// STEP 1: OAuth Discovery (catalog-based, bypass 401 probe)
71-
discovery, err := oauth.DiscoverOAuthRequirements(ctx, serverURL)
72-
if err != nil {
73-
return fmt.Errorf("OAuth discovery failed: %w", err)
74-
}
75-
76-
// STEP 2: Dynamic Client Registration
77-
fmt.Fprintf(os.Stderr, "[MCP-DCR] Starting DCR registration for server: %s\n", serverName)
78-
fmt.Printf("registering oauth client for %s\n", serverName)
79-
credentials, err := oauth.PerformDCR(ctx, discovery, serverName)
80-
if err != nil {
81-
return fmt.Errorf("DCR registration failed: %w", err)
82-
}
83-
84-
// Extract provider name from OAuth config
85-
var providerName string
86-
if server.OAuth != nil && len(server.OAuth.Providers) > 0 {
87-
providerName = server.OAuth.Providers[0].Provider // Use first provider
88-
} else {
89-
return fmt.Errorf("no OAuth providers configured for server %s", serverName)
90-
}
91-
92-
// STEP 3: Store DCR client in Docker Desktop (updates the pending provider)
93-
client := desktop.NewAuthClient()
94-
dcrRequest := desktop.RegisterDCRRequest{
95-
ClientID: credentials.ClientID,
96-
ProviderName: providerName,
97-
AuthorizationEndpoint: credentials.AuthorizationEndpoint,
98-
TokenEndpoint: credentials.TokenEndpoint,
99-
ResourceURL: credentials.ServerURL,
100-
}
101-
102-
if err := client.RegisterDCRClient(ctx, serverName, dcrRequest); err != nil {
103-
return fmt.Errorf("failed to store DCR client: %w", err)
104-
}
105-
106-
fmt.Fprintf(os.Stderr, "[MCP-DCR] DCR registration successful, clientID: %s, authEndpoint: %s\n", credentials.ClientID, credentials.AuthorizationEndpoint)
107-
fmt.Printf("oauth client registered successfully\n")
108-
fmt.Printf(" Client ID: %s\n", credentials.ClientID)
109-
110-
// STEP 4: Continue with authorization
111-
dcrClient := &desktop.DCRClient{
112-
ServerName: serverName,
113-
ProviderName: providerName,
114-
ClientID: credentials.ClientID,
115-
AuthorizationEndpoint: credentials.AuthorizationEndpoint,
116-
TokenEndpoint: credentials.TokenEndpoint,
117-
}
118-
119-
return authorizeRemoteMCPServer(ctx, serverName, scopes, dcrClient)
120-
}
121-
122-
func authorizeRemoteMCPServer(ctx context.Context, serverName string, scopes string, dcrClient *desktop.DCRClient) error {
123-
client := desktop.NewAuthClient()
124-
125-
fmt.Printf("starting oauth authorization for %s\n", serverName)
126-
fmt.Printf(" Using client: %s\n", dcrClient.ClientID)
12712

128-
// Start OAuth flow via Docker Desktop (handles PKCE generation and browser opening)
129-
fmt.Printf("starting oauth authorization flow\n")
130-
authResponse, err := client.PostOAuthApp(ctx, serverName, scopes, false)
13+
// Start OAuth flow - Docker Desktop handles DCR automatically if needed
14+
authResponse, err := client.PostOAuthApp(ctx, app, scopes, false)
13115
if err != nil {
132-
return fmt.Errorf("failed to start OAuth flow: %w", err)
16+
return err
13317
}
13418

135-
// Provide user feedback based on auth response
136-
if authResponse.BrowserURL != "" {
137-
fmt.Printf("browser opened for oauth authentication\n")
138-
fmt.Printf("If the browser doesn't open, visit: %s\n", authResponse.BrowserURL)
139-
} else {
140-
fmt.Printf("oauth flow started successfully\n")
19+
// Check if the response contains a valid browser URL
20+
if authResponse.BrowserURL == "" {
21+
return fmt.Errorf("OAuth provider does not exist")
14122
}
14223

24+
fmt.Printf("Opening your browser for authentication. If it doesn't open automatically, please visit: %s\n", authResponse.BrowserURL)
14325
return nil
14426
}

cmd/docker-mcp/oauth/revoke.go

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,25 @@ import (
99

1010
func Revoke(ctx context.Context, app string) error {
1111
client := desktop.NewAuthClient()
12-
// Check if this is a DCR provider by trying to get DCR client directly
12+
13+
// Check if this is a DCR provider
1314
dcrClient, err := client.GetDCRClient(ctx, app)
1415
if err == nil && dcrClient.State != "" {
15-
// This is a DCR provider (registered or unregistered) - revoke OAuth access only (preserve DCR client for re-auth)
16-
return revokeRemoteMCPServer(ctx, app)
16+
// Handle UNREGISTERED providers - they don't have tokens yet
17+
if dcrClient.State == "unregistered" {
18+
return fmt.Errorf("provider %s is not authenticated yet - nothing to revoke", app)
19+
}
20+
21+
// REGISTERED DCR provider - revoke tokens but preserve DCR client for re-auth
22+
fmt.Printf("Revoking OAuth access for %s...\n", app)
23+
if err := client.DeleteOAuthApp(ctx, app); err != nil {
24+
return fmt.Errorf("failed to revoke OAuth access for %s: %w", app, err)
25+
}
26+
fmt.Printf("OAuth access revoked for %s\n", app)
27+
fmt.Printf("Note: DCR client registration preserved. Run 'docker mcp oauth authorize %s' to re-authenticate\n", app)
28+
return nil
1729
}
1830

19-
// Traditional OAuth provider revoke (built-in providers)
31+
// Built-in OAuth provider - just revoke tokens
2032
return client.DeleteOAuthApp(ctx, app)
2133
}
22-
23-
func revokeRemoteMCPServer(ctx context.Context, serverName string) error {
24-
client := desktop.NewAuthClient()
25-
26-
fmt.Printf("Revoking OAuth access for %s...\n", serverName)
27-
28-
// Revoke OAuth tokens only - DCR client remains for future authorization
29-
if err := client.DeleteOAuthApp(ctx, serverName); err != nil {
30-
return fmt.Errorf("failed to revoke OAuth access for %s: %w", serverName, err)
31-
}
32-
33-
fmt.Printf("OAuth access revoked for %s\n", serverName)
34-
fmt.Printf("Note: DCR client registration preserved for future re-authorization\n")
35-
36-
return nil
37-
}

pkg/oauth/dcr.go

Lines changed: 0 additions & 143 deletions
This file was deleted.

0 commit comments

Comments
 (0)