From 6ae86569d37420f45ac0eae471685cd30d14a859 Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Sat, 26 Jul 2025 23:43:27 -0400 Subject: [PATCH] basicauth: add ctx to authorizer --- docs/middleware/basicauth.md | 4 ++-- docs/whats_new.md | 1 + middleware/basicauth/basicauth.go | 2 +- middleware/basicauth/basicauth_test.go | 26 ++++++++++++++++++++++++++ middleware/basicauth/config.go | 11 ++++++----- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/docs/middleware/basicauth.md b/docs/middleware/basicauth.md index 74c70865a53..0e9c5009a1b 100644 --- a/docs/middleware/basicauth.md +++ b/docs/middleware/basicauth.md @@ -45,7 +45,7 @@ app.Use(basicauth.New(basicauth.Config{ "admin": "123456", }, Realm: "Forbidden", - Authorizer: func(user, pass string) bool { + Authorizer: func(user, pass string, c fiber.Ctx) bool { if user == "john" && pass == "doe" { return true } @@ -80,7 +80,7 @@ func handler(c fiber.Ctx) error { | Realm | `string` | Realm is a string to define the realm attribute of BasicAuth. The realm identifies the system to authenticate against and can be used by clients to save credentials. | `"Restricted"` | | Charset | `string` | Charset sent in the `WWW-Authenticate` header, so clients know how credentials are encoded. | `"UTF-8"` | | StorePassword | `bool` | Store the plaintext password in the context and retrieve it via `PasswordFromContext`. | `false` | -| Authorizer | `func(string, string) bool` | Authorizer defines a function to check the credentials. It will be called with a username and password and is expected to return true or false to indicate approval. | `nil` | +| Authorizer | `func(string, string, fiber.Ctx) bool` | Authorizer defines a function to check the credentials. It will be called with a username, password, and the current context and is expected to return true or false to indicate approval. | `nil` | | Unauthorized | `fiber.Handler` | Unauthorized defines the response body for unauthorized responses. | `nil` | ## Default Config diff --git a/docs/whats_new.md b/docs/whats_new.md index 4749e6052f5..6156e400859 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -1054,6 +1054,7 @@ The adaptor middleware has been significantly optimized for performance and effi ### BasicAuth The BasicAuth middleware now validates the `Authorization` header more rigorously and sets security-focused response headers. The default challenge includes the `charset="UTF-8"` parameter and disables caching. Passwords are no longer stored in the request context by default; use the new `StorePassword` option to retain them. A `Charset` option controls the value used in the challenge header. +The `Authorizer` function now receives the current `fiber.Ctx` as a third argument, allowing credential checks to incorporate request context. ### Cache diff --git a/middleware/basicauth/basicauth.go b/middleware/basicauth/basicauth.go index 17cf041f6b9..62016366aa5 100644 --- a/middleware/basicauth/basicauth.go +++ b/middleware/basicauth/basicauth.go @@ -68,7 +68,7 @@ func New(config Config) fiber.Handler { username := creds[:index] password := creds[index+1:] - if cfg.Authorizer(username, password) { + if cfg.Authorizer(username, password, c) { c.Locals(usernameKey, username) if cfg.StorePassword { c.Locals(passwordKey, password) diff --git a/middleware/basicauth/basicauth_test.go b/middleware/basicauth/basicauth_test.go index 27d39fe2758..76e281bbdb7 100644 --- a/middleware/basicauth/basicauth_test.go +++ b/middleware/basicauth/basicauth_test.go @@ -113,6 +113,32 @@ func Test_BasicAuth_NoStorePassword(t *testing.T) { require.Equal(t, fiber.StatusOK, resp.StatusCode) } +func Test_BasicAuth_AuthorizerCtx(t *testing.T) { + t.Parallel() + app := fiber.New() + + called := false + app.Use(New(Config{ + Authorizer: func(user, pass string, c fiber.Ctx) bool { + called = true + require.Equal(t, "john", user) + require.Equal(t, "doe", pass) + require.Equal(t, "/ctx", c.Path()) + return true + }, + })) + + app.Get("/ctx", func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) }) + + creds := base64.StdEncoding.EncodeToString([]byte("john:doe")) + req := httptest.NewRequest(fiber.MethodGet, "/ctx", nil) + req.Header.Set(fiber.HeaderAuthorization, "Basic "+creds) + resp, err := app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + require.True(t, called) +} + func Test_BasicAuth_WWWAuthenticateHeader(t *testing.T) { t.Parallel() app := fiber.New() diff --git a/middleware/basicauth/config.go b/middleware/basicauth/config.go index a6493fcea31..698d8568844 100644 --- a/middleware/basicauth/config.go +++ b/middleware/basicauth/config.go @@ -22,12 +22,13 @@ type Config struct { // Authorizer defines a function you can pass // to check the credentials however you want. - // It will be called with a username and password - // and is expected to return true or false to indicate - // that the credentials were approved or not. + // It will be called with a username, password and + // the current fiber context and is expected to return + // true or false to indicate that the credentials were + // approved or not. // // Optional. Default: nil. - Authorizer func(string, string) bool + Authorizer func(string, string, fiber.Ctx) bool // Unauthorized defines the response body for unauthorized responses. // By default it will return with a 401 Unauthorized and the correct WWW-Auth header @@ -91,7 +92,7 @@ func configDefault(config ...Config) Config { cfg.Charset = ConfigDefault.Charset } if cfg.Authorizer == nil { - cfg.Authorizer = func(user, pass string) bool { + cfg.Authorizer = func(user, pass string, _ fiber.Ctx) bool { userPwd, exist := cfg.Users[user] return exist && subtle.ConstantTimeCompare(utils.UnsafeBytes(userPwd), utils.UnsafeBytes(pass)) == 1 }