Dalam percakapan sehari-hari, kata “auth” sering dipakai untuk dua hal yang sebenarnya berbeda: memastikan siapa penggunanya, dan menentukan apa yang boleh dilakukan pengguna tersebut. Pencampuran ini bukan sekadar soal istilah — aplikasi yang menganggap “sudah login berarti boleh segalanya” adalah aplikasi yang rawan lubang keamanan. Laravel menyediakan tool yang berbeda untuk kedua masalah itu: Auth untuk authentication, serta Gate dan Policy untuk authorization. Chapter ini memisahkan keduanya dengan tegas agar kita tidak pernah salah memilih tool.

Dua Pertanyaan yang Berbeda #

Authentication menjawab “siapa Anda”: pengguna menyerahkan kredensial, aplikasi mencocokkannya dengan data di database, dan hasilnya adalah identitas yang sudah terverifikasi. Authorization menjawab “apa yang boleh Anda lakukan”: setelah identitas diketahui, aplikasi mengevaluasi izin — apakah user ini pemilik data ini? apakah user ini admin?

Analogi hotel membantu memetakan hal ini: resepsionis memeriksa paspor saat check-in adalah authentication, sedangkan kartu kunci yang hanya membuka kamar milik tamu tersebut adalah authorization. Batas analoginya juga perlu kita sepakati — di web, kedua pemeriksaan itu dievaluasi ulang pada setiap request, bukan sekali saat check-in, dan “kartu kunci” di web berupa logika di server, bukan benda fisik.

Dalam kode, authentication bekerja lewat attempt terhadap kredensial:

php
$credentials = $request->validate([
    'email' => ['required', 'email'],
    'password' => ['required', 'string'],
]);

if (Auth::attempt($credentials)) {
    // identitas terbukti: user dikenali lewat session
}

Sementara authorization di Laravel dievaluasi lewat Gate (atau Policy untuk resource yang lebih kompleks):

php
use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Gate;

Gate::define('update-post', function (User $user, Post $post) {
    return $user->id === $post->user_id;
});

if (Gate::allows('update-post', $post)) {
    // user dikenali DAN punya izin atas post ini
}

Perhatikan parameter $user pada closure Gate: user tersebut sudah dijamin ter-authentication oleh Gate sendiri — Gate untuk guest otomatis mengembalikan false tanpa closure dijalankan.

Mengapa Pemisahan Ini Penting #

Bug klasik akibat pencampuran dua konsep ini mudah kita temukan. Bayangkan tombol “Hapus Post” disembunyikan untuk user yang bukan pemilik — itu hanyalah authorization di lapisan tampilan. Bila route DELETE /posts/{post} tetap bisa dipanggil langsung oleh user yang login, lubang keamanan pun terbuka: menonaktifkan tombol di browser tidak pernah menonaktifkan route di server. Kebalikannya juga terjadi: controller yang hanya memeriksa Auth::check() lalu mengizinkan pengubahan data apa pun sedang memperlakukan authentication seolah-olah authorization.

Karena itu aturannya sederhana: authentication ditegakkan di middleware (route tanpa identitas ditolak), authorization ditegakkan di route, controller, atau Gate/Policy (identitas ada, tapi izin ditolak). Ringkasan kontrasnya:

AspekAuthenticationAuthorization
PertanyaanSiapa penggunanya?Apa yang boleh dilakukan?
PrasyaratIdentitas belum diketahuiIdentitas sudah diketahui
Alat LaravelAuth::attempt, session, tokenGate, Policy, middleware otorisasi
Kegagalan khasHTTP 401 atau redirect ke loginHTTP 403 Forbidden
Contoh“Apakah ini benar Budi?”“Bolehkah Budi mengubah post Siti?”

Satu Skenario, Dua Keputusan #

Kita buktikan pemisahan itu dengan satu skenario yang sama, dievaluasi lewat runner. Skenarionya: sebuah post milik Budi (user dengan id 1), lalu tiga pihak mencoba mengubahnya — tamu tanpa identitas, Budi, dan Siti:

php
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;

$budi = new User(['name' => 'Budi', 'email' => '[email protected]']);
$budi->id = 1;
$siti = new User(['name' => 'Siti', 'email' => '[email protected]']);
$siti->id = 2;

$post = new class {
    public int $user_id = 1;
    public string $judul = 'Belajar Authentication';
};

Gate::define('update-post', function (User $user, $post) {
    return $user->id === $post->user_id;
});

echo 'tamu : check='.var_export(Auth::check(), true)
    .', allows='.var_export(Gate::allows('update-post', $post), true)."\n";

Auth::setUser($budi);
echo 'budi : check='.var_export(Auth::check(), true)
    .', allows='.var_export(Gate::allows('update-post', $post), true)."\n";

Auth::setUser($siti);
echo 'siti : check='.var_export(Auth::check(), true)
    .', allows='.var_export(Gate::allows('update-post', $post), true)."\n";

Output yang Dihasilkan:

text
tamu : check=false, allows=false
budi : check=true, allows=true
siti : check=true, allows=false

Tiga baris hasil tersebut memetakan tiga kondisi yang berbeda. Tamu gagal di tahap authentication — Gate::allows langsung false tanpa closure pernah dievaluasi. Budi lolos dua tahap: dikenali sebagai user, dan id-nya cocok dengan user_id post. Siti lolos authentication tetapi gagal authorization — check bernilai true sementara allows bernilai false. Method Auth::setUser() di sini dipakai untuk memasang identitas secara manual agar demo berjalan di CLI; dalam aplikasi nyata identitas tersebut diperoleh lewat Auth::attempt yang kita bahas di chapter Login dan Logout .

Dengan konsep yang kini terpisah rapi, langkah berikutnya adalah melihat di mana seluruh mesin ini dikonfigurasi: file config/auth.php menentukan guard, provider, sampai kebijakan reset password. Kita bedah isi aslinya di chapter Konfigurasi Authentication . Gate dan Policy sendiri akan dibahas tuntas di dokumentasi Authorization serta section Authorization Course ini.