feat(auth): document caller header precedence + warn on conflict (#10)

CallerMiddleware silently preferred X-Auth-Request-User over X-Forwarded-User
with no explanation and no signal when both were set. Documented the precedence
(X-Auth-Request-User is the verified OIDC identity oauth2-proxy sets, so it is
authoritative; X-Forwarded-User is a fallback), and it now takes a *slog.Logger
and warns when both headers are present and disagree, so a proxy
misconfiguration is visible instead of silently resolved. Table-driven tests
cover precedence (both/single/none) and the conflict-warning path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-03 22:57:24 +02:00
co-authored by Claude Opus 4.8
parent 4c1f36f9eb
commit 9b7f53bdda
4 changed files with 92 additions and 15 deletions
+26 -3
View File
@@ -2,17 +2,40 @@ package auth
import (
"context"
"log/slog"
"net/http"
)
type ctxKey struct{}
func CallerMiddleware(next http.Handler) http.Handler {
// CallerMiddleware extracts the authenticated username from the reverse-proxy
// identity headers and stashes it in the request context for Caller().
//
// Header precedence: X-Auth-Request-User takes priority over X-Forwarded-User.
// X-Auth-Request-User is the header oauth2-proxy sets from the *verified* OIDC
// identity, so it is authoritative. X-Forwarded-User is a weaker, proxy-set
// convention some setups populate instead; it is used only as a fallback when
// X-Auth-Request-User is absent. If a proxy sets BOTH and they disagree, the
// verified X-Auth-Request-User still wins and we log a warning so the
// misconfiguration is visible rather than silently resolved (#10).
//
// logger may be nil, in which case the conflict warning is skipped.
func CallerMiddleware(logger *slog.Logger, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := r.Header.Get("X-Auth-Request-User")
authUser := r.Header.Get("X-Auth-Request-User")
fwdUser := r.Header.Get("X-Forwarded-User")
user := authUser
if user == "" {
user = r.Header.Get("X-Forwarded-User")
user = fwdUser
}
if logger != nil && authUser != "" && fwdUser != "" && authUser != fwdUser {
logger.Warn("conflicting caller identity headers; using X-Auth-Request-User",
"x_auth_request_user", authUser,
"x_forwarded_user", fwdUser)
}
ctx := context.WithValue(r.Context(), ctxKey{}, user)
next.ServeHTTP(w, r.WithContext(ctx))
})