Skip to content

Set http.auth.user variables even on unsuccessful authentication #7684

@Samasaur1

Description

@Samasaur1

Issue Details

I was just writing an authentication plugin for a method of authentication with the property that all requests are authenticated (ie matched to users), but only some are authorized. The actual authentication plugin is working great, but while attempting to serve a custom error page that displayed the user's name, I discovered that the http.auth.user.id placeholder (and custom placeholders derived from user metadata) aren't set if an Authenticator returns false for the second parameter.

I'd like Caddy to set these http.auth.user.* properties before returning HTTP 401, which will allow reading these if present in an error handler. I think this would be achieved by reordering some lines in the following function:

func (a Authentication) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
var user User
var authed bool
var err error
for provName, prov := range a.Providers {
user, authed, err = prov.Authenticate(w, r)
if err != nil {
if c := a.logger.Check(zapcore.ErrorLevel, "auth provider returned error"); c != nil {
c.Write(zap.String("provider", provName), zap.Error(err))
}
// Set the error from the authentication provider in a placeholder,
// so it can be used in the handle_errors directive.
repl.Set("http.auth."+provName+".error", err.Error())
continue
}
if authed {
break
}
}
if !authed {
return caddyhttp.Error(http.StatusUnauthorized, fmt.Errorf("not authenticated"))
}
repl.Set("http.auth.user.id", user.ID)
for k, v := range user.Metadata {
repl.Set("http.auth.user."+k, v)
}
return next.ServeHTTP(w, r)
}

Specifically, the following block:

repl.Set("http.auth.user.id", user.ID)
for k, v := range user.Metadata {
	repl.Set("http.auth.user."+k, v)
}

should move between the for loop and the if !authed block (and perhaps be wrapped in a check to ensure user.ID != "").

I believe this is a fully backwards-compatible change, since we'd simply be setting extra placeholders in the unauthorized case.

Assistance Disclosure

AI not used

If AI was used, describe the extent to which it was used.

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug 🐞Something isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions