From a0a4ebb943ba28d2cf32067367f7fc33e48a99c7 Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Mon, 9 Mar 2026 11:08:19 +1300 Subject: [PATCH] Chore: Refactor API send authentication logic --- server/server.go | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/server/server.go b/server/server.go index 7a88244..46644ea 100644 --- a/server/server.go +++ b/server/server.go @@ -4,6 +4,7 @@ package server import ( "bytes" "compress/gzip" + "context" "fmt" "io" "net" @@ -36,6 +37,13 @@ var ( htmlPreviewRouteRe *regexp.Regexp ) +// skipUIAuthKey is a private context key used to signal that UI basic-auth +// should be bypassed for a specific request. This avoids mutating the global +// auth.UICredentials pointer (which is a data race under concurrent load). +type contextKey int + +const skipUIAuthKey contextKey = iota + // Listen will start the httpd func Listen() { setCORSOrigins() @@ -213,22 +221,20 @@ func basicAuthResponse(w http.ResponseWriter) { _, _ = w.Write([]byte("Unauthorized.\n")) } -// sendAPIAuthMiddleware handles authentication specifically for the send API endpoint -// It can use dedicated send API authentication or accept any credentials based on configuration +// sendAPIAuthMiddleware handles authentication specifically for the send API endpoint. +// It can use dedicated send API authentication or accept any credentials based on configuration. +// It communicates skip-UI-auth intent via request context rather than mutating the global +// auth.UICredentials pointer, which would be a data race under concurrent load. func sendAPIAuthMiddleware(fn http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - // If send API auth accept any is enabled, bypass all authentication + // If send API auth accept any is enabled, bypass all authentication. if config.SendAPIAuthAcceptAny { - // Temporarily disable UI auth for this request - originalCredentials := auth.UICredentials - auth.UICredentials = nil - defer func() { auth.UICredentials = originalCredentials }() - // Call the standard middleware - middleWareFunc(fn)(w, r) + ctx := context.WithValue(r.Context(), skipUIAuthKey, true) + middleWareFunc(fn)(w, r.WithContext(ctx)) return } - // If Send API credentials are configured, only accept those credentials + // If Send API credentials are configured, only accept those credentials. if auth.SendAPICredentials != nil { user, pass, ok := r.BasicAuth() @@ -242,15 +248,13 @@ func sendAPIAuthMiddleware(fn http.HandlerFunc) http.HandlerFunc { return } - // Valid Send API credentials - bypass UI auth and call function directly - originalCredentials := auth.UICredentials - auth.UICredentials = nil - defer func() { auth.UICredentials = originalCredentials }() - middleWareFunc(fn)(w, r) + // Valid Send API credentials — bypass UI auth via context flag. + ctx := context.WithValue(r.Context(), skipUIAuthKey, true) + middleWareFunc(fn)(w, r.WithContext(ctx)) return } - // No Send API credentials configured - fall back to UI auth + // No Send API credentials configured — fall back to UI auth. middleWareFunc(fn)(w, r) } } @@ -301,8 +305,11 @@ func middleWareFunc(fn http.HandlerFunc) http.HandlerFunc { // Check basic authentication headers if configured. // OPTIONS requests are skipped if CORS is enabled, since browsers omit credentials for preflight checks. + // skipUIAuthKey in the request context allows sendAPIAuthMiddleware to bypass UI auth + // for a specific request without touching the global auth.UICredentials pointer. + skipUIAuth, _ := r.Context().Value(skipUIAuthKey).(bool) isCORSOptionsRequest := AccessControlAllowOrigin != "" && r.Method == http.MethodOptions - if !isCORSOptionsRequest && auth.UICredentials != nil { + if !skipUIAuth && !isCORSOptionsRequest && auth.UICredentials != nil { user, pass, ok := r.BasicAuth() if !ok {