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
}
}