401 vs 403: Bedah Tuntas Dua Error Auth
Memahami perbedaan mendalam 401 dan 403, alur refresh token, dan pola penanganan di frontend.
Belum punya kartu vs kartu ditolak satpam
Dua skenario di gedung perkantoran: kamu datang tanpa kartu akses sama sekali (satpam memintamu mendaftar dulu), atau kamu punya kartu tapi mencoba masuk ruangan server yang bukan hakmu (satpam tahu siapa kamu, tapi menolak). Yang pertama adalah 401 Unauthorized: "perkenalkan dirimu dulu." Yang kedua adalah 403 Forbidden: "saya tahu siapa kamu, tapi kamu tidak boleh masuk." Tertukar keduanya adalah sumber bug auth paling umum.
Kenapa pembedaan ini penting untuk frontend
Respons frontend terhadap keduanya beda total. Menerima 401, aplikasi boleh mencoba refresh token secara diam-diam; kalau gagal baru arahkan ke halaman login. Menerima 403, refresh token tidak ada gunanya: user memang tidak punya izin. Aplikasi harus menampilkan "kamu tidak punya akses" bukan menendang ke login berulang-ulang (yang membuat user frustrasi).
Contoh 1: alur 401 dengan refresh token
async function apiDenganAuth(url) {
let response = await fetch(url, {
headers: { "Authorization": "Bearer " + accessToken }
});
if (response.status === 401) {
const ok = await refreshToken();
if (!ok) {
window.location.href = "/login";
return;
}
response = await fetch(url, {
headers: { "Authorization": "Bearer " + accessToken }
});
}
return response;
}Pola ini disebut silent refresh: user tidak sadar tokennya diperbarui. Hanya kalau refresh gagal (refresh token juga kedaluwarsa), user diarahkan login.
Contoh 2: menangani 403 dengan benar
const response = await fetch("/api/admin/laporan", {
headers: { "Authorization": "Bearer " + token }
});
if (response.status === 403) {
tampilkanHalaman("Akses ditolak: halaman ini khusus admin.");
}Jangan redirect ke login: user sudah login, masalahnya izin. Tampilkan halaman 403 yang jelas, mungkin dengan tombol "kembali" atau "minta akses". Di sisi UX, lebih baik lagi sembunyikan menu admin sejak awal kalau peran user bukan admin, sehingga 403 jarang terlihat.
Kesalahan umum
Salah: melempar ke login setiap menerima 403. User yang sudah login dipaksa login lagi, tetap ditolak, frustrasi. Yang benar: 403 tampilkan halaman akses ditolak.
Salah: retry tanpa batas saat 401. Refresh token gagal, kode mencoba lagi, gagal lagi, infinite loop. Yang benar: satu kali percobaan refresh, gagal berarti logout.
Salah: menyimpan token baru tapi lupa update yang lama. Race condition: dua request 401 bersamaan, dua-duanya refresh, token kedua menimpa yang pertama. Yang benar (level lanjut): antrekan request selama refresh berlangsung, atau pakai mutex sederhana.
Kartu contekan 401 vs 403
Tempel di dinding:
- 401: "Siapa kamu?" -> belum login / token hilang / token kedaluwarsa -> aksi: refresh token, kalau gagal arahkan ke login.
- 403: "Kamu tidak boleh." -> sudah login tapi tidak punya izin -> aksi: tampilkan halaman akses ditolak, JANGAN arahkan ke login.
Satu kalimat pengingat: 401 soal identitas, 403 soal izin. Kalau kamu masih menebak-nebak, buka tab Network dan lihat apakah request membawa header Authorization atau tidak.
Catatan teknis: Nama
401 Unauthorizedsebenarnya menyesatkan; arti tepatnya "unauthenticated" (belum terautentikasi). Istilah ini warisan sejarah HTTP yang tidak bisa diubah karena kompatibilitas. Ingat saja: 401 = siapa kamu? 403 = kamu tidak boleh.
Tantangan
Simulasikan penanganan 401 dan 403
Dengan JSONPlaceholder (yang tidak punya auth), simulasikan di kode:
- Tulis fungsi yang memanggil fetch dan mengembalikan pesan berbeda untuk status 401, 403, dan 200.
- Untuk 401: tulis alur "coba refresh sekali, gagal -> redirect /login".
- Untuk 403: tulis apa yang ditampilkan ke user (bukan redirect login).
Tulis sebagai satu blok JavaScript dengan komentar.