Fiber JWT

El middleware JWT devuelve un middleware de autenticación de Token Web JSON (JWT). Para un token válido, establece al usuario en Ctx.Locals y llama al siguiente controlador. Para un token no válido, devuelve un error "401 - No autorizado". Para un token faltante, devuelve un error "400 - Solicitud incorrecta".

Nota: Se requiere Go versión 1.19 o superior

Instalación

Este middleware es compatible con Fiber v1 y v2, por favor instale según corresponda.

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

Firma

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

Configuración

Propiedad Tipo Descripción Valor por defecto
Filtro func(*fiber.Ctx) bool Define una función para saltar el middleware nil
Controlador de éxito func(*fiber.Ctx) error Define una función para ejecutar al tener un token válido nil
Controlador de error func(*fiber.Ctx, error) error Define una función para ejecutar al tener un token inválido 401 JWT no válido o caducado
Clave de firma interface{} La clave de firma usada para verificar el token. Si la longitud de SigningKeys es 0, se usa como respaldo nil
Claves de firma map[string]interface{} El mapeo de claves de firma usadas para verificar tokens con un campo kid nil
Clave de contexto string La clave de contexto usada para almacenar la información del usuario a partir del token en el contexto "user"
Reclamaciones jwt.Claim Los datos de reclamaciones extendidas que definen el contenido del token jwt.MapClaims{}
Búsqueda de token string Una cadena en el formato : usada para analizar el token "header:Authorization"
Esquema de autenticación string El esquema de autenticación usado en el encabezado de Autorización. El valor predeterminado ("Bearer") se usa solo con el valor predeterminado de TokenLookup "Bearer"
Función de clave func() jwt.Keyfunc Una función definida por el usuario para proporcionar la clave pública para la verificación del token jwtKeyFunc
URL de JWKSet []string Una lista de URL de conjunto de claves JSON Web (JWK) únicas utilizadas para analizar JWT nil

Ejemplo 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()

	// Ruta de inicio de sesión
	app.Post("/login", login)

	// Ruta accesible sin autenticación
	app.Get("/", accessible)

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

	// Ruta restringida
	app.Get("/restringido", restringido)

	app.Listen(":3000")
}

func login(c *fiber.Ctx) error {
	usuario := c.FormValue("usuario")
	contraseña := c.FormValue("contraseña")

	// Devolver error no autorizado
	if usuario != "juan" || contraseña != "doe" {
		return c.SendStatus(fiber.StatusUnauthorized)
	}

	// Crear reclamaciones
	reclamaciones := jwt.MapClaims{
		"nombre":  "Juan Doe",
		"admin": true,
		"exp":   time.Now().Add(time.Hour * 72).Unix(),
	}

	// Crear token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, reclamaciones)

	// Generar token codificado y enviar como respuesta
	t, err := token.SignedString([]byte("secreto"))
	if err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

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

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

func restringido(c *fiber.Ctx) error {
	usuario := c.Locals("user").(*jwt.Token)
	reclamaciones := usuario.Claims.(jwt.MapClaims)
	nombre := reclamaciones["nombre"].(string)
	return c.SendString("Bienvenido " + nombre)
}

Prueba de HS256

Inicie sesión con nombre de usuario y contraseña para obtener un token.

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

Respuesta

{
  "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"

Respuesta

Bienvenido 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 (
	// Obviamente, esto es solo un ejemplo de prueba. No debes hacer esto en producción.
	// En producción, debes generar el par de claves de antemano.
	// Nunca agregues la clave privada a ningún repositorio de GitHub.
	privateKey *rsa.PrivateKey
)

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

	// Para fines de demostración, genera un nuevo par de claves privada/pública cada vez que se ejecuta el programa. Consulta la nota anterior.
	rng := rand.Reader
	var err error
	privateKey, err = rsa.GenerateKey(rng, 2048)
	if err != nil {
		log.Fatalf("rsa.GenerateKey:%v", err)
	}

	// Ruta de inicio de sesión
	app.Post("/login", login)

	// Ruta no autenticada
	app.Get("/", accessible)

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

	// Ruta restringida
	app.Get("/restricted", restricted)

	app.Listen(":3000")
}

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

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

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

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

	// Generar token codificado y enviarlo como respuesta
	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("Accesible")
}

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

Prueba de RS256

RS256 es esencialmente lo mismo que la prueba HS256 anterior.

Prueba de JWK Set

Estas pruebas son las mismas que las pruebas básicas de JWT anteriores, pero requieren conjuntos de claves públicas válidas en formato JWK Set en JWKSetURLs.

Ejemplo de Custom KeyFunc

La KeyFunc define una función definida por el usuario utilizada para proporcionar claves públicas para la validación del token. Esta función es responsable de validar el algoritmo de firma y seleccionar la clave correcta. Si el token es emitido por una parte externa, la KeyFunc definida por el usuario puede ser útil.

Cuando se proporciona una KeyFunc definida por el usuario, se ignorarán SigningKey, SigningKeys y SigningMethod. Esta es una de las tres opciones para proporcionar claves de validación de tokens. El orden de prioridad es KeyFunc definida por el usuario, SigningKeys y SigningKey. Si no se proporciona ni SigningKeys ni SigningKey, esta función debe ser proporcionada. El valor predeterminado es validar el algoritmo de firma y seleccionar la clave apropiada utilizando la implementación interna.

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) {
		// Verificar si el método de firma es correcto
		if t.Method.Alg() != jwtware.HS256 {
			return nil, fmt.Errorf("Método de firma jwt inesperado=%v", t.Header["alg"])
		}

		// TODO Implementar la carga personalizada de la clave de firma, por ejemplo, cargar desde la base de datos
    signingKey := "secreto"

		return []byte(signingKey), nil
	}
}