Skip to main content
Version: Next

KeyAuth

The KeyAuth middleware implements API key authentication.

Signatures​

func New(config ...Config) fiber.Handler
func TokenFromContext(ctx any) string

TokenFromContext accepts a fiber.CustomCtx, fiber.Ctx, a *fasthttp.RequestCtx, or a context.Context. The token points into the request's buffer unless Immutable is set: copy it with strings.Clone before a goroutine keeps it.

Examples​

Basic example​

This example registers KeyAuth with an API key stored in a cookie.

package main

import (
"crypto/sha256"
"crypto/subtle"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/extractors"
"github.com/gofiber/fiber/v3/middleware/keyauth"
)

var (
apiKey = "correct horse battery staple"
)

func validateAPIKey(c fiber.Ctx, key string) (bool, error) {
hashedAPIKey := sha256.Sum256([]byte(apiKey))
hashedKey := sha256.Sum256([]byte(key))

if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 {
return true, nil
}
return false, keyauth.ErrMissingOrMalformedAPIKey
}

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

// Register middleware before the routes that need it
app.Use(keyauth.New(keyauth.Config{
Extractor: extractors.FromCookie("access_token"),
Validator: validateAPIKey,
}))

app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Successfully authenticated!")
})

app.Listen(":3000")
}

Test:

# No API key specified -> 401 missing or invalid API Key
curl http://localhost:3000
#> missing or invalid API Key

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

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

For a more detailed example, see the envoy-extauthz recipe in the gofiber/recipes repository.

Authenticate only certain endpoints​

Use the Next function to run KeyAuth only on selected routes.

package main

import (
"crypto/sha256"
"crypto/subtle"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/extractors"
"github.com/gofiber/fiber/v3/middleware/keyauth"
"regexp"
"strings"
)

var (
apiKey = "correct horse battery staple"
protectedURLs = []*regexp.Regexp{
regexp.MustCompile("^/authenticated$"),
regexp.MustCompile("^/auth2$"),
}
)

func validateAPIKey(c fiber.Ctx, key string) (bool, error) {
hashedAPIKey := sha256.Sum256([]byte(apiKey))
hashedKey := sha256.Sum256([]byte(key))

if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 {
return true, nil
}
return false, keyauth.ErrMissingOrMalformedAPIKey
}

func authFilter(c fiber.Ctx) bool {
originalURL := strings.ToLower(c.OriginalURL())

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,
Extractor: extractors.FromCookie("access_token"),
Validator: validateAPIKey,
}))

app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Welcome")
})
app.Get("/authenticated", func(c fiber.Ctx) error {
return c.SendString("Successfully authenticated!")
})
app.Get("/auth2", func(c fiber.Ctx) error {
return c.SendString("Successfully authenticated 2!")
})

app.Listen(":3000")
}

Test:

# / doesn't require authentication
curl http://localhost:3000
#> Welcome

# /authenticated requires authentication
curl --cookie "access_token=correct horse battery staple" http://localhost:3000/authenticated
#> Successfully authenticated!

# /auth2 requires authentication too
curl --cookie "access_token=correct horse battery staple" http://localhost:3000/auth2
#> Successfully authenticated 2!

Apply middleware in the handler​

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

package main

import (
"crypto/sha256"
"crypto/subtle"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/keyauth"
)

const (
apiKey = "my-super-secret-key"
)

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

authMiddleware := keyauth.New(keyauth.Config{
Validator: func(c fiber.Ctx, key string) (bool, error) {
hashedAPIKey := sha256.Sum256([]byte(apiKey))
hashedKey := sha256.Sum256([]byte(key))

if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 {
return true, nil
}
return false, keyauth.ErrMissingOrMalformedAPIKey
},
})

app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Welcome")
})

app.Get("/allowed", authMiddleware, func(c fiber.Ctx) error {
return c.SendString("Successfully authenticated!")
})

app.Listen(":3000")
}

Test:

# / doesn't require authentication
curl http://localhost:3000
#> Welcome

# /allowed requires authentication
curl --header "Authorization: Bearer my-super-secret-key" http://localhost:3000/allowed
#> Successfully authenticated!

Key Extractors​

KeyAuth uses an Extractor from the shared extractors package to retrieve the API key from the request. You can specify one or more extractors in the configuration. For a full list of extractors, chaining, and advanced usage, see the Extractors Guide.

Typical Usage​

Specify the extractor in the config. For example, to extract from a cookie:

app.Use(keyauth.New(keyauth.Config{
Extractor: extractors.FromCookie("access_token"),
Validator: validateAPIKey,
}))

To use the default (Authorization header with Bearer scheme):

app.Use(keyauth.New(keyauth.Config{
Validator: validateAPIKey, // Extractor defaults to FromAuthHeader("Bearer")
}))

To try multiple sources (header, then query):

app.Use(keyauth.New(keyauth.Config{
Extractor: extractors.Chain(
extractors.FromHeader("X-API-Key"),
extractors.FromQuery("api_key"),
),
Validator: validateAPIKey,
}))

For custom logic, use extractors.FromCustom:

app.Use(keyauth.New(keyauth.Config{
Extractor: extractors.FromCustom("X-My-API-Key", func(c fiber.Ctx) (string, error) {
key := c.Get("X-My-API-Key")
if key == "" {
return "", extractors.ErrNotFound
}
return key, nil
}),
Validator: validateAPIKey,
}))

Refer to the Extractors Guide for details, security notes, and advanced configuration.

Config​

PropertyTypeDescriptionDefault
Nextfunc(fiber.Ctx) boolNext defines a function to skip this middleware when it returns true.nil
SuccessHandlerfiber.HandlerSuccessHandler defines a function which is executed for a valid key.c.Next()
ErrorHandlerfiber.ErrorHandlerErrorHandler defines a function which is executed for an invalid key. It receives the extraction or validation error, which is nil when the Validator returned false without an error. By default a 401 response with the body missing or invalid API Key (keyauth.ErrMissingOrMalformedAPIKey) and a WWW-Authenticate challenge is sent. The challenge is also added when a custom handler returns a *fiber.Error instead of writing the response itself: as WWW-Authenticate for status 401, and as Proxy-Authenticate for status 407.Default error handler
Validatorfunc(fiber.Ctx, string) (bool, error)Required. Validator is a function to validate the key.nil (panic)
Extractorextractors.ExtractorExtractor defines how to retrieve the key from the request. Use helper functions from the shared extractors package, e.g. extractors.FromAuthHeader("Bearer") or extractors.FromCookie("access_token").extractors.FromAuthHeader("Bearer")
RealmstringRealm specifies the protected area name used in the WWW-Authenticate header."Restricted"
ChallengestringValue of the WWW-Authenticate header when no Authorization scheme is present.ApiKey realm="Restricted"
ErrorstringError code appended as the error parameter in Bearer challenges when the request presented credentials that were malformed or failed validation. It is omitted, together with ErrorDescription, ErrorURI and Scope, when the request carried no credentials or used an Authorization scheme other than Bearer, as RFC 6750 asks. Must be invalid_request, invalid_token, or insufficient_scope, available as the constants keyauth.ErrorInvalidRequest, keyauth.ErrorInvalidToken, and keyauth.ErrorInsufficientScope. Any other value panics.""
ErrorDescriptionstringHuman-readable text for the error_description parameter in Bearer challenges. Requires Error.""
ErrorURIstringURI identifying a human-readable web page with information about the error in Bearer challenges. Requires Error and must be an absolute URI.""
ScopestringSpace-delimited list of scopes for the scope parameter in Bearer challenges. Each token must conform to the RFC 6750 scope-token syntax. Only valid with Error set to insufficient_scope, which in turn requires Scope.""

When the extractor reads the Authorization header with extractors.FromAuthHeader and a non-empty scheme, alone or inside a Chain, the challenge is generated as <scheme> realm="<Realm>" for each scheme, for example Bearer realm="Restricted", and Challenge is not used. FromAuthHeader("") names no scheme, so Challenge applies, which is ApiKey realm="<Realm>" unless you set it.

Generated challenge parameter values are escaped as RFC 9110 HTTP quoted strings. A custom Challenge value is emitted verbatim.

Default Config​

var ConfigDefault = Config{
SuccessHandler: func(c fiber.Ctx) error {
return c.Next()
},
ErrorHandler: func(c fiber.Ctx, _ error) error {
return c.Status(fiber.StatusUnauthorized).SendString(ErrMissingOrMalformedAPIKey.Error())
},
Realm: "Restricted",
Extractor: extractors.FromAuthHeader("Bearer"),
}