Pengenalan JWT

JWT (JSON Web Token) adalah metode yang ringkas dan mandiri untuk otentikasi di web. JWT terdiri dari tiga bagian: header, payload, dan tanda tangan. Header berisi informasi jenis token dan algoritma enkripsi, payload berisi data yang akan dilewatkan (biasanya termasuk beberapa informasi izin dan identifikasi pengguna), dan tanda tangan digunakan untuk memverifikasi keutuhan dan kevalidan token.

JWT terutama digunakan dalam dua skenario:

  1. Otentikasi: Setelah pengguna masuk, setiap permintaan selanjutnya akan menyertakan JWT, memungkinkan pengguna mengakses rute, layanan, dan sumber daya yang diizinkan.
  2. Pertukaran informasi: JWT adalah metode yang baik untuk mentransmisikan informasi dengan aman antara pihak, karena dapat ditandatangani secara digital, misalnya menggunakan pasangan kunci publik/privat.

Format Data JWT

JWT adalah cara ringkas dan aman untuk merepresentasikan informasi yang akan ditukar antara pihak-pihak. Sebuah JWT sebenarnya terdiri dari tiga bagian, dipisahkan oleh titik (.), yaitu Header.Payload.Signature. Selanjutnya, kita akan rincikan format data dari ketiga bagian ini.

1. Header

Header biasanya terdiri dari dua bagian: jenis token—biasanya JWT, dan algoritma tanda tangan atau enkripsi yang digunakan, seperti HMAC SHA256 atau RSA. Header direpresentasikan dalam format JSON dan kemudian dienkripsi menjadi sebuah string menggunakan Base64Url. Contohnya:

{
  "alg": "HS256",
  "typ": "JWT"
}

Setelah dienkripsi, Anda mungkin akan mendapatkan sebuah string seperti ini:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

2. Payload

Payload terdiri dari serangkaian klaim, yang berisi informasi tentang entitas (biasanya pengguna) dan data lainnya. Payload dapat mencakup beberapa klaim bawaan (juga dikenal sebagai Klaim Terdaftar) dan klaim kustom (Klaim Pribadi).

Klaim bawaan mungkin termasuk:

  • iss (Issuer): Penerbit
  • exp (Expiration Time): Waktu kedaluwarsa
  • sub (Subject): Subyek
  • aud (Audience): Audiens
  • iat (Issued At): Waktu diterbitkan
  • nbf (Not Before): Waktu efektif
  • jti (JWT ID): Identitas unik dari JWT

Sebuah payload contoh mungkin terlihat seperti ini (dalam format JSON):

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "iat": 1516239022
}

Informasi ini juga akan dienkripsi dengan Base64Url, dan Anda mungkin akan mendapatkan sebuah string seperti ini:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0

3. Tanda Tangan

Bagian tanda tangan digunakan untuk menandatangani kedua string yang telah dienkripsi sebelumnya, untuk memverifikasi bahwa pesan tersebut tidak mengalami perubahan selama transmisi. Pertama-tama, Anda perlu menentukan sebuah kunci (jika menggunakan algoritma HMAC SHA256), dan kemudian menggunakan algoritma yang tercantum dalam header untuk menandatangani header dan payload.

Misalnya, jika Anda memiliki header dan payload berikut:

HeaderEncoded.HeaderPayload

Pseudocode untuk menandatanganinya menggunakan sebuah kunci mungkin seperti ini:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secretKey)

String yang ditandatangani mungkin akan terlihat seperti ini:

dBjftJeZ4CVPmTaoyL4IiArYfL4kH0jOspm6XwbcJXY

JWT Lengkap

Menggabungkan ketiga bagian ini dengan titik (.) sebagai pemisah membentuk JWT lengkap:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.dBjftJeZ4CVPmTaoyL4IiArYfL4kH0jOspm6XwbcJXY

Standar JWT sangat fleksibel dan dapat menyimpan informasi apa pun yang Anda butuhkan dalam payload (tetapi untuk tujuan mengurangi ukuran JWT dan alasan keamanan, informasi sensitif sebaiknya tidak disimpan), dan memastikan keutuhan informasi ini melalui tanda tangan.

Menginstal library Go JWT

Komunitas Go menyediakan paket github.com/golang-jwt/jwt untuk menangani JWT. Menginstal library ini sangatlah mudah, cukup jalankan perintah berikut di direktori proyek Anda:

go get -u github.com/golang-jwt/jwt/v5

Setelah terinstal, Anda dapat menyertakannya dalam import sebagai berikut:

import "github.com/golang-jwt/jwt/v5"

Membuat token sederhana

Untuk membuat token JWT sederhana menggunakan bahasa Go dan algoritma HS256, Anda perlu mengikuti langkah-langkah berikut:

Pertama, hasilkan objek Token baru, menggunakan algoritma HS256:

token := jwt.New(jwt.SigningMethodHS256)

Selanjutnya, gunakan metode SignedString untuk menghasilkan representasi string dari token ini, dengan melewatkan kunci yang akan Anda gunakan untuk penandatanganan.

var mySigningKey = []byte("rahasia-256-bit-anda")
strToken, err := token.SignedString(mySigningKey)
if err != nil {
    log.Fatalf("Terjadi kesalahan: %v", err)
}
fmt.Println(strToken)

Ini akan menghasilkan token sederhana tanpa ada klaim apapun.

Membuat token dengan parameter

Salah satu fungsi utama JWT adalah membawa informasi. Informasi ini dienkripsi dalam Token melalui klaim. Sebagai contoh, mari buat klaim kustom:

// Struktur Klaim Kustom
type MyClaims struct {
	jwt.RegisteredClaims
	Username string `json:"username"`
	Admin    bool   `json:"admin"`
}

// Buat token dengan klaim kustom
claims := MyClaims{
    RegisteredClaims: jwt.RegisteredClaims{},
    Username: "username_anda",
	Admin: true,
}

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

// Kunci rahasia untuk penandatanganan
var mySigningKey = []byte("rahasia-256-bit-anda")

// Menghasilkan token dalam format string
strToken, err := token.SignedString(mySigningKey)
if err != nil {
    log.Fatalf("Kesalahan terjadi: %v", err)
}

fmt.Println(strToken)

Pada kode di atas, kami telah mendefinisikan struktur MyClaims untuk berisi klaim terdaftar serta beberapa informasi kustom. Kemudian, kami masih menggunakan SignedString untuk menghasilkan string token.

Mem-parsing dan memvalidasi token

Mem-parsing dan memvalidasi JWT sangatlah penting, dan Anda dapat melakukannya seperti berikut:

// Kami akan menggunakan struktur MyClaims yang sama
tokenString := "string-JWT-anda"

// Kami perlu mendefinisikan sebuah fungsi yang akan digunakan paket jwt untuk mem-parsing tokenString
keyFunc := func(t *jwt.Token) (interface{}, error) {
	// Verifikasi bahwa metode penandatanganan yang diharapkan digunakan
	if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
	    return nil, fmt.Errorf("Metode penandatanganan tak terduga: %v", t.Header["alg"])
	}
	// Mengembalikan kunci rahasia untuk token jwt, dalam format []byte, sesuai dengan kunci yang digunakan untuk penandatanganan sebelumnya
	return mySigningKey, nil
}

// Mem-parsing token
claims := &MyClaims{}
parsedToken, err := jwt.ParseWithClaims(tokenString, claims, keyFunc)
if err != nil {
	log.Fatalf("Kesalahan parsing: %v", err)
}

if !parsedToken.Valid {
    log.Fatalf("Token Tidak Valid")
}

// Pada tahapan ini, parsedToken telah diverifikasi, dan kita dapat membaca klaim-klaimnya
fmt.Printf("Pengguna: %s, Admin: %v\n", claims.Username, claims.Admin)

Pada kode di atas, kami menyediakan string token, instansi MyClaims, dan fungsi kunci keyFunc ke fungsi jwt.ParseWithClaims. Fungsi ini akan memvalidasi tanda tangan dan mem-parsing token, mengisi variabel claims jika token valid.