JWT do Fiber

O middleware JWT retorna um middleware de autenticação de Token de Web JSON (JWT). Para um token válido, ele define o usuário em Ctx.Locals e chama o próximo manipulador. Para um token inválido, retorna um erro "401 - Não Autorizado". Para um token ausente, retorna um erro "400 - Solicitação Inválida".

Observação: É necessário o Go na versão 1.19 ou superior

Instalação

Este middleware suporta Fiber v1 e v2, por favor, instale de acordo com a versão.

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

Assinatura

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

Configuração

Propriedade Tipo Descrição Valor Padrão
Filtro func(*fiber.Ctx) bool Define uma função para pular o middleware nil
Manipulador de Sucesso func(*fiber.Ctx) error Define uma função a ser executada ao validar o token nil
Manipulador de Erro func(*fiber.Ctx, error) error Define uma função a ser executada ao validar o token como inválido 401 Token JWT inválido ou expirado
Chave de Assinatura interface{} A chave de assinatura usada para verificar o token. Se o comprimento de SigningKeys for 0, é usada como alternativa nil
Chaves de Assinatura map[string]interface{} O mapeamento de chaves de assinatura usadas para verificar tokens com um campo kid nil
Chave de Contexto string A chave de contexto usada para armazenar informações do usuário do token no contexto "user"
Reivindicações jwt.Claim Os dados de reivindicações estendidas que definem o conteúdo do token jwt.MapClaims{}
TokenLookup string Uma string no formato : usada para analisar o token "header:Authorization"
Esquema de Autenticação string O esquema de autenticação usado no cabeçalho Autorização. O valor padrão ("Bearer") é usado apenas com o valor padrão TokenLookup "Bearer"
KeyFunc func() jwt.Keyfunc Uma função definida pelo usuário para fornecer chave pública para verificação de token jwtKeyFunc
JWKSetURLs []string Uma lista de URLs de Conjunto de Chave Web JSON (JWK) exclusivas utilizadas para analisar JWT nil

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

	// Rota de login
	app.Post("/login", login)

	// Rota acessível sem autenticação
	app.Get("/", acessivel)

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

	// Rota restrita
	app.Get("/restrito", restrito)

	app.Listen(":3000")
}

func login(c *fiber.Ctx) error {
	usuário := c.FormValue("usuário")
	senha := c.FormValue("senha")

	// Lançando erro não autorizado
	if usuário != "john" || senha != "doe" {
		return c.SendStatus(fiber.StatusUnauthorized)
	}

	// Criando reivindicações
	reivindicações := jwt.MapClaims{
		"nome":  "John Doe",
		"admin": true,
		"exp":   time.Now().Add(time.Hour * 72).Unix(),
	}

	// Criando token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, reivindicações)

	// Gerando token codificado e enviando como resposta
	t, err := token.SignedString([]byte("secret"))
	if err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

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

func acessivel(c *fiber.Ctx) error {
	return c.SendString("Acessível")
}

func restrito(c *fiber.Ctx) error {
	usuário := c.Locals("user").(*jwt.Token)
	reivindicações := usuário.Claims.(jwt.MapClaims)
	nome := reivindicações["nome"].(string)
	return c.SendString("Bem-vindo " + nome)
}

Teste HS256

Faça login com nome de usuário e senha para obter um token.

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

Resposta

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

Solicitar recurso restrito usando o token no cabeçalho de autorização.

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

Resposta

Bem-vindo 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, este é apenas um exemplo de teste. Não faça isso em produção.
	// Em produção, você deve gerar o par de chaves antecipadamente.
	// Nunca adicione a chave privada a nenhum repositório do GitHub.
	privateKey *rsa.PrivateKey
)

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

	// Para demonstração, gere um novo par de chaves público/privado cada vez que o programa é executado. Veja a observação acima.
	rng := rand.Reader
	var err error
	privateKey, err = rsa.GenerateKey(rng, 2048)
	if err != nil {
		log.Fatalf("rsa.GenerateKey:%v", err)
	}

	// Rota de login
	app.Post("/login", login)

	// Rota não autenticada
	app.Get("/", acessível)

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

	// Rota restrita
	app.Get("/restricted", restrito)

	app.Listen(":3000")
}

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

	// Lançar erro não autorizado
	if user != "john" || pass != "doe" {
		return c.SendStatus(fiber.StatusUnauthorized)
	}

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

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

	// Gerar token codificado e enviá-lo como resposta
	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("Acessível")
}

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

Teste RS256

O RS256 é essencialmente o mesmo que o teste HS256 acima.

Teste de Conjunto JWK

Esses testes são os mesmos que os testes básicos de JWT acima, mas exigem conjuntos de chaves públicas válidas no formato de Conjunto JWK em JWKSetURLs.

Exemplo de Custom KeyFunc

O KeyFunc define uma função definida pelo usuário usada para fornecer chaves públicas para validação do token. Esta função é responsável por validar o algoritmo de assinatura e selecionar a chave correta. Se o token for emitido por uma parte externa, o KeyFunc definido pelo usuário pode ser útil.

Quando um KeyFunc definido pelo usuário é fornecido, o SigningKey, SigningKeys e SigningMethod serão ignorados. Esta é uma das três opções para fornecer chaves de validação de token. A ordem de prioridade é KeyFunc definido pelo usuário, SigningKeys e SigningKey. Se nenhum SigningKeys nem SigningKey for fornecido, esta função deve ser fornecida. O padrão é validar o algoritmo de assinatura e selecionar a chave apropriada usando a implementação 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) {
		// Verifica se o método de assinatura está correto
		if t.Method.Alg() != jwtware.HS256 {
			return nil, fmt.Errorf("Método de assinatura inesperado=%v", t.Header["alg"])
		}

		// TODO Implementar o carregamento personalizado da chave de assinatura, por exemplo, carregar do banco de dados
    signingKey := "secreto"

		return []byte(signingKey), nil
	}
}