Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/middleware/basicauth.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ id: basicauth

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.

The default unauthorized response includes the header `WWW-Authenticate: Basic realm="Restricted"`.

## Signatures

```go
Expand Down
5 changes: 5 additions & 0 deletions docs/whats_new.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Here's a quick overview of the changes in Fiber `v3`:
- [🧬 Middlewares](#-middlewares)
- [Important Change for Accessing Middleware Data](#important-change-for-accessing-middleware-data)
- [Adaptor](#adaptor)
- [BasicAuth](#basicauth)
- [Cache](#cache)
- [CORS](#cors)
- [CSRF](#csrf)
Expand Down Expand Up @@ -968,6 +969,10 @@ The adaptor middleware has been significantly optimized for performance and effi
| | Memory Usage | 2734 B/op | 298 B/op | -89.10% |
| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% |

### BasicAuth

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.

### Cache

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.
Expand Down
9 changes: 5 additions & 4 deletions middleware/basicauth/basicauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,16 @@ func New(config Config) fiber.Handler {
}

// Get authorization header
auth := c.Get(fiber.HeaderAuthorization)
auth := utils.Trim(c.Get(fiber.HeaderAuthorization), ' ')

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

// Decode the header contents
raw, err := base64.StdEncoding.DecodeString(auth[6:])
raw, err := base64.StdEncoding.DecodeString(parts[1])
if err != nil {
return cfg.Unauthorized(c)
}
Expand Down
51 changes: 51 additions & 0 deletions middleware/basicauth/basicauth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,57 @@ func Test_Middleware_BasicAuth(t *testing.T) {
}
}

func Test_BasicAuth_WWWAuthenticateHeader(t *testing.T) {
t.Parallel()
app := fiber.New()

app.Use(New(Config{Users: map[string]string{"john": "doe"}}))

resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
require.NoError(t, err)
require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode)
require.Equal(t, `Basic realm="Restricted"`, resp.Header.Get(fiber.HeaderWWWAuthenticate))
}

func Test_BasicAuth_InvalidHeader(t *testing.T) {
t.Parallel()
app := fiber.New()

app.Use(New(Config{Users: map[string]string{"john": "doe"}}))

req := httptest.NewRequest(fiber.MethodGet, "/", nil)
req.Header.Set(fiber.HeaderAuthorization, "Basic notbase64")
resp, err := app.Test(req)

require.NoError(t, err)
require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode)
}

func Test_BasicAuth_WhitespaceHandling(t *testing.T) {
t.Parallel()
app := fiber.New()

app.Use(New(Config{Users: map[string]string{"john": "doe"}}))
app.Get("/", func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusTeapot) })

creds := base64.StdEncoding.EncodeToString([]byte("john:doe"))

cases := []string{
"Basic " + creds,
" Basic \t" + creds,
"Basic " + creds + " ",
}

for _, h := range cases {
req := httptest.NewRequest(fiber.MethodGet, "/", nil)
req.Header.Set(fiber.HeaderAuthorization, h)
resp, err := app.Test(req)

require.NoError(t, err)
require.Equal(t, fiber.StatusTeapot, resp.StatusCode)
}
}

// go test -v -run=^$ -bench=Benchmark_Middleware_BasicAuth -benchmem -count=4
func Benchmark_Middleware_BasicAuth(b *testing.B) {
app := fiber.New()
Expand Down
3 changes: 2 additions & 1 deletion middleware/basicauth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package basicauth

import (
"crypto/subtle"
"strconv"

"github.com/gofiber/fiber/v3"
"github.com/gofiber/utils/v2"
Expand Down Expand Up @@ -79,7 +80,7 @@ func configDefault(config ...Config) Config {
}
if cfg.Unauthorized == nil {
cfg.Unauthorized = func(c fiber.Ctx) error {
c.Set(fiber.HeaderWWWAuthenticate, "basic realm="+cfg.Realm)
c.Set(fiber.HeaderWWWAuthenticate, "Basic realm="+strconv.Quote(cfg.Realm))
return c.SendStatus(fiber.StatusUnauthorized)
}
}
Expand Down
Loading