Fiber JWT

Middleware JWT mengembalikan middleware otentikasi JSON Web Token (JWT). Untuk token yang valid, ia menetapkan pengguna di Ctx.Locals dan memanggil handler berikutnya. Untuk token yang tidak valid, ia mengembalikan error "401 - Unauthorized". Untuk token yang hilang, ia mengembalikan error "400 - Bad Request".

Catatan: Versi Go 1.19 atau lebih tinggi diperlukan

Instalasi

Middleware ini mendukung Fiber v1 dan v2, harap instal sesuai dengan perintah berikut.

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

Tanda Tangan

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

Konfigurasi

Properti Tipe Deskripsi Nilai Default
Filter func(*fiber.Ctx) bool Mendefinisikan fungsi untuk melewati middleware nil
SuccessHandler func(*fiber.Ctx) error Mendefinisikan fungsi untuk dieksekusi saat token valid nil
ErrorHandler func(*fiber.Ctx, error) error Mendefinisikan fungsi untuk dieksekusi saat token tidak valid 401 Invalid or expired JWT
SigningKey interface{} Kunci penandatanganan yang digunakan untuk memverifikasi token. Jika panjang SigningKeys adalah 0, ini digunakan sebagai cadangan nil
SigningKeys map[string]interface{} Pemetaan kunci penandatanganan yang digunakan untuk memverifikasi token dengan bidang kid nil
ContextKey string Kunci konteks yang digunakan untuk menyimpan informasi pengguna dari token di konteks "user"
Claims jwt.Claim Data klaim tambahan yang mendefinisikan konten token jwt.MapClaims{}
TokenLookup string String dalam format : yang digunakan untuk mengurai token "header:Authorization"
AuthScheme string AuthScheme yang digunakan dalam header Authorization. Nilai default ("Bearer") digunakan hanya dengan nilai TokenLookup default "Bearer"
KeyFunc func() jwt.Keyfunc Fungsi yang ditentukan pengguna untuk menyediakan kunci publik untuk verifikasi token jwtKeyFunc
JWKSetURLs []string Potongan unik URL Set Kunci Web JSON (JWK) digunakan untuk mengurai JWT nil

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

	// Rute login
	app.Post("/login", login)

	// Rute yang dapat diakses tanpa otentikasi
	app.Get("/", accessible)

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

	// Rute terbatas
	app.Get("/restricted", restricted)

	app.Listen(":3000")
}

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

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

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

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

	// Menghasilkan token yang dienkripsi dan mengirimkannya sebagai respons
	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)
}

Uji HS256

Masuk dengan nama pengguna dan kata sandi untuk mendapatkan token.

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

Respon

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

Permintaan sumber terbatas menggunakan token dalam header otorisasi.

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

Respon

Selamat datang 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 (
	// Jelas, ini hanya contoh uji. Jangan lakukan ini dalam produksi.
	// Dalam produksi, Anda harus membuat pasangan kunci terlebih dahulu.
	// Jangan pernah menambahkan kunci pribadi ke repositori GitHub mana pun.
	privateKey *rsa.PrivateKey
)

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

	// Untuk demonstrasi, hasilkan pasangan kunci privat/publik baru setiap kali program berjalan. Lihat catatan di atas.
	rng := rand.Reader
	var err error
	privateKey, err = rsa.GenerateKey(rng, 2048)
	if err != nil {
		log.Fatalf("rsa.GenerateKey:%v", err)
	}

	// Rute login
	app.Post("/login", login)

	// Rute tanpa autentikasi
	app.Get("/", accessible)

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

	// Rute terbatas
	app.Get("/restricted", restricted)

	app.Listen(":3000")
}

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

	// Siapkan kesalahan tak terotorisasi
	if user != "john" || pass != "doe" {
		return c.SendStatus(fiber.StatusUnauthorized)
	}

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

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

	// Hasilkan token yang terenkripsi dan kirim sebagai respon
	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("Selamat datang " + name)
}

Uji RS256

RS256 pada dasarnya sama dengan uji HS256 di atas.

Uji Set JWK

Uji ini sama dengan uji JWT dasar di atas, tetapi memerlukan set kunci publik yang valid dalam format Set JWK di JWKSetURLs.

Contoh KeyFunc Kustom

KeyFunc mendefinisikan fungsi yang ditentukan pengguna untuk memberikan kunci publik untuk validasi token. Fungsi ini bertanggung jawab untuk memvalidasi algoritma tanda tangan dan memilih kunci yang tepat. Jika token diterbitkan oleh pihak eksternal, KeyFunc yang ditentukan pengguna dapat bermanfaat.

Ketika KeyFunc yang ditentukan pengguna disediakan, SigningKey, SigningKeys, dan SigningMethod akan diabaikan. Ini adalah salah satu dari tiga opsi untuk menyediakan kunci validasi token. Urutan prioritasnya adalah KeyFunc yang ditentukan pengguna, SigningKeys, dan SigningKey. Jika tidak ada SigningKeys atau SigningKey yang disediakan, fungsi ini harus disediakan. Secara defaultnya adalah untuk memvalidasi algoritma tanda tangan dan memilih kunci yang sesuai menggunakan implementasi internal.

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) {
		// Periksa apakah metode tanda tangan benar
		if t.Method.Alg() != jwtware.HS256 {
			return nil, fmt.Errorf("Metode penandatanganan jwt tidak terduga=%v", t.Header["alg"])
		}

		// TODO Implementasikan muatan kunci tanda tangan kustom, misalnya, muat dari database
    signingKey := "rahasia"

		return []byte(signingKey), nil
	}
}