Setiap request ke halaman yang butuh login dijawab server dengan dua pertanyaan, berurutan. Authentication (disingkat AuthN): "kamu siapa?". Authorization (AuthZ): "kamu boleh ngapain?". Bayangin sekolah: satpam di gerbang mengecek kartu pelajar, itu authentication. Di dalam, ruang guru cuma boleh dimasuki guru, itu authorization. Kartu pelajar Siti asli, tapi tetap gak bikin dia boleh masuk ruang guru.
🧪 Tebakan cepat: Siti sudah login sebagai siswa, lalu mencoba mengisi nilai. Server harus menjawab
401atau403? Tebak dulu, jawabannya ada di bagian "401 atau 403".
Contoh yang dipakai sepanjang track: Portal Sekolah
Semua lesson di track Auth memakai satu aplikasi kecil, Portal Sekolah di https://portal.sekolah.test. Penggunanya tiga:
| id | Nama | Role | |
|---|---|---|---|
| 1 | Siti | [email protected] |
student (siswa) |
| 2 | Pak Andi | [email protected] |
teacher (guru) |
| 3 | Bu Dewi | [email protected] |
admin |
Route-nya juga tiga. Role adalah peran pengguna, dan permission adalah izin untuk satu jenis aksi:
| Route | Gunanya | Permission yang dibutuhkan | Siapa yang punya |
|---|---|---|---|
GET /students/1/grades |
lihat rapor Siti | grades:read |
Siti (rapornya sendiri), guru, admin |
POST /grades |
isi nilai | grades:write |
guru, admin |
DELETE /users/4 |
hapus akun Rizky, alumni | users:delete |
admin |
Authentication: kamu siapa?
Authentication membuktikan identitas. Caranya: kamu mengirim credential (bukti diri, biasanya email dan password) ke POST /login. Server mencocokkannya. Kalau cocok, server memberi browser sebuah tanda login: session cookie atau token. Browser mengirim tanda itu di setiap request berikutnya, jadi server tahu "ini Siti" tanpa menanyakan password lagi. Cara menyimpan dan membaca tanda itu dibahas di lesson Session, JWT, dan PASETO.
Request tanpa tanda login, atau dengan tanda yang sudah gak berlaku, gagal di pertanyaan pertama.
Authorization: kamu boleh ngapain?
Setelah server tahu siapa kamu, baru dia mengecek izin. Cara paling umum: tiap role punya daftar permission.
const PERMISSIONS = {
student: ["grades:read"],
teacher: ["grades:read", "grades:write"],
admin: ["grades:read", "grades:write", "users:delete"],
};
function authorize(user, permission) {
if (!user) return false; // belum login: gak punya izin apa pun
const allowed = PERMISSIONS[user.role] || [];
return allowed.includes(permission);
}
function handle(user, permission) {
if (!user) return 401; // 1. authentication gagal
if (!authorize(user, permission)) return 403; // 2. authorization gagal
return 200;
}
const siti = { id: 1, name: "Siti", role: "student" };
const andi = { id: 2, name: "Pak Andi", role: "teacher" };
console.log(handle(null, "grades:write")); // 401
console.log(handle(siti, "grades:write")); // 403
console.log(handle(andi, "grades:write")); // 200
PERMISSIONS[user.role] || []: role yang gak dikenal dapat daftar kosong, jadi ditolak, bukan error.if (!user) return falsediauthorize: tanpa baris ini,user.rolepadanullmelempar errorCannot read properties of null.- Urutan di
handlepenting: cek siapa dulu, baru cek boleh apa. Izin gak bisa dicek untuk orang yang belum dikenal.
Hasil semua kombinasi di Portal Sekolah, persis seperti yang dijawab Lab:
| Yang mengirim | GET /students/1/grades |
POST /grades |
DELETE /users/4 |
|---|---|---|---|
| Tamu (belum login) | 401 | 401 | 401 |
Siti (student) |
200 | 403 | 403 |
Pak Andi (teacher) |
200 | 201 | 403 |
Bu Dewi (admin) |
200 | 201 | 204 |
Yang lolos dijawab oleh route-nya sendiri: 200 OK untuk rapor, 201 Created karena nilainya tersimpan, 204 No Content karena akunnya terhapus tanpa body jawaban. Di handle tadi, semua yang lolos disederhanakan jadi 200.
401 atau 403
401 Unauthorized: namanya menyesatkan, artinya justru belum ter-authenticate. Server gak tahu kamu siapa: belum login, token-nya kedaluwarsa, atau tanda login-nya rusak. Jawabannya disertai headerWWW-Authenticate. Client yang menerima 401 mengarahkan ke halaman login.403 Forbidden: server tahu kamu siapa, tapi kamu gak punya izin. Login ulang gak membantu. Client menampilkan "akses ditolak".
Siti sudah login, jadi pertanyaan pertama lolos. Dia gagal di pertanyaan kedua karena student gak punya grades:write. Jadi jawabannya 403. Itu jawaban tebakannya.
Di Laravel
Laravel memisahkan dua pertanyaan ini dengan rapi. Middleware auth menjawab "kamu siapa?" dan menolak dengan 401. Gate dan Policy menjawab "boleh apa?" dan menolak dengan 403.
// app/Providers/AppServiceProvider.php, di method boot()
Gate::define('delete-user', fn (User $user) => $user->role === 'admin');
// routes/api.php
Route::delete('/users/{user}', [UserController::class, 'destroy'])
->middleware(['auth', 'can:delete-user']);
Di dalam middleware buatanmu sendiri, cek izinnya pakai cannot:
if ($request->user()->cannot('delete-user')) {
abort(403);
}
can('delete-user')bernilaitruekalau user punya izin itu,cannot(...)kebalikannya.- Tamu ditolak middleware
authdengan{"message": "Unauthenticated."}(401). User yang dikenal tapi gak berizin ditolak denganThis action is unauthorized.(403).
BelajarWeb.dev sendiri bekerja begitu: route yang butuh login dipasang auth:api, dan guard-nya membaca token PASETO dari header Authorization.
📌 Peta track ini
Lesson berikutnya membangun bagian-bagiannya satu per satu: menyimpan password dengan aman (Password Hashing), mengingat siapa yang login lewat session di server, atau lewat token yang dibawa client (JWT dan PASETO), lalu refresh token, OAuth, dan role yang lebih rinci.
Untuk SPA React dengan API Laravel, tanpa login Google, susunan yang sehat biasanya: access token pendek (misalnya PASETO, umur 15 menit) di memori aplikasi, plus refresh token panjang (misalnya 30 hari) yang opaque, artinya string acak tanpa isi yang bisa dibaca, di cookie HttpOnly yang gak bisa dibaca JavaScript. Kalau access token bocor, umurnya cuma 15 menit. JWT umur 30 hari di localStorage justru kebalikannya: bisa dicuri script mana pun di halaman, dan berlaku lama.
Sisi serangannya ada di track Security: lesson Password Hashing & Storage, Session Management, Cookie Security, dan JWT Security. Track ini fokus ke membangunnya dengan benar.
⚠️ Jebakan yang sering ketemu
- Cek izin cuma di frontend. Menyembunyikan tombol "Hapus" dari siswa itu bagus untuk tampilan, tapi request bisa dikirim tanpa tombol. Server wajib mengecek izin di setiap request.
- 401 dan 403 tertukar. Kalau server menjawab 401 ke Siti yang sudah login, aplikasinya melempar dia ke halaman login berulang-ulang.
- Lupa kasus belum login.
authorize(null, "read")harus mengembalikanfalse, bukan melempar error. - Cek role saja, lupa pemilik data.
studentbolehgrades:read, tapi cuma rapornya sendiri. Siti yang membukaGET /students/4/grades(rapor Rizky) harus ditolak 403.
TL;DR: Authentication menjawab "kamu siapa?" (login, lalu tanda login di setiap request). Authorization menjawab "kamu boleh ngapain?" (role, permission, pemilik data). Cek siapa dulu, baru izinnya. Gak dikenal: 401 Unauthorized. Dikenal tapi gak berizin: 403 Forbidden. Di Laravel: middleware auth untuk yang pertama, Gate/Policy dengan can dan cannot untuk yang kedua. Semua pengecekan terjadi di server.