Fiber JWT

The JWT middleware returns a JSON Web Token (JWT) authentication middleware. For a valid token, it sets the user in Ctx.Locals and calls the next handler. For an invalid token, it returns a “401 - Unauthorized” error. For a missing token, it returns a “400 - Bad Request” error.

Note: Go version 1.19 or higher is required

Installation

This middleware supports Fiber v1 and v2, please install accordingly.

go get -u github.com/gofiber/fiber/v2
go get -u github.com/gofiber/contrib/jwt
go get -u github.com/golang-jwt/jwt/v5

Signature

jwtware.New(config ...jwtware.Config) func(*fiber.Ctx) error

Configuration

Property Type Description Default Value
Filter func(*fiber.Ctx) bool Defines a function to skip the middleware nil
SuccessHandler func(*fiber.Ctx) error Defines a function to execute upon valid token nil
ErrorHandler func(*fiber.Ctx, error) error Defines a function to execute upon invalid token 401 Invalid or expired JWT
SigningKey interface{} The signing key used to verify the token. If the length of SigningKeys is 0, it is used as a fallback nil
SigningKeys map[string]interface{} The mapping of signing keys used to verify tokens with a kid field nil
ContextKey string The context key used to store user information from the token in the context "user"
Claims jwt.Claim The extended claims data defining the token content jwt.MapClaims{}
TokenLookup string A string in the format : used to parse the token "header:Authorization"
AuthScheme string The AuthScheme used in the Authorization header. The default value (“Bearer”) is used only with the default TokenLookup value "Bearer"
KeyFunc func() jwt.Keyfunc A user-defined function to provide public key for token verification jwtKeyFunc
JWKSetURLs []string A slice of unique JSON Web Key (JWK) Set URLs used to parse JWT nil

Example of HS256

package main

import (
    "time"

    "github.com/gofiber/fiber/v2"

    jwtware "github.com/gofiber/contrib/jwt"
    "github.com/golang-jwt/jwt/v5"
)

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

    // Login route
    app.Post("/login", login)

    // Accessible route without authentication
    app.Get("/", accessible)

    // JWT middleware
    app.Use(jwtware.New(jwtware.Config{
        SigningKey: jwtware.SigningKey{Key: []byte("secret")},
    }))

    // Restricted route
    app.Get("/restricted", restricted)

    app.Listen(":3000")
}

func login(c *fiber.Ctx) error {
    user := c.FormValue("user")
    pass := c.FormValue("pass")

    // Throwing unauthorized error
    if user != "john" || pass != "doe" {
        return c.SendStatus(fiber.StatusUnauthorized)
    }

    // Creating claims
    claims := jwt.MapClaims{
        "name":  "John Doe",
        "admin": true,
        "exp":   time.Now().Add(time.Hour * 72).Unix(),
    }

    // Creating token
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

    // Generating encoded token and sending as response
    t, err := token.SignedString([]byte("secret"))
    if err != nil {
        return c.SendStatus(fiber.StatusInternalServerError)
    }

    return c.JSON(fiber.Map{"token": t})
}

func accessible(c *fiber.Ctx) error {
    return c.SendString("Accessible")
}

func restricted(c *fiber.Ctx) error {
    user := c.Locals("user").(*jwt.Token)
    claims := user.Claims.(jwt.MapClaims)
    name := claims["name"].(string)
    return c.SendString("Welcome " + name)
}

HS256 Test

Log in with username and password to obtain a token.

curl --data "user=john&pass=doe" http://localhost:3000/login

Response

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"
}

Request restricted resource using the token in the authorization header.

curl localhost:3000/restricted -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"

Response

Welcome John Doe
package main

import (
    "crypto/rand"
    "crypto/rsa"
    "log"
    "time"

    "github.com/gofiber/fiber/v2"

    "github.com/golang-jwt/jwt/v5"

    jwtware "github.com/gofiber/contrib/jwt"
)

var (
    // Obviously, this is just a test example. Don't do this in production.
    // In production, you should generate the key pair beforehand.
    // Never add the private key to any GitHub repository.
    privateKey *rsa.PrivateKey
)

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

    // For demonstration, generate a new private/public key pair each time the program runs. See the note above.
    rng := rand.Reader
    var err error
    privateKey, err = rsa.GenerateKey(rng, 2048)
    if err != nil {
        log.Fatalf("rsa.GenerateKey:%v", err)
    }

    // Login route
    app.Post("/login", login)

    // Unauthenticated route
    app.Get("/", accessible)

    // JWT middleware
    app.Use(jwtware.New(jwtware.Config{
        SigningKey: jwtware.SigningKey{
            JWTAlg: jwtware.RS256,
            Key:    privateKey.Public(),
        },
    }))

    // Restricted route
    app.Get("/restricted", restricted)

    app.Listen(":3000")
}

func login(c *fiber.Ctx) error {
    user := c.FormValue("user")
    pass := c.FormValue("pass")

    // Throw unauthorized error
    if user != "john" || pass != "doe" {
        return c.SendStatus(fiber.StatusUnauthorized)
    }

    // Create claims
    claims := jwt.MapClaims{
        "name":  "John Doe",
        "admin": true,
        "exp":   time.Now().Add(time.Hour * 72).Unix(),
    }

    // Create token
    token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)

    // Generate encoded token and send it as the response
    t, err := token.SignedString(privateKey)
    if err != nil {
        log.Printf("token.SignedString:%v", err)
        return c.SendStatus(fiber.StatusInternalServerError)
    }

    return c.JSON(fiber.Map{"token": t})
}

func accessible(c *fiber.Ctx) error {
    return c.SendString("Accessible")
}

func restricted(c *fiber.Ctx) error {
    user := c.Locals("user").(*jwt.Token)
    claims := user.Claims.(jwt.MapClaims)
    name := claims["name"].(string)
    return c.SendString("Welcome " + name)
}

RS256 Test

RS256 is essentially the same as the above HS256 test.

JWK Set Test

These tests are the same as the basic JWT tests above, but they require valid public key sets in JWK Set format at JWKSetURLs.

Custom KeyFunc Example

The KeyFunc defines a user-defined function used to provide public keys for token validation. This function is responsible for validating the signature algorithm and selecting the correct key. If the token is issued by an external party, the user-defined KeyFunc may be useful.

When a user-defined KeyFunc is provided, the SigningKey, SigningKeys, and SigningMethod will be ignored. This is one of the three options for providing token validation keys. The priority order is the user-defined KeyFunc, SigningKeys, and SigningKey. If neither SigningKeys nor SigningKey is provided, this function must be provided. The default is to validate the signature algorithm and select the appropriate key using internal implementation.

package main

import (
    "fmt"
    "github.com/gofiber/fiber/v2"

  jwtware "github.com/gofiber/contrib/jwt"
  "github.com/golang-jwt/jwt/v5"
)

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

    app.Use(jwtware.New(jwtware.Config{
        KeyFunc: customKeyFunc(),
    }))

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

func customKeyFunc() jwt.Keyfunc {
    return func(t *jwt.Token) (interface{}, error) {
        // Check if the signature method is correct
        if t.Method.Alg() != jwtware.HS256 {
            return nil, fmt.Errorf("Unexpected jwt signing method=%v", t.Header["alg"])
        }

        // TODO Implement custom loading of the signing key, e.g., load from the database
    signingKey := "secret"

        return []byte(signingKey), nil
    }
}