Skip to content

Commit 721f6cf

Browse files
committed
Fixing desktop cleanup + other issues
1 parent 8320403 commit 721f6cf

File tree

3 files changed

+33
-23
lines changed

3 files changed

+33
-23
lines changed

cmd/docker-mcp/oauth/auth.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,6 @@ func authorizeCommunityMode(ctx context.Context, serverName string, scopes strin
173173
return fmt.Errorf("docker pass required for community server OAuth: %w", err)
174174
}
175175

176-
// Clean any stale Desktop Secrets Engine entries for this server. If the
177-
// server was previously authorized via Desktop OAuth (flag OFF), the
178-
// docker-desktop-mcp-oauth plugin holds a token with a more specific
179-
// pattern match (docker/mcp/oauth/**) than docker-pass (**), causing
180-
// GetSecret to return the stale Desktop value instead of the fresh
181-
// docker pass value written below.
182-
cleanStaleDesktopEntriesFunc(ctx, serverName)
183-
184176
// Step 1: Create callback server first — we need its localhost URL for DCR
185177
// registration. Community servers reject mcp.docker.com/oauth/callback and
186178
// only accept localhost redirect URIs.
@@ -310,6 +302,15 @@ func authorizeCommunityMode(ctx context.Context, serverName string, scopes strin
310302
return fmt.Errorf("failed to store token: %w", err)
311303
}
312304

305+
// Step 8: Clean stale Desktop Secrets Engine entries now that the fresh
306+
// token is safely stored in docker pass. We defer this until after storage
307+
// succeeds so that if the authorize flow fails at any earlier step, the
308+
// user retains their existing Desktop authorization as a fallback.
309+
// The docker-desktop-mcp-oauth plugin (pattern docker/mcp/oauth/**) has
310+
// higher priority than docker-pass (**), so stale Desktop entries would
311+
// shadow the fresh token if not removed.
312+
cleanStaleDesktopEntriesFunc(ctx, serverName)
313+
313314
fmt.Printf("Authorization successful! Token stored securely.\n")
314315
fmt.Printf("You can now use: docker mcp server start %s\n", serverName)
315316

cmd/docker-mcp/oauth/auth_test.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,13 @@ func TestAuthorize_CEMode_CommunityServer(t *testing.T) {
141141
assert.Equal(t, "ce", *called)
142142
}
143143

144-
// TestAuthorizeCommunityMode_CleansDesktopEntries verifies that the real
145-
// authorizeCommunityMode function cleans stale Desktop Secrets Engine
146-
// entries before proceeding with the Gateway OAuth flow.
147-
func TestAuthorizeCommunityMode_CleansDesktopEntries(t *testing.T) {
144+
// TestAuthorizeCommunityMode_NoCleanupOnFailure verifies that
145+
// authorizeCommunityMode does NOT clean stale Desktop entries when the
146+
// authorize flow fails before token storage. This ensures the user
147+
// retains their existing Desktop authorization as a fallback if the
148+
// community flow fails mid-way (port conflict, user closes browser, etc.).
149+
// Cleanup only runs after the fresh token is safely stored in docker pass.
150+
func TestAuthorizeCommunityMode_NoCleanupOnFailure(t *testing.T) {
148151
// Save and restore all function pointers touched by this test.
149152
oldDesktopCleanup := cleanStaleDesktopEntriesFunc
150153
oldCheckPass := checkHasDockerPassFunc
@@ -158,21 +161,20 @@ func TestAuthorizeCommunityMode_CleansDesktopEntries(t *testing.T) {
158161
// Mock docker pass check to succeed.
159162
checkHasDockerPassFunc = func(_ context.Context) error { return nil }
160163

161-
// Mock callback server creation to fail — this stops execution right
162-
// after cleanup runs, avoiding the need to mock DCR, PKCE, etc.
164+
// Mock callback server creation to fail — simulates a mid-flow failure.
163165
newCallbackServerFunc = func() (*pkgoauth.CallbackServer, error) {
164-
return nil, fmt.Errorf("test: stop after cleanup")
166+
return nil, fmt.Errorf("test: port conflict")
165167
}
166168

167-
var desktopCleanupCalled string
168-
cleanStaleDesktopEntriesFunc = func(_ context.Context, app string) {
169-
desktopCleanupCalled = app
169+
var desktopCleanupCalled bool
170+
cleanStaleDesktopEntriesFunc = func(_ context.Context, _ string) {
171+
desktopCleanupCalled = true
170172
}
171173

172174
// Call the real authorizeCommunityMode directly.
173175
err := authorizeCommunityMode(t.Context(), "my-community-server", "")
174176
require.Error(t, err)
175177
assert.Contains(t, err.Error(), "failed to create callback server")
176-
assert.Equal(t, "my-community-server", desktopCleanupCalled,
177-
"community authorize should clean stale Desktop entries before proceeding")
178+
assert.False(t, desktopCleanupCalled,
179+
"community authorize should NOT clean Desktop entries when flow fails before token storage")
178180
}

cmd/docker-mcp/oauth/cleanup.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/docker/mcp-gateway/cmd/docker-mcp/secret-management/secret"
77
"github.com/docker/mcp-gateway/pkg/desktop"
8+
"github.com/docker/mcp-gateway/pkg/log"
89
)
910

1011
// Function pointers for testability.
@@ -21,7 +22,9 @@ var (
2122
// (normal case for first-time authorizations or single-store workflows).
2223
func cleanStaleDesktopEntries(ctx context.Context, app string) {
2324
client := desktop.NewAuthClient()
24-
_ = client.DeleteOAuthApp(ctx, app)
25+
if err := client.DeleteOAuthApp(ctx, app); err != nil {
26+
log.Logf("Warning: failed to clean stale Desktop entry for %s: %v", app, err)
27+
}
2528
}
2629

2730
// cleanStaleDockerPassEntries removes OAuth token and DCR client entries
@@ -39,10 +42,14 @@ func cleanStaleDockerPassEntries(ctx context.Context, app string) {
3942
dcrKey := secret.GetDCRKey(app)
4043
for _, k := range keys {
4144
if k == oauthKey {
42-
_ = secret.DeleteOAuthToken(ctx, app)
45+
if err := secret.DeleteOAuthToken(ctx, app); err != nil {
46+
log.Logf("Warning: failed to clean stale docker pass OAuth token for %s: %v", app, err)
47+
}
4348
}
4449
if k == dcrKey {
45-
_ = secret.DeleteDCRClient(ctx, app)
50+
if err := secret.DeleteDCRClient(ctx, app); err != nil {
51+
log.Logf("Warning: failed to clean stale docker pass DCR client for %s: %v", app, err)
52+
}
4653
}
4754
}
4855
}

0 commit comments

Comments
 (0)