feat: rename "reverse proxy authentication" to "external authentication" (#4418)

* Rename external auth options

ReverseProxyWhitelist was regularly confusing users that enabled it for
non-authenticating reverse proxy setups.

The new option name makes it clear that it's related to authentication, not
just reverse proxies.

* small refactor

Signed-off-by: Deluan <deluan@navidrome.org>

* add test

Signed-off-by: Deluan <deluan@navidrome.org>

---------

Signed-off-by: Deluan <deluan@navidrome.org>
Co-authored-by: Deluan Quintão <deluan@navidrome.org>
This commit is contained in:
crazygolem
2025-12-02 18:01:48 +01:00
committed by GitHub
parent 654607ea53
commit 917726c166
13 changed files with 51 additions and 27 deletions
+9 -9
View File
@@ -193,24 +193,24 @@ func UsernameFromToken(r *http.Request) string {
return token.Subject()
}
func UsernameFromReverseProxyHeader(r *http.Request) string {
if conf.Server.ReverseProxyWhitelist == "" {
func UsernameFromExtAuthHeader(r *http.Request) string {
if conf.Server.ExtAuth.TrustedSources == "" {
return ""
}
reverseProxyIp, ok := request.ReverseProxyIpFrom(r.Context())
if !ok {
log.Error("ReverseProxyWhitelist enabled but no proxy IP found in request context. Please report this error.")
log.Error("ExtAuth enabled but no proxy IP found in request context. Please report this error.")
return ""
}
if !validateIPAgainstList(reverseProxyIp, conf.Server.ReverseProxyWhitelist) {
log.Warn(r.Context(), "IP is not whitelisted for reverse proxy login", "proxy-ip", reverseProxyIp, "client-ip", r.RemoteAddr)
if !validateIPAgainstList(reverseProxyIp, conf.Server.ExtAuth.TrustedSources) {
log.Warn(r.Context(), "IP is not whitelisted for external authentication", "proxy-ip", reverseProxyIp, "client-ip", r.RemoteAddr)
return ""
}
username := r.Header.Get(conf.Server.ReverseProxyUserHeader)
username := r.Header.Get(conf.Server.ExtAuth.UserHeader)
if username == "" {
return ""
}
log.Trace(r, "Found username in ReverseProxyUserHeader", "username", username)
log.Trace(r, "Found username in ExtAuth.UserHeader", "username", username)
return username
}
@@ -256,7 +256,7 @@ func authenticateRequest(ds model.DataStore, r *http.Request, findUsernameFns ..
func Authenticator(ds model.DataStore) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx, err := authenticateRequest(ds, r, UsernameFromConfig, UsernameFromToken, UsernameFromReverseProxyHeader)
ctx, err := authenticateRequest(ds, r, UsernameFromConfig, UsernameFromToken, UsernameFromExtAuthHeader)
if err != nil {
_ = rest.RespondWithError(w, http.StatusUnauthorized, "Not authenticated")
return
@@ -291,7 +291,7 @@ func JWTRefresher(next http.Handler) http.Handler {
func handleLoginFromHeaders(ds model.DataStore, r *http.Request) map[string]interface{} {
username := UsernameFromConfig(r)
if username == "" {
username = UsernameFromReverseProxyHeader(r)
username = UsernameFromExtAuthHeader(r)
if username == "" {
return nil
}
+5 -5
View File
@@ -80,7 +80,7 @@ var _ = Describe("Auth", func() {
req.Header.Add("Remote-User", "janedoe")
resp = httptest.NewRecorder()
conf.Server.UILoginBackgroundURL = ""
conf.Server.ReverseProxyWhitelist = "192.168.0.0/16,2001:4860:4860::/48"
conf.Server.ExtAuth.TrustedSources = "192.168.0.0/16,2001:4860:4860::/48"
})
It("sets auth data if IPv4 matches whitelist", func() {
@@ -155,7 +155,7 @@ var _ = Describe("Auth", func() {
It("does not set auth data when listening on unix socket without whitelist", func() {
conf.Server.Address = "unix:/tmp/navidrome-test"
conf.Server.ReverseProxyWhitelist = ""
conf.Server.ExtAuth.TrustedSources = ""
// No ReverseProxyIp in request context
serveIndex(ds, fs, nil)(resp, req)
@@ -176,7 +176,7 @@ var _ = Describe("Auth", func() {
It("sets auth data when listening on unix socket with correct whitelist", func() {
conf.Server.Address = "unix:/tmp/navidrome-test"
conf.Server.ReverseProxyWhitelist = conf.Server.ReverseProxyWhitelist + ",@"
conf.Server.ExtAuth.TrustedSources = conf.Server.ExtAuth.TrustedSources + ",@"
req = req.WithContext(request.WithReverseProxyIp(req.Context(), "@"))
serveIndex(ds, fs, nil)(resp, req)
@@ -302,8 +302,8 @@ var _ = Describe("Auth", func() {
ds = &tests.MockDataStore{}
req = httptest.NewRequest("GET", "/", nil)
req = req.WithContext(request.WithReverseProxyIp(req.Context(), trustedIP))
conf.Server.ReverseProxyWhitelist = "192.168.0.0/16"
conf.Server.ReverseProxyUserHeader = "Remote-User"
conf.Server.ExtAuth.TrustedSources = "192.168.0.0/16"
conf.Server.ExtAuth.UserHeader = "Remote-User"
})
It("makes the first user an admin", func() {
+1 -1
View File
@@ -168,7 +168,7 @@ func clientUniqueIDMiddleware(next http.Handler) http.Handler {
// realIPMiddleware applies middleware.RealIP, and additionally saves the request's original RemoteAddr to the request's
// context if navidrome is behind a trusted reverse proxy.
func realIPMiddleware(next http.Handler) http.Handler {
if conf.Server.ReverseProxyWhitelist != "" {
if conf.Server.ExtAuth.TrustedSources != "" {
return chi.Chain(
reqToCtx(request.ReverseProxyIp, func(r *http.Request) any { return r.RemoteAddr }),
middleware.RealIP,
+1 -1
View File
@@ -56,7 +56,7 @@ func fromInternalOrProxyAuth(r *http.Request) (string, bool) {
return username, true
}
return server.UsernameFromReverseProxyHeader(r), false
return server.UsernameFromExtAuthHeader(r), false
}
func checkRequiredParameters(next http.Handler) http.Handler {
+4 -4
View File
@@ -95,8 +95,8 @@ var _ = Describe("Middlewares", func() {
})
It("passes when all required params are available (reverse-proxy case)", func() {
conf.Server.ReverseProxyWhitelist = "127.0.0.234/32"
conf.Server.ReverseProxyUserHeader = "Remote-User"
conf.Server.ExtAuth.TrustedSources = "127.0.0.234/32"
conf.Server.ExtAuth.UserHeader = "Remote-User"
r := newGetRequest("v=1.15", "c=test")
r.Header.Add("Remote-User", "user")
@@ -254,8 +254,8 @@ var _ = Describe("Middlewares", func() {
When("using reverse proxy authentication", func() {
BeforeEach(func() {
DeferCleanup(configtest.SetupConfig())
conf.Server.ReverseProxyWhitelist = "192.168.1.1/24"
conf.Server.ReverseProxyUserHeader = "Remote-User"
conf.Server.ExtAuth.TrustedSources = "192.168.1.1/24"
conf.Server.ExtAuth.UserHeader = "Remote-User"
})
It("passes authentication with correct IP and header", func() {