Skip to content

Commit 9b8c0d4

Browse files
saucowclaude
andcommitted
Address PR feedback: handle OAuth secrets in fallback path
Refactored into separate functions for clarity: - buildFallbackURIs: generates se:// URIs for all declared secrets when the secrets engine is unreachable (OAuth preferred when configured) - buildVerifiedURIs: generates se:// URIs only for secrets that exist in the store (OAuth checked first, then direct secret) - oauthMapping: shared helper for OAuth provider lookup When GetSecrets() fails (e.g. MSIX sandbox on Windows), the fallback generates URIs for everything and lets Docker Desktop resolve at runtime. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 599c8b8 commit 9b8c0d4

File tree

1 file changed

+51
-47
lines changed

1 file changed

+51
-47
lines changed

pkg/gateway/secrets_uri.go

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,85 +11,89 @@ import (
1111
// ServerSecretConfig contains the secret definitions and OAuth config for a server.
1212
type ServerSecretConfig struct {
1313
Secrets []catalog.Secret // Secret definitions from server catalog
14-
OAuth *catalog.OAuth // OAuth config (if present, OAuth takes priority over PAT)
14+
OAuth *catalog.OAuth // OAuth config (if present, OAuth takes priority over direct secret)
1515
Namespace string // Prefix for map keys (used by WorkingSet for namespacing)
1616
}
1717

1818
// BuildSecretsURIs generates se:// URIs for secrets with OAuth priority handling.
1919
//
20-
// For servers WITH OAuth configured:
21-
// - Check if OAuth token exists (docker/mcp/oauth/{provider})
22-
// - If yes, use OAuth URI
23-
// - If no, fall back to PAT URI (docker/mcp/{secret_name})
20+
// When the secrets engine is reachable, URIs are only generated for secrets that
21+
// actually exist in the store (OAuth token checked first, then direct secret).
2422
//
25-
// For servers WITHOUT OAuth:
26-
// - Use PAT URI directly if secret exists
27-
//
28-
// Docker Desktop resolves se:// URIs at container runtime.
29-
// Remote servers fetch actual values via remote.go.
23+
// When the secrets engine is unreachable (e.g. MSIX-sandboxed clients on Windows
24+
// cannot follow AF_UNIX reparse points), URIs are generated for all declared secrets
25+
// since we cannot check existence. Docker Desktop resolves se:// URIs at container
26+
// runtime via named pipes, which are unaffected by MSIX restrictions.
3027
func BuildSecretsURIs(ctx context.Context, configs []ServerSecretConfig) map[string]string {
31-
// secretNameToURI maps secret names to their se:// URIs
32-
// Key: secret name (e.g., "github.token" or "default_github.token" with namespace)
33-
// Value: se:// URI (e.g., "se://docker/mcp/github.token")
34-
secretNameToURI := make(map[string]string)
35-
36-
// availableSecrets maps secret IDs to their values (used to check existence)
3728
allSecrets, err := secret.GetSecrets(ctx)
3829
if err != nil {
3930
log.Logf("Warning: Failed to fetch secrets from secrets engine: %v", err)
40-
// Fallback: generate se:// URIs for all declared secrets without checking existence.
41-
// Docker Desktop resolves se:// URIs at container runtime via named pipes,
42-
// which works even when the secrets engine Unix socket is unreachable
43-
// (e.g. MSIX-sandboxed clients on Windows cannot follow AF_UNIX reparse points).
44-
for _, cfg := range configs {
45-
for _, s := range cfg.Secrets {
46-
secretID := secret.GetDefaultSecretKey(s.Name)
47-
secretName := cfg.Namespace + s.Name
48-
secretNameToURI[secretName] = "se://" + secretID
49-
}
50-
}
51-
return secretNameToURI
31+
return buildFallbackURIs(configs)
5232
}
33+
5334
availableSecrets := make(map[string]string)
5435
for _, envelope := range allSecrets {
5536
availableSecrets[envelope.ID] = string(envelope.Value)
5637
}
38+
return buildVerifiedURIs(configs, availableSecrets)
39+
}
40+
41+
// buildFallbackURIs generates se:// URIs for all declared secrets without checking
42+
// existence. Used when the secrets engine is unreachable. OAuth URIs are preferred
43+
// when configured (matching the normal priority order).
44+
func buildFallbackURIs(configs []ServerSecretConfig) map[string]string {
45+
secretNameToURI := make(map[string]string)
5746

5847
for _, cfg := range configs {
59-
// Server has no OAuth configured - use secret directly
60-
if cfg.OAuth == nil {
61-
for _, s := range cfg.Secrets {
62-
secretID := secret.GetDefaultSecretKey(s.Name)
63-
if availableSecrets[secretID] != "" {
64-
secretName := cfg.Namespace + s.Name
65-
secretNameToURI[secretName] = "se://" + secretID
66-
}
48+
secretToOAuthID := oauthMapping(cfg)
49+
50+
for _, s := range cfg.Secrets {
51+
secretName := cfg.Namespace + s.Name
52+
if oauthSecretID, ok := secretToOAuthID[s.Name]; ok {
53+
secretNameToURI[secretName] = "se://" + oauthSecretID
54+
} else {
55+
secretNameToURI[secretName] = "se://" + secret.GetDefaultSecretKey(s.Name)
6756
}
68-
continue
6957
}
58+
}
59+
return secretNameToURI
60+
}
7061

71-
// Build mapping from secret name to OAuth storage key
72-
secretToOAuthID := make(map[string]string)
73-
for _, p := range cfg.OAuth.Providers {
74-
secretToOAuthID[p.Secret] = secret.GetOAuthKey(p.Provider)
75-
}
62+
// buildVerifiedURIs generates se:// URIs only for secrets that exist in the store.
63+
// For servers with OAuth, checks OAuth token first, then falls back to direct secret.
64+
func buildVerifiedURIs(configs []ServerSecretConfig, availableSecrets map[string]string) map[string]string {
65+
secretNameToURI := make(map[string]string)
66+
67+
for _, cfg := range configs {
68+
secretToOAuthID := oauthMapping(cfg)
7669

7770
for _, s := range cfg.Secrets {
7871
secretName := cfg.Namespace + s.Name
7972

80-
// Try OAuth first
73+
// Try OAuth token first
8174
if oauthSecretID, ok := secretToOAuthID[s.Name]; ok {
8275
if availableSecrets[oauthSecretID] != "" {
8376
secretNameToURI[secretName] = "se://" + oauthSecretID
8477
continue
8578
}
8679
}
87-
// Fall back to PAT if it exists
88-
patSecretID := secret.GetDefaultSecretKey(s.Name)
89-
if availableSecrets[patSecretID] != "" {
90-
secretNameToURI[secretName] = "se://" + patSecretID
80+
// Fall back to direct secret (API keys, tokens, etc.)
81+
secretID := secret.GetDefaultSecretKey(s.Name)
82+
if availableSecrets[secretID] != "" {
83+
secretNameToURI[secretName] = "se://" + secretID
9184
}
9285
}
9386
}
9487
return secretNameToURI
9588
}
89+
90+
// oauthMapping builds a map from secret name to OAuth storage key for a server config.
91+
func oauthMapping(cfg ServerSecretConfig) map[string]string {
92+
m := make(map[string]string)
93+
if cfg.OAuth != nil {
94+
for _, p := range cfg.OAuth.Providers {
95+
m[p.Secret] = secret.GetOAuthKey(p.Provider)
96+
}
97+
}
98+
return m
99+
}

0 commit comments

Comments
 (0)