Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 12:15 UTC
caddyauth: set user placeholders before auth rejection (#7685)
* caddyauth: set user placeholders before auth rejection * docs: update auth placeholder comment
Rayan Salhab committed
May 3, 2026 at 06:40 UTC
7e77eec0ae824d86f71f410acd78a0f938baeda7
2 files changed
+90
-7
modules/caddyhttp/caddyauth/caddyauth.go
+8
-7
@@ -32,10 +32,10 @@ func init() {
32
// Authentication is a middleware which provides user authentication.
33
// Rejects requests with HTTP 401 if the request is not authenticated.
34
//
35
-// After a successful authentication, the placeholder
35
+// When an authentication provider returns user information, the placeholder
36
// `{http.auth.user.id}` will be set to the username, and also
37
// `{http.auth.user.*}` placeholders may be set for any authentication
38
-// modules that provide user metadata.
38
+// modules that provide user metadata, even if authentication is rejected.
39
//
40
// In case of an error, the placeholder `{http.auth.<provider>.error}`
41
// will be set to the error message returned by the authentication
@@ -91,6 +91,12 @@ func (a Authentication) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
91
repl.Set("http.auth."+provName+".error", err.Error())
92
continue
93
}
94
+ if authed || user.ID != "" || len(user.Metadata) > 0 {
95
+ repl.Set("http.auth.user.id", user.ID)
96
+ for k, v := range user.Metadata {
97
+ repl.Set("http.auth.user."+k, v)
98
+ }
99
+ }
100
if authed {
101
break
102
}
@@ -99,11 +105,6 @@ func (a Authentication) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
105
return caddyhttp.Error(http.StatusUnauthorized, fmt.Errorf("not authenticated"))
106
}
107
102
- repl.Set("http.auth.user.id", user.ID)
103
- for k, v := range user.Metadata {
104
- repl.Set("http.auth.user."+k, v)
105
- }
106
-
108
return next.ServeHTTP(w, r)
109
}
110
modules/caddyhttp/caddyauth/caddyauth_test.go
new
+82
@@ -0,0 +1,82 @@
1
+// Copyright 2015 Matthew Holt and The Caddy Authors
2
+//
3
+// Licensed under the Apache License, Version 2.0 (the "License");
4
+// you may not use this file except in compliance with the License.
5
+// You may obtain a copy of the License at
6
+//
7
+// http://www.apache.org/licenses/LICENSE-2.0
8
+//
9
+// Unless required by applicable law or agreed to in writing, software
10
+// distributed under the License is distributed on an "AS IS" BASIS,
11
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+// See the License for the specific language governing permissions and
13
+// limitations under the License.
14
+
15
+package caddyauth
16
+
17
+import (
18
+ "context"
19
+ "errors"
20
+ "net/http"
21
+ "net/http/httptest"
22
+ "testing"
23
+
24
+ "github.com/caddyserver/caddy/v2"
25
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp"
26
+)
27
+
28
+func TestAuthenticationSetsUserPlaceholdersOnUnauthorized(t *testing.T) {
29
+ repl := caddy.NewReplacer()
30
+ req := httptest.NewRequest(http.MethodGet, "/", nil)
31
+ req = req.WithContext(context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl))
32
+
33
+ a := Authentication{
34
+ Providers: map[string]Authenticator{
35
+ "test": staticAuthenticator{
36
+ user: User{
37
+ ID: "alice",
38
+ Metadata: map[string]string{
39
+ "role": "admin",
40
+ },
41
+ },
42
+ },
43
+ },
44
+ }
45
+
46
+ nextCalled := false
47
+ err := a.ServeHTTP(httptest.NewRecorder(), req, caddyhttp.HandlerFunc(func(http.ResponseWriter, *http.Request) error {
48
+ nextCalled = true
49
+ return nil
50
+ }))
51
+ if err == nil {
52
+ t.Fatal("expected unauthorized error")
53
+ }
54
+
55
+ var handlerErr caddyhttp.HandlerError
56
+ if !errors.As(err, &handlerErr) {
57
+ t.Fatalf("expected caddyhttp.HandlerError, got %T", err)
58
+ }
59
+ if handlerErr.StatusCode != http.StatusUnauthorized {
60
+ t.Fatalf("expected status %d, got %d", http.StatusUnauthorized, handlerErr.StatusCode)
61
+ }
62
+ if nextCalled {
63
+ t.Fatal("next handler was called")
64
+ }
65
+
66
+ if got, ok := repl.GetString("http.auth.user.id"); !ok || got != "alice" {
67
+ t.Fatalf("expected http.auth.user.id to be alice, got %q (ok=%v)", got, ok)
68
+ }
69
+ if got, ok := repl.GetString("http.auth.user.role"); !ok || got != "admin" {
70
+ t.Fatalf("expected http.auth.user.role to be admin, got %q (ok=%v)", got, ok)
71
+ }
72
+}
73
+
74
+type staticAuthenticator struct {
75
+ user User
76
+ authed bool
77
+ err error
78
+}
79
+
80
+func (s staticAuthenticator) Authenticate(http.ResponseWriter, *http.Request) (User, bool, error) {
81
+ return s.user, s.authed, s.err
82
+}