Token JWT-nya baru dibuat, exp-nya masih besok, tapi server balas 401. Di-decode isinya kelihatan normal.
Empat sebab berikut nyakup hampir semua kejadian, diurutin dari yang paling sering.
1. Detik dibaca sebagai milidetik
exp dan iat di JWT satuannya detik sejak 1 Januari 1970 — bukan milidetik.
JavaScript ngasih milidetik lewat Date.now(). Kalau nilainya dimasukin apa adanya, exp-nya jadi ribuan tahun ke depan, dan sebagian pustaka nolak token yang tanggalnya nggak masuk akal.
Kebalikannya lebih sering: pustaka pemeriksa ngeliat angka yang kegedean, ngebandingin sama waktu sekarang dalam detik, terus ngambil kesimpulan yang salah.
Cara ngecek cepat: exp yang bener panjangnya sepuluh digit sampai tahun 2286. Kalau tiga belas digit, itu milidetik.
2. Tanda tangannya nggak cocok, bukan tokennya kedaluwarsa
Pesan errornya sering nyesatin. Banyak pustaka bilang "invalid token" buat dua hal yang beda jauh: kedaluwarsa, dan tanda tangan salah.
Tanda tangan salah biasanya karena:
- Secret beda antar server. Token dibikin server A, diperiksa server B, dan
.env-nya nggak sama. Sering banget kejadian sesudah deploy atau pas nambah server kedua di belakang load balancer. - Secret-nya punya spasi atau baris baru di ujung. Kesalinan waktu tempel dari catatan. Dua secret yang keliatan sama persis di layar bisa beda satu aksara tak terlihat.
- Kuncinya udah dirotasi, dan token lama masih beredar di sisi pengguna.
Bedain dulu keduanya sebelum ngubek-ngubek waktu: kalau tanda tangannya salah, ngutak-atik exp nggak akan pernah nolong.
3. Jam server geser
Pemeriksaan exp dan nbf bandingin sama jam server pemeriksa. Kalau jam server itu maju beberapa menit, token yang baru dibuat bisa langsung dianggap kedaluwarsa. Kalau mundur, token yang pakai nbf ditolak karena dianggap "belum berlaku".
Gejalanya khas dan bikin bingung: cuma sebagian permintaan yang gagal, tergantung server mana yang kebagian nanganin. Di belakang load balancer, ini keliatan acak total.
Pastiin NTP jalan di semua server. Sebagian pustaka nyediain toleransi beberapa detik — pakai secukupnya, jangan buat nutupin jam yang emang nggak diurus.
4. Tokennya kepotong sebelum sampai
JWT gampang jadi panjang, apalagi kalau payload-nya diisi daftar hak akses.
Yang motong biasanya:
- Cookie dibatasi sekitar 4 KB per cookie. Lewat dari itu, browser buang diam-diam.
- Header dibatasi server web. Nginx punya
large_client_header_buffers; kalau lewat, jawabannya 400 atau 431, bukan 401. - Salin tempel yang kepotong pas nguji lewat curl.
Cara ngeceknya gampang: JWT sah selalu punya dua titik. Kalau yang nyampe cuma satu titik atau nol, tokennya emang kepotong, dan nggak ada gunanya nyari di sisi logika.
Yang perlu diluruskan: decode bukan verifikasi
Bagian payload JWT itu base64url, bukan enkripsi. Siapa pun yang megang tokennya bisa baca isinya tanpa kunci apa pun.
Artinya dua hal:
- Jangan naruh apa pun yang rahasia di payload. Nomor pelanggan boleh; nomor kartu jelas nggak.
- Kalau kode Anda cuma nge-decode payload terus percaya isinya, Anda nggak lagi mereriksa apa-apa. Siapa pun bisa ngirim token bikinan sendiri dengan
"role": "admin"di dalamnya.
Dan yang paling berbahaya: selalu kunci algoritmanya di sisi pemeriksa. Ada serangan lama tapi masih laku, yaitu ngirim token dengan header "alg": "none". Pustaka yang percaya begitu aja sama header dari token bakal nerima token tanpa tanda tangan sama sekali.
Pembaca dan Pemeriksa JWT mbaca header dan payload, nampilin exp sama iat dalam zona waktu Anda, sekalian ngeverifikasi tanda tangan HS256 — dan seluruhnya jalan di browser, tokennya nggak pernah dikirim ke mana-mana.
Bacaan lanjutan: Urutan pasang header keamanan situs.