Skip to content

Commit 47f47ae

Browse files
authored
🧹 chore: Enhance BasicAuth middleware to better comply with RFC 6750 (#3484)
* test(basicauth): ensure whitespace tolerant * use utils.TrimSpace * Update docs
1 parent 804a2b9 commit 47f47ae

File tree

5 files changed

+65
-5
lines changed

5 files changed

+65
-5
lines changed

docs/middleware/basicauth.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ id: basicauth
66

77
Basic Authentication middleware for [Fiber](https://github.com/gofiber/fiber) that provides an HTTP basic authentication. It calls the next handler for valid credentials and [401 Unauthorized](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401) or a custom response for missing or invalid credentials.
88

9+
The default unauthorized response includes the header `WWW-Authenticate: Basic realm="Restricted"`.
10+
911
## Signatures
1012

1113
```go

docs/whats_new.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Here's a quick overview of the changes in Fiber `v3`:
2929
- [🧬 Middlewares](#-middlewares)
3030
- [Important Change for Accessing Middleware Data](#important-change-for-accessing-middleware-data)
3131
- [Adaptor](#adaptor)
32+
- [BasicAuth](#basicauth)
3233
- [Cache](#cache)
3334
- [CORS](#cors)
3435
- [CSRF](#csrf)
@@ -968,6 +969,10 @@ The adaptor middleware has been significantly optimized for performance and effi
968969
| | Memory Usage | 2734 B/op | 298 B/op | -89.10% |
969970
| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% |
970971

972+
### BasicAuth
973+
974+
The BasicAuth middleware was updated for improved robustness in parsing the Authorization header, with enhanced validation and whitespace handling. The default unauthorized response now uses a properly quoted and capitalized `WWW-Authenticate` header.
975+
971976
### Cache
972977

973978
We are excited to introduce a new option in our caching middleware: Cache Invalidator. This feature provides greater control over cache management, allowing you to define a custom conditions for invalidating cache entries.

middleware/basicauth/basicauth.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,16 @@ func New(config Config) fiber.Handler {
3131
}
3232

3333
// Get authorization header
34-
auth := c.Get(fiber.HeaderAuthorization)
34+
auth := utils.Trim(c.Get(fiber.HeaderAuthorization), ' ')
3535

36-
// Check if the header contains content besides "basic".
37-
if len(auth) <= 6 || !utils.EqualFold(auth[:6], "basic ") {
36+
// Expect a scheme token followed by credentials
37+
parts := strings.Fields(auth)
38+
if len(parts) != 2 || !utils.EqualFold(parts[0], "basic") {
3839
return cfg.Unauthorized(c)
3940
}
4041

4142
// Decode the header contents
42-
raw, err := base64.StdEncoding.DecodeString(auth[6:])
43+
raw, err := base64.StdEncoding.DecodeString(parts[1])
4344
if err != nil {
4445
return cfg.Unauthorized(c)
4546
}

middleware/basicauth/basicauth_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,57 @@ func Test_Middleware_BasicAuth(t *testing.T) {
9191
}
9292
}
9393

94+
func Test_BasicAuth_WWWAuthenticateHeader(t *testing.T) {
95+
t.Parallel()
96+
app := fiber.New()
97+
98+
app.Use(New(Config{Users: map[string]string{"john": "doe"}}))
99+
100+
resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
101+
require.NoError(t, err)
102+
require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode)
103+
require.Equal(t, `Basic realm="Restricted"`, resp.Header.Get(fiber.HeaderWWWAuthenticate))
104+
}
105+
106+
func Test_BasicAuth_InvalidHeader(t *testing.T) {
107+
t.Parallel()
108+
app := fiber.New()
109+
110+
app.Use(New(Config{Users: map[string]string{"john": "doe"}}))
111+
112+
req := httptest.NewRequest(fiber.MethodGet, "/", nil)
113+
req.Header.Set(fiber.HeaderAuthorization, "Basic notbase64")
114+
resp, err := app.Test(req)
115+
116+
require.NoError(t, err)
117+
require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode)
118+
}
119+
120+
func Test_BasicAuth_WhitespaceHandling(t *testing.T) {
121+
t.Parallel()
122+
app := fiber.New()
123+
124+
app.Use(New(Config{Users: map[string]string{"john": "doe"}}))
125+
app.Get("/", func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusTeapot) })
126+
127+
creds := base64.StdEncoding.EncodeToString([]byte("john:doe"))
128+
129+
cases := []string{
130+
"Basic " + creds,
131+
" Basic \t" + creds,
132+
"Basic " + creds + " ",
133+
}
134+
135+
for _, h := range cases {
136+
req := httptest.NewRequest(fiber.MethodGet, "/", nil)
137+
req.Header.Set(fiber.HeaderAuthorization, h)
138+
resp, err := app.Test(req)
139+
140+
require.NoError(t, err)
141+
require.Equal(t, fiber.StatusTeapot, resp.StatusCode)
142+
}
143+
}
144+
94145
// go test -v -run=^$ -bench=Benchmark_Middleware_BasicAuth -benchmem -count=4
95146
func Benchmark_Middleware_BasicAuth(b *testing.B) {
96147
app := fiber.New()

middleware/basicauth/config.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package basicauth
22

33
import (
44
"crypto/subtle"
5+
"strconv"
56

67
"github.com/gofiber/fiber/v3"
78
"github.com/gofiber/utils/v2"
@@ -79,7 +80,7 @@ func configDefault(config ...Config) Config {
7980
}
8081
if cfg.Unauthorized == nil {
8182
cfg.Unauthorized = func(c fiber.Ctx) error {
82-
c.Set(fiber.HeaderWWWAuthenticate, "basic realm="+cfg.Realm)
83+
c.Set(fiber.HeaderWWWAuthenticate, "Basic realm="+strconv.Quote(cfg.Realm))
8384
return c.SendStatus(fiber.StatusUnauthorized)
8485
}
8586
}

0 commit comments

Comments
 (0)