Summary
InitiateBrowserAuth in internal/utils/http_request/common/token.go has three issues that can break the auth login flow on the Web Experimentation side:
- Uses the global
http.DefaultServeMux via http.HandleFunc, which panics with http: multiple registrations for /auth/callback when the function is invoked more than once in the same process (e.g. re-login, tests, library use).
- The HTTP server is never proactively shut down after the callback is received; it keeps running until the 5-minute timeout goroutine fires, leaking a goroutine and a port binding.
log.Fatalf is called from goroutines for non-fatal conditions (normal shutdown returns http.ErrServerClosed; server.Shutdown may return a non-fatal error), which can kill the whole process unexpectedly.
Location
internal/utils/http_request/common/token.go:126-159
func InitiateBrowserAuth(username, clientID, clientSecret string) (models.TokenResponse, error) {
...
server := &http.Server{
Addr: "127.0.0.1:8010",
}
...
go func() {
http.HandleFunc("/auth/callback", func(w http.ResponseWriter, r *http.Request) {
handleCallback(w, r, codeChan)
})
if err := server.ListenAndServe(); err != nil {
log.Fatalf("Error starting callback server: %s", err)
}
}()
go func() {
select {
case <-time.After(5 * time.Minute):
if err := server.Shutdown(context.Background()); err != nil {
log.Fatalf("Server forced to shutdown: %s", err)
}
}
}()
code := <-codeChan
...
Expected behavior
Calling abtasty-cli auth login multiple times in the same session, or using InitiateBrowserAuth programmatically, should work reliably.
Actual behavior
- Second and subsequent calls panic with
http: multiple registrations for /auth/callback because http.HandleFunc registers on the global DefaultServeMux, which is shared and persistent across calls.
- Even on a single successful call, the bound port
127.0.0.1:8010 remains occupied until the 5-minute timer goroutine fires, preventing a quick re-login.
- When
server.Shutdown is eventually called, ListenAndServe returns http.ErrServerClosed. The current code treats this as fatal and calls log.Fatalf, terminating the process.
Suggested fix
Use a dedicated http.ServeMux per call, and stop treating normal shutdown as fatal:
mux := http.NewServeMux()
mux.HandleFunc("/auth/callback", func(w http.ResponseWriter, r *http.Request) {
handleCallback(w, r, codeChan)
})
server := &http.Server{
Addr: "127.0.0.1:8010",
Handler: mux,
}
serverErr := make(chan error, 1)
go func() {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
serverErr <- err
}
}()
// ... wait for code or timeout ...
// Always shut down the server before returning, not just on timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = server.Shutdown(ctx)
Additionally, replacing log.Fatalf calls inside goroutines with error returns (via a channel) would make the function safer to use programmatically.
Environment
- Version: v1.6.0
- Platform: macOS (Darwin)
- Context: observed while automating AB Tasty management operations via the public API; encountered the panic when retrying
auth login in the same session.
Reported by Yuya Suzuki from GAPRISE Inc. (official AB Tasty reseller in Japan).
Happy to provide more detail or test a fix if useful.
Summary
InitiateBrowserAuthininternal/utils/http_request/common/token.gohas three issues that can break theauth loginflow on the Web Experimentation side:http.DefaultServeMuxviahttp.HandleFunc, which panics withhttp: multiple registrations for /auth/callbackwhen the function is invoked more than once in the same process (e.g. re-login, tests, library use).log.Fatalfis called from goroutines for non-fatal conditions (normal shutdown returnshttp.ErrServerClosed;server.Shutdownmay return a non-fatal error), which can kill the whole process unexpectedly.Location
internal/utils/http_request/common/token.go:126-159Expected behavior
Calling
abtasty-cli auth loginmultiple times in the same session, or usingInitiateBrowserAuthprogrammatically, should work reliably.Actual behavior
http: multiple registrations for /auth/callbackbecausehttp.HandleFuncregisters on the globalDefaultServeMux, which is shared and persistent across calls.127.0.0.1:8010remains occupied until the 5-minute timer goroutine fires, preventing a quick re-login.server.Shutdownis eventually called,ListenAndServereturnshttp.ErrServerClosed. The current code treats this as fatal and callslog.Fatalf, terminating the process.Suggested fix
Use a dedicated
http.ServeMuxper call, and stop treating normal shutdown as fatal:Additionally, replacing
log.Fatalfcalls inside goroutines with error returns (via a channel) would make the function safer to use programmatically.Environment
auth loginin the same session.Reported by Yuya Suzuki from GAPRISE Inc. (official AB Tasty reseller in Japan).
Happy to provide more detail or test a fix if useful.