JSON Web Token (JWT) — Auth

JWT (JSON Web Token) adalah standar terbuka (RFC 7519) untuk mentransmisikan informasi antar pihak secara aman dalam bentuk JSON object yang di-sign secara…

JWT (JSON Web Token, standar RFC 7519) adalah token yang ditandatangani server lalu dibawa client di setiap request. Bedanya dengan session: server gak perlu mencari apa pun di store. Dia cukup mengecek tanda tangannya, lalu membaca isinya. Mirip tiket konser berhologram: nama, kursi, dan jam berlakunya tercetak di tiket. Petugas pintu gak menelepon kantor, cukup mengecek hologramnya asli dan jamnya belum lewat. Tapi ingat, siapa pun yang memegang tiket bisa membaca tulisannya.

🧪 Tebakan cepat: payload token Siti berisi "exp": 1791504900. Saat itu Date.now() bernilai 1791504600000. Token-nya sudah kedaluwarsa atau belum? Tebak dulu, jawabannya ada di bagian "exp itu detik, bukan milidetik".

Tiga bagian, dipisah titik

Ini token yang diberikan Portal Sekolah waktu Siti login jam 07.00 WIB, umur 15 menit. Lab membuat token yang sama persis:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwibmFtZSI6IlNpdGkiLCJyb2xlIjoic3R1ZGVudCIsImlhdCI6MTc5MTUwNDAwMCwiZXhwIjoxNzkxNTA0OTAwfQ.wPCIAotJt4FnZM2YEp9xXSLw_qSqX7KOwZkVsszdUQA
Bagian Isinya
Header {"alg":"HS256","typ":"JWT"}
Payload {"sub":"1","name":"Siti","role":"student","iat":1791504000,"exp":1791504900}
Signature 32 byte HMAC-SHA256 dari header.payload, dengan secret milik server

Isi payload disebut claims. Yang standar: sub (subject, siapa pemiliknya, di sini id user), iat (issued at, kapan dibuat), dan exp (expiration, kapan berakhir). iss (penerbit) dan jti (id unik token) juga sering dipakai. name dan role adalah claim buatan Portal Sekolah. Waktunya ditulis sebagai Unix time: jumlah detik sejak 1 Januari 1970 UTC. 1791504000 adalah 9 Oktober 2026 jam 07.00 WIB, dan exp 900 detik sesudahnya, jam 07.15.

Base64url: siapa pun bisa membaca payload

Header dan payload cuma JSON yang dikodekan base64url: Base64 yang + dan /-nya diganti - dan _, tanpa = di ujung, supaya aman ditaruh di URL. Itu pengodean, bukan enkripsi. Siapa pun bisa membacanya tanpa kunci:

function decodeJWT(token) {
  const payload = token.split(".")[1]; // bagian kedua
  const base64 = payload.replace(/-/g, "+").replace(/_/g, "/"); // kembali ke Base64 biasa
  return JSON.parse(atob(base64));
}

const sitiToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwibmFtZSI6IlNpdGkiLCJyb2xlIjoic3R1ZGVudCIsImlhdCI6MTc5MTUwNDAwMCwiZXhwIjoxNzkxNTA0OTAwfQ.wPCIAotJt4FnZM2YEp9xXSLw_qSqX7KOwZkVsszdUQA";
console.log(decodeJWT(sitiToken).role); // "student"

Jadi jangan pernah menaruh password, NIK, atau rahasia apa pun di payload.

Signature: bukti dari server

Signature adalah HMAC-SHA256 dari teks header.payload dengan secret yang cuma diketahui server. Ubah satu huruf di payload, signature-nya gak cocok lagi. Membuatnya dengan Web Crypto:

const enc = new TextEncoder();
const b64url = (bytes) => btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");

async function signJWT(payload, secret) {
  const header = b64url(enc.encode(JSON.stringify({ alg: "HS256", typ: "JWT" })));
  const body = b64url(enc.encode(JSON.stringify(payload)));
  const key = await crypto.subtle.importKey("raw", enc.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
  const sig = await crypto.subtle.sign("HMAC", key, enc.encode(header + "." + body));
  return header + "." + body + "." + b64url(new Uint8Array(sig));
}

Waktu request datang dengan Authorization: Bearer <token>, server memeriksa berurutan, dan berhenti di pemeriksaan pertama yang gagal dengan 401:

  1. Token punya tiga bagian.
  2. alg sama dengan algoritma yang server pakai, HS256. Server yang menentukan, bukan header token.
  3. Server menghitung ulang HMAC dari header.payload dengan secret-nya (crypto.subtle.verify), dan hasilnya harus sama dengan signature di token.
  4. exp masih di masa depan.

Baru setelah keempatnya lolos, sub dan role boleh dipercaya. Kenapa urutan dan tiap pemeriksaan ini penting, dari sisi serangan, dibahas di lesson JWT Security di track Security. Satu akibat yang perlu kamu tahu sebagai pembuat: mengganti secret (rotasi) membuat semua token lama gagal di langkah 3, jadi semua orang harus login ulang.

exp itu detik, bukan milidetik

exp dan iat dalam detik. Date.now() di JavaScript dalam milidetik. Samakan satuannya dulu:

function isExpired(payload) {
  return payload.exp * 1000 <= Date.now(); // tepat di detik exp sudah dianggap habis
}
Nilai
payload.exp * 1000 1791504900000 (07.15)
Date.now() 1791504600000 (07.10)
Hasil isExpired false, masih 300.000 ms alias 5 menit

Jadi token Siti belum kedaluwarsa, masih 5 menit lagi. Itu jawaban tebakannya. Tanpa * 1000, kode membandingkan 1791504900 dengan 1791504600000 dan menganggap semua token sudah lama mati.

Di PHP, time() sudah dalam detik. Token yang berlaku satu jam: 'exp' => time() + 3600, karena 60 menit × 60 detik = 3.600.

Di mana client menyimpan token?

Client mengirim token lewat header Authorization: Bearer <token>. Menyimpannya ada tiga pilihan:

Detailnya ada di lesson Secure Storage di Browser. Apa pun pilihannya, buat umur token pendek, 15 menit misalnya, lalu perpanjang dengan refresh token (lesson berikutnya).

JWT atau session?

Session JWT
Data login Di store server Di token yang dibawa client
Banyak server Butuh store bersama Cukup secret yang sama
Logout atau cabut akses Hapus catatannya, langsung berlaku Token tetap sah sampai exp, kecuali server menyimpan daftar token yang dicabut

⚠️ Jebakan yang sering ketemu

TL;DR: JWT = header.payload.signature, masing-masing base64url. Header dan payload bisa dibaca siapa pun, jadi jangan taruh rahasia. Signature = HMAC-SHA256 dari header.payload dengan secret server. Server memeriksa: tiga bagian, alg yang dia tentukan sendiri, signature, lalu exp, dan menjawab 401 di pemeriksaan pertama yang gagal. exp dan iat dalam detik: payload.exp * 1000 <= Date.now() berarti kedaluwarsa, dan satu jam = time() + 3600. Kirim lewat Authorization: Bearer, simpan sesingkat mungkin, jangan di localStorage kalau bisa.

Yang akan kamu pelajari