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
4 changes: 2 additions & 2 deletions docs/middleware/encryptcookie.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ To generate a 32 char key, use `openssl rand -base64 32` or `encryptcookie.Gener
|:----------|:----------------------------------------------------|:------------------------------------------------------------------------------------------------------|:-----------------------------|
| Next | `func(fiber.Ctx) bool` | A function to skip this middleware when returned true. | `nil` |
| Except | `[]string` | Array of cookie keys that should not be encrypted. | `[]` |
| Key | `string` | A base64-encoded unique key to encode & decode cookies. Required. Key length should be 32 characters. | (No default, required field) |
| Key | `string` | A base64-encoded unique key to encode & decode cookies. Required. Key length should be 16, 24, or 32 bytes. | (No default, required field) |
| Encryptor | `func(decryptedString, key string) (string, error)` | A custom function to encrypt cookies. | `EncryptCookie` |
| Decryptor | `func(encryptedString, key string) (string, error)` | A custom function to decrypt cookies. | `DecryptCookie` |

Expand Down Expand Up @@ -95,7 +95,7 @@ app.Use(encryptcookie.New(encryptcookie.Config{
Except: []string{csrf.ConfigDefault.CookieName}, // exclude CSRF cookie
}))
app.Use(csrf.New(csrf.Config{
KeyLookup: "header:" + csrf.HeaderName,
Extractor: csrf.FromHeader(csrf.HeaderName),
CookieSameSite: "Lax",
CookieSecure: true,
CookieHTTPOnly: false,
Expand Down
101 changes: 72 additions & 29 deletions docs/middleware/keyauth.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ func TokenFromContext(c fiber.Ctx) string

## Examples

### Basic Example

This example shows how to use the KeyAuth middleware with an API key passed in a cookie.

```go
package main

Expand Down Expand Up @@ -44,7 +48,7 @@ func main() {

// note that the keyauth middleware needs to be defined before the routes are defined!
app.Use(keyauth.New(keyauth.Config{
KeyLookup: "cookie:access_token",
Extractor: keyauth.FromCookie("access_token"),
Validator: validateAPIKey,
}))

Expand All @@ -56,25 +60,27 @@ func main() {
}
```

## Test
**Test:**

```bash
# No api-key specified -> 400 missing
# No api-key specified -> 401 missing api key in cookie
curl http://localhost:3000
#> missing or malformed API Key
#> missing api key in cookie

# Correct API key -> 200 OK
curl --cookie "access_token=correct horse battery staple" http://localhost:3000
#> Successfully authenticated!

# Incorrect API key -> 401 Invalid or expired API Key
curl --cookie "access_token=Clearly A Wrong Key" http://localhost:3000
#> missing or malformed API Key
#> Invalid or expired API Key
```

For a more detailed example, see also the [`github.com/gofiber/recipes`](https://github.com/gofiber/recipes) repository and specifically the `fiber-envoy-extauthz` repository and the [`keyauth example`](https://github.com/gofiber/recipes/blob/master/fiber-envoy-extauthz/authz/main.go) code.

### Authenticate only certain endpoints

If you want to authenticate only certain endpoints, you can use the `Config` of keyauth and apply a filter function (eg. `authFilter`) like so
If you want to authenticate only certain endpoints, you can use the `Next` function in the config to skip the middleware for specific routes.

```go
package main
Expand Down Expand Up @@ -111,18 +117,20 @@ func authFilter(c fiber.Ctx) bool {

for _, pattern := range protectedURLs {
if pattern.MatchString(originalURL) {
// Run middleware for protected routes
return false
}
}
// Skip middleware for non-protected routes
return true
}

func main() {
app := fiber.New()

app.Use(keyauth.New(keyauth.Config{
Next: authFilter,
KeyLookup: "cookie:access_token",
Next: authFilter,
Extractor: keyauth.FromCookie("access_token"),
Validator: validateAPIKey,
}))

Expand All @@ -140,7 +148,7 @@ func main() {
}
```

Which results in this
**Test:**

```bash
# / does not need to be authenticated
Expand All @@ -158,6 +166,8 @@ curl --cookie "access_token=correct horse battery staple" http://localhost:3000/

### Specifying middleware in the handler

You can apply the middleware to specific routes or groups instead of globally. This example uses the default extractor (`FromAuthHeader`).

```go
package main

Expand Down Expand Up @@ -199,30 +209,60 @@ func main() {
}
```

Which results in this
**Test:**

```bash
# / does not need to be authenticated
curl http://localhost:3000
#> Welcome

# /allowed needs to be authenticated too
# /allowed needs to be authenticated
curl --header "Authorization: Bearer my-super-secret-key" http://localhost:3000/allowed
#> Successfully authenticated!
```

## Key Extractors

The middleware extracts the API key from the request using an `Extractor`. You can specify one or more extractors in the configuration.

### Built-in Extractors

The following extractors are available:

- `keyauth.FromHeader(header string)`: Extracts the key from the specified header.
- `keyauth.FromAuthHeader(header, authScheme string)`: Extracts the key from an authorization header (e.g., `Authorization: Bearer <key>`).
- `keyauth.FromQuery(param string)`: Extracts the key from a URL query parameter.
- `keyauth.FromParam(param string)`: Extracts the key from a URL path parameter.
- `keyauth.FromCookie(name string)`: Extracts the key from a cookie.
- `keyauth.FromForm(name string)`: Extracts the key from a form field.

### Chaining Extractors

You can use `keyauth.Chain` to try multiple extractors in order until one succeeds. The first successful extraction will be used.

```go
// This will try to extract the key from:
// 1. The "X-API-Key" header
// 2. The "api_key" query parameter
app.Use(keyauth.New(keyauth.Config{
Extractor: keyauth.Chain(
keyauth.FromHeader("X-API-Key"),
keyauth.FromQuery("api_key"),
),
Validator: validateAPIKey,
}))
```

## Config

| Property | Type | Description | Default |
|:----------------|:-----------------------------------------|:-------------------------------------------------------------------------------------------------------|:------------------------------|
| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` |
| SuccessHandler | `fiber.Handler` | SuccessHandler defines a function which is executed for a valid key. | `nil` |
| ErrorHandler | `fiber.ErrorHandler` | ErrorHandler defines a function which is executed for an invalid key. By default a 401 response with a `WWW-Authenticate` challenge is sent. | `nil` |
| KeyLookup | `string` | KeyLookup is a string in the form of "`<source>:<name>`" that is used to extract the key from the request. | "header:Authorization" |
| CustomKeyLookup | `KeyLookupFunc` aka `func(c fiber.Ctx) (string, error)` | If more complex logic is required to extract the key from the request, an arbitrary function to extract it can be specified here. Utility helper functions are described below. | `nil` |
| AuthScheme | `string` | AuthScheme to be used with the `Authorization` header. When `KeyLookup` is not set, this defaults to `"Bearer"`. | "Bearer" |
| SuccessHandler | `fiber.Handler` | SuccessHandler defines a function which is executed for a valid key. | `c.Next()` |
| ErrorHandler | `fiber.ErrorHandler` | ErrorHandler defines a function which is executed for an invalid key. By default a 401 response with a `WWW-Authenticate` challenge is sent. | Default error handler |
| Validator | `func(fiber.Ctx, string) (bool, error)` | **Required.** Validator is a function to validate the key. | `nil` (panic) |
| Extractor | `keyauth.Extractor` | Extractor defines how to retrieve the key from the request. Use helper functions like `keyauth.FromAuthHeader` or `keyauth.FromCookie`. | `keyauth.FromAuthHeader("Authorization", "Bearer")` |
| Realm | `string` | Realm specifies the protected area name used in the `WWW-Authenticate` header. | `"Restricted"` |
| Validator | `func(fiber.Ctx, string) (bool, error)` | Validator is a function to validate the key. | A function for key validation |

## Default Config

Expand All @@ -231,17 +271,20 @@ var ConfigDefault = Config{
SuccessHandler: func(c fiber.Ctx) error {
return c.Next()
},
ErrorHandler: nil,
KeyLookup: "header:" + fiber.HeaderAuthorization,
CustomKeyLookup: nil,
AuthScheme: "Bearer",
Realm: "Restricted",
ErrorHandler: func(c fiber.Ctx, err error) error {
switch {
case errors.Is(err, ErrMissingOrMalformedAPIKey),
errors.Is(err, ErrMissingAPIKey),
errors.Is(err, ErrMissingAPIKeyInHeader),
errors.Is(err, ErrMissingAPIKeyInQuery),
errors.Is(err, ErrMissingAPIKeyInParam),
errors.Is(err, ErrMissingAPIKeyInForm),
errors.Is(err, ErrMissingAPIKeyInCookie):
return c.Status(fiber.StatusUnauthorized).SendString(err.Error())
}
return c.Status(fiber.StatusUnauthorized).SendString("Invalid or expired API Key")
},
Realm: "Restricted",
Extractor: FromAuthHeader(fiber.HeaderAuthorization, "Bearer"),
}
```

## CustomKeyLookup

Two public utility functions are provided that may be useful when creating custom extraction:

* `DefaultKeyLookup(keyLookup string, authScheme string)`: This is the function that implements the default `KeyLookup` behavior, exposed to be used as a component of custom parsing logic
* `MultipleKeySourceLookup(keyLookups []string, authScheme string)`: Creates a CustomKeyLookup function that checks each listed source using the above function until a key is found or the options are all exhausted. For example, `MultipleKeySourceLookup([]string{"header:Authorization", "header:x-api-key", "cookie:apikey"}, "Bearer")` would first check the standard Authorization header, checks the `x-api-key` header next, and finally checks for a cookie named `apikey`. If any of these contain a valid API key, the request continues. Otherwise, an error is returned.
25 changes: 24 additions & 1 deletion docs/whats_new.md
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,7 @@ The `Expiration` field in the CSRF middleware configuration has been renamed to

### EncryptCookie

Added support for specifying Key length when using `encryptcookie.GenerateKey(length)`. This allows the user to generate keys compatible with `AES-128`, `AES-192`, and `AES-256` (Default).
Added support for specifying key length when using `encryptcookie.GenerateKey(length)`. Keys must be base64-encoded and may be 16, 24, or 32 bytes when decoded, supporting AES-128, AES-192, and AES-256 (default).

### EnvVar

Expand All @@ -1113,6 +1113,7 @@ Refer to the [healthcheck middleware migration guide](./middleware/healthcheck.m
### KeyAuth

The keyauth middleware was updated to introduce a configurable `Realm` field for the `WWW-Authenticate` header.
The old string-based `KeyLookup` configuration has been replaced with an `Extractor` field. Use helper functions like `keyauth.FromHeader`, `keyauth.FromAuthHeader`, or `keyauth.FromCookie` to define where the key should be retrieved from. Multiple sources can be combined with `keyauth.Chain`. See the migration guide below.

Comment on lines 1115 to 1117

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Ensure WWW-Authenticate behavior is documented for chained extractors

When an auth scheme is present (e.g., FromAuthHeader), the middleware sets the WWW-Authenticate header. If the first extractor in the chain doesn’t specify a scheme, this header may be omitted. Mention that for chains, the scheme is inferred from the first extractor.

To verify code behavior, search for getAuthScheme and confirm it inspects Extractor.AuthScheme and/or walks Extractor.Chain:

Also applies to: 1943-1963


🏁 Script executed:

#!/bin/bash
ast-grep --pattern $'func $_($_$) $_ {\n  $$$\n  getAuthScheme($_)\n  $$$\n}' -A 3
rg -n "getAuthScheme|WWW-Authenticate|HeaderWWWAuthenticate" -A 5

Length of output: 9722


Document WWW-Authenticate behavior for chained extractors

Short summary: I verified getAuthScheme inspects Extractor.AuthScheme and walks Extractor.Chain: it returns the first auth scheme from an extractor using the Authorization header and, if none is found, the middleware does not set the WWW-Authenticate header. Please document this behavior.

Files/locations to update:

  • middleware/keyauth/keyauth.go — getAuthScheme implementation (around lines 67–74) shows it checks e.Source == SourceAuthHeader and iterates e.Chain to find a matching AuthScheme.
  • docs/whats_new.md — update both occurrences (around lines 1115–1117 and 1944–1946) to mention chained extractor behavior.
  • docs/middleware/keyauth.md — add a short note in the Extractor/Realm/default-config section explaining when WWW-Authenticate is set for chains.
  • (optional) middleware/keyauth/keyauth_test.go — referenced tests (e.g., ~lines 682/702) confirm current behavior; update tests or add examples in docs if helpful.

Suggested wording to add:
"Note: When using keyauth.Chain the WWW-Authenticate header is inferred from the first extractor that uses the Authorization header (i.e., the first extractor with SourceAuthHeader). If no extractor in the chain provides an auth scheme, the WWW-Authenticate header will not be set."

### Logger

Expand Down Expand Up @@ -1938,6 +1939,28 @@ Passwords configured for BasicAuth must now be pre-hashed. If no prefix is suppl
You can also set the optional `HeaderLimit` and `Charset`
options to further control authentication behavior.

#### KeyAuth

The keyauth middleware was updated to introduce a configurable `Realm` field for the `WWW-Authenticate` header.
The old string-based `KeyLookup` configuration has been replaced with an `Extractor` field, and the `AuthScheme` field has been removed. The auth scheme is now inferred from the extractor used (e.g., `keyauth.FromAuthHeader`). Use helper functions like `keyauth.FromHeader`, `keyauth.FromAuthHeader`, or `keyauth.FromCookie` to define where the key should be retrieved from. Multiple sources can be combined with `keyauth.Chain`.

```go
// Before
app.Use(keyauth.New(keyauth.Config{
KeyLookup: "header:Authorization",
AuthScheme: "Bearer",
Validator: validateAPIKey,
}))

// After
app.Use(keyauth.New(keyauth.Config{
Extractor: keyauth.FromAuthHeader(fiber.HeaderAuthorization, "Bearer"),
Validator: validateAPIKey,
}))
```

Combine multiple sources with `keyauth.Chain()` when needed.

#### Cache

The deprecated `Store` and `Key` fields were removed. Use `Storage` and
Expand Down
Loading
Loading