diff --git a/internal/api/handlers/management/auth_files.go b/internal/api/handlers/management/auth_files.go index dcff98d7..e0a16377 100644 --- a/internal/api/handlers/management/auth_files.go +++ b/internal/api/handlers/management/auth_files.go @@ -13,6 +13,7 @@ import ( "net/http" "os" "path/filepath" + "runtime" "sort" "strconv" "strings" @@ -692,17 +693,20 @@ func (h *Handler) authIDForPath(path string) string { if path == "" { return "" } - if h == nil || h.cfg == nil { - return path + id := path + if h != nil && h.cfg != nil { + authDir := strings.TrimSpace(h.cfg.AuthDir) + if authDir != "" { + if rel, errRel := filepath.Rel(authDir, path); errRel == nil && rel != "" { + id = rel + } + } } - authDir := strings.TrimSpace(h.cfg.AuthDir) - if authDir == "" { - return path + // On Windows, normalize ID casing to avoid duplicate auth entries caused by case-insensitive paths. + if runtime.GOOS == "windows" { + id = strings.ToLower(id) } - if rel, err := filepath.Rel(authDir, path); err == nil && rel != "" { - return rel - } - return path + return id } func (h *Handler) registerAuthFromFile(ctx context.Context, path string, data []byte) error { diff --git a/internal/watcher/synthesizer/file.go b/internal/watcher/synthesizer/file.go index 4e053117..ea96118b 100644 --- a/internal/watcher/synthesizer/file.go +++ b/internal/watcher/synthesizer/file.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "runtime" "strconv" "strings" "time" @@ -72,6 +73,10 @@ func (s *FileSynthesizer) Synthesize(ctx *SynthesisContext) ([]*coreauth.Auth, e if rel, errRel := filepath.Rel(ctx.AuthDir, full); errRel == nil && rel != "" { id = rel } + // On Windows, normalize ID casing to avoid duplicate auth entries caused by case-insensitive paths. + if runtime.GOOS == "windows" { + id = strings.ToLower(id) + } proxyURL := "" if p, ok := metadata["proxy_url"].(string); ok { diff --git a/sdk/auth/filestore.go b/sdk/auth/filestore.go index c424a89b..987d305e 100644 --- a/sdk/auth/filestore.go +++ b/sdk/auth/filestore.go @@ -10,6 +10,7 @@ import ( "net/url" "os" "path/filepath" + "runtime" "strings" "sync" "time" @@ -257,14 +258,17 @@ func (s *FileTokenStore) readAuthFile(path, baseDir string) (*cliproxyauth.Auth, } func (s *FileTokenStore) idFor(path, baseDir string) string { - if baseDir == "" { - return path + id := path + if baseDir != "" { + if rel, errRel := filepath.Rel(baseDir, path); errRel == nil && rel != "" { + id = rel + } } - rel, err := filepath.Rel(baseDir, path) - if err != nil { - return path + // On Windows, normalize ID casing to avoid duplicate auth entries caused by case-insensitive paths. + if runtime.GOOS == "windows" { + id = strings.ToLower(id) } - return rel + return id } func (s *FileTokenStore) resolveAuthPath(auth *cliproxyauth.Auth) (string, error) {