From 6baab3bdd664a0ae6c24c0f447b605f323124293 Mon Sep 17 00:00:00 2001 From: Sarthak Date: Sun, 4 Oct 2026 15:07:24 +0530 Subject: [PATCH] fix: use a private ServeMux for the SSO login callback server Signed-off-by: Sarthak --- cmd/login.go | 7 ++++++- cmd/logout_test.go | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/cmd/login.go b/cmd/login.go index 896aa1d9..4129d0d9 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -304,12 +304,17 @@ func oauth2login( completionChan <- "" } + // Use a private mux so that several logins in the same process do not + // register the callback twice on the global http.DefaultServeMux. + mux := http.NewServeMux() + mux.HandleFunc("/auth/callback", callbackHandler) + // G112: Set ReadHeaderTimeout to mitigate Slowloris DoS attack. srv := &http.Server{ Addr: "localhost:" + strconv.Itoa(port), + Handler: mux, ReadHeaderTimeout: 3 * time.Second, } - http.HandleFunc("/auth/callback", callbackHandler) var url string opts := []oauth2.AuthCodeOption{} diff --git a/cmd/logout_test.go b/cmd/logout_test.go index d21a74ad..0200aee3 100644 --- a/cmd/logout_test.go +++ b/cmd/logout_test.go @@ -17,13 +17,64 @@ package cmd import ( + "context" + "fmt" + "net" + "net/http" "path/filepath" "testing" + "time" "github.com/microcks/microcks-cli/pkg/config" "github.com/stretchr/testify/require" + "golang.org/x/oauth2" ) +func runOAuth2LoginWithError(t *testing.T) { + t.Helper() + l, err := net.Listen("tcp", "localhost:0") + require.NoError(t, err) + port := l.Addr().(*net.TCPAddr).Port + require.NoError(t, l.Close()) + + oauth2conf := &oauth2.Config{ + Endpoint: oauth2.Endpoint{ + AuthURL: "http://127.0.0.1/auth", + TokenURL: "http://127.0.0.1/token", + }, + } + + done := make(chan error, 1) + go func() { + _, _, err := oauth2login(context.Background(), port, oauth2conf, false) + done <- err + }() + + // Send an error callback so the login flow ends. + callbackURL := fmt.Sprintf("http://localhost:%d/auth/callback?error=access_denied", port) + require.Eventually(t, func() bool { + resp, err := http.Get(callbackURL) // #nosec G107 -- local test server + if err != nil { + return false + } + resp.Body.Close() + return true + }, 10*time.Second, 100*time.Millisecond) + + select { + case err := <-done: + require.Error(t, err) + case <-time.After(10 * time.Second): + t.Fatal("oauth2login did not return") + } +} + +func TestOAuth2LoginCanRunTwice(t *testing.T) { + runOAuth2LoginWithError(t) + // The second login must not panic on a duplicate callback registration. + runOAuth2LoginWithError(t) +} + func TestLogoutContextResolvesNamedContextUser(t *testing.T) { configPath := filepath.Join(t.TempDir(), "config") server := "https://microcks.example"