Fiber JWT

Le middleware JWT renvoie un middleware d'authentification JSON Web Token (JWT). Pour un jeton valide, il définit l'utilisateur dans Ctx.Locals et appelle le gestionnaire suivant. Pour un jeton invalide, il renvoie une erreur "401 - Non autorisé". Pour un jeton manquant, il renvoie une erreur "400 - Requête incorrecte".

Remarque : la version de Go 1.19 ou supérieure est requise

Installation

Ce middleware prend en charge Fiber v1 et v2, veuillez installer en conséquence.

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

Propriété Type Description Valeur par défaut
Filter func(*fiber.Ctx) bool Définit une fonction pour sauter le middleware nil
SuccessHandler func(*fiber.Ctx) error Définit une fonction à exécuter lors d'un jeton valide nil
ErrorHandler func(*fiber.Ctx, error) error Définit une fonction à exécuter lors d'un jeton invalide 401 JWT invalide ou expiré
SigningKey interface{} La clé de signature utilisée pour vérifier le jeton. Si la longueur de SigningKeys est de 0, elle est utilisée comme solution de secours nil
SigningKeys map[string]interface{} La correspondance des clés de signature utilisées pour vérifier les jetons avec un champ kid nil
ContextKey string La clé de contexte utilisée pour stocker les informations d'utilisateur du jeton dans le contexte "utilisateur"
Claims jwt.Claim Les données de revendications étendues définissant le contenu du jeton jwt.MapClaims{}
TokenLookup string Une chaîne au format : utilisée pour analyser le jeton "header:Authorization"
AuthScheme string Le schéma d'authentification utilisé dans l'en-tête Authorization. La valeur par défaut ("Bearer") est utilisée uniquement avec la valeur TokenLookup par défaut "Bearer"
KeyFunc func() jwt.Keyfunc Une fonction définie par l'utilisateur pour fournir une clé publique pour la vérification du jeton jwtKeyFunc
JWKSetURLs []string Une liste d'URL de jeu de clés JSON Web (JWK) uniques utilisée pour analyser les JWT nil

Exemple de 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()

	// Route de connexion
	app.Post("/login", login)

	// Route accessible sans authentification
	app.Get("/", accessible)

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

	// Route restreinte
	app.Get("/restricted", restricted)

	app.Listen(":3000")
}

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

	// Envoi d'une erreur non autorisée
	if utilisateur != "john" || pass != "doe" {
		return c.SendStatus(fiber.StatusUnauthorized)
	}

	// Création de revendications
	claims := jwt.MapClaims{
		"nom":  "John Doe",
		"admin": true,
		"exp":   time.Now().Add(time.Hour * 72).Unix(),
	}

	// Création du jeton
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// Génération du jeton encodé et envoi en réponse
	t, err := token.SignedString([]byte("secret"))
	if err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

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

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

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

Test HS256

Connectez-vous avec un nom d'utilisateur et un mot de passe pour obtenir un jeton.

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

Réponse

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

Demander l'accès restreint en utilisant le jeton dans l'en-tête d'autorisation.

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

Réponse

Bienvenue 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 (
	// De toute évidence, ceci n'est qu'un exemple de test. Ne le faites pas en production.
	// En production, vous devriez générer la paire de clés au préalable.
	// N'ajoutez jamais la clé privée à un dépôt GitHub.
	privateKey *rsa.PrivateKey
)

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

	// Pour la démonstration, générez une nouvelle paire de clés privée/publique à chaque exécution du programme. Voir la note ci-dessus.
	rng := rand.Reader
	var err error
	privateKey, err = rsa.GenerateKey(rng, 2048)
	if err != nil {
		log.Fatalf("rsa.GenerateKey:%v", err)
	}

	// Route de connexion
	app.Post("/login", login)

	// Route non authentifiée
	app.Get("/", accessible)

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

	// Route restreinte
	app.Get("/restricted", restricted)

	app.Listen(":3000")
}

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

	// Renvoyer une erreur non autorisée
	if user != "john" || pass != "doe" {
		return c.SendStatus(fiber.StatusUnauthorized)
	}

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

	// Créer un jeton
	token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)

	// Générer un jeton encodé et l'envoyer comme réponse
	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("Bienvenue " + name)
}

Test RS256

RS256 est essentiellement identique au test HS256 ci-dessus.

Test de l'ensemble JWK

Ces tests sont identiques aux tests JWT de base ci-dessus, mais ils nécessitent des ensembles de clés publiques valides au format JWK Set à JWKSetURLs.

Exemple de Custom KeyFunc

La fonction KeyFunc définit une fonction définie par l'utilisateur utilisée pour fournir des clés publiques pour la validation de jetons. Cette fonction est responsable de la validation de l'algorithme de signature et de la sélection de la clé appropriée. Si le jeton est émis par une partie externe, la KeyFunc définie par l'utilisateur peut être utile.

Lorsqu'une KeyFunc définie par l'utilisateur est fournie, SigningKey, SigningKeys et SigningMethod seront ignorés. Il s'agit de l'une des trois options pour fournir des clés de validation de jetons. L'ordre de priorité est le suivant : KeyFunc définie par l'utilisateur, SigningKeys et SigningKey. Si ni SigningKeys ni SigningKey ne sont fournis, cette fonction doit être fournie. Par défaut, la validation de l'algorithme de signature et la sélection de la clé appropriée se font à l'aide de l'implémentation interne.

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) {
		// Vérifier si la méthode de signature est correcte
		if t.Method.Alg() != jwtware.HS256 {
			return nil, fmt.Errorf("Méthode de signature jwt inattendue = %v", t.Header["alg"])
		}

		// TODO Implémenter le chargement personnalisé de la clé de signature, par exemple, charger depuis la base de données
    signingKey := "secret"

		return []byte(signingKey), nil
	}
}