Belajar Laravel #11: Authorization — Gate & Policy, Pagar Kedua Setelah Login

Di episode sebelumnya saya belajar bagaimana Laravel mengenali siapa yang sedang login — itu disebut autentikasi. Hari ini saya lanjut satu level: setelah tahu siapa penggunanya, bagaimana caranya memastikan pengguna itu berhak melakukan aksi tertentu? Inilah yang disebut otorisasi. Saya baru sadar, dua konsep ini sering dicampur, padahal bedanya tegas: autentikasi menjawab “kamu siapa?”, otorisasi menjawab “kamu boleh apa?”. Di Laravel, otorisasi diurus oleh dua alat: Gate dan Policy. Ini catatan saya.

Diagram alur autentikasi vs otorisasi di Laravel
Autentikasi dulu, baru otorisasi. Keduanya berjalan berurutan di setiap request.

Pertama, Bedakan Dulu: Authentication vs Authorization

Analogi yang paling membekas buat saya: autentikasi itu KTP, otorisasi itu izin masuk ruangan. KTP membuktikan siapa saya. Tapi punya KTP tidak otomatis membuat saya berhak masuk ruang server — itu urusan tersendiri. Di aplikasi web, seorang pengguna yang sudah login (terautentikasi) belum tentu boleh mengedit postingan milik orang lain (terotorisasi). Episode 10 kemarin membahas KTP-nya (guard, hash, alur login). Sekarang saya membahas pintu-pintunya.

Gate: Cek Izin Ringan dengan Closure

Gate adalah cara paling sederhana untuk mendefinisikan izin. Bentuknya closure yang menerima user dan mengembalikan true/false. Dokumentasi resmi menyebutnya “closure-based approach” — cocok untuk kemampuan yang tidak terikat pada model tertentu, misalnya “boleh melihat dashboard admin” atau “boleh mengakses laporan billing”. Gate biasanya didaftarkan di App\Providers\AppServiceProvider:

use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    Gate::define('view-admin-dashboard', function (User $user) {
        return $user->is_admin;
    });
}

Lalu di controller atau Blade, saya cek dengan facade Gate atau helper can:

if (Gate::allows('view-admin-dashboard')) {
    // tampilkan dashboard
}

// di Blade:
@can('view-admin-dashboard')
    <a href="/admin">Buka Admin</a>
@endcan

Kalau user tidak berhak dan saya ingin langsung menghentikan request, ada Gate::authorize — kalau gagal, Laravel otomatis melempar 403:

Gate::authorize('view-admin-dashboard');

Policy: Aturan Per Model yang Lebih Terstruktur

Kalau izinnya berkutat di satu model — misalnya “bolehkah user ini mengupdate Post dengan ID 5?” — Gate akan cepat berantakan. Di situ Laravel menyediakan Policy: kelas yang mengelompokkan logika otorisasi per model, mirip controller yang mengelompokkan rute. Documentation-nya memakai analogi “gates like routes, policies like controllers”. Buat policy dengan artisan:

php artisan make:policy PostPolicy --model=Post

Perintah dengan --model=Post sekaligus mengisi metode bawaan: viewAny, view, create, update, delete, restore, forceDelete. Contoh metode update yang memastikan hanya pemilik post yang bisa mengedit:

use App\Models\Post;
use App\Models\User;

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

Laravel memetakan policy ke model secara otomatis selama nama kelasnya {NamaModel}Policy dan modelnya memakai trait App\Models\Post standar. Kalau perlu mapping manual, bisa didaftarkan di AuthServiceProvider (meski di Laravel 11+ provider ini tidak lagi otomatis dibuat — auto-discovery sudah cukup).

Memakai Policy di Controller dan Blade

Di controller, cara yang saya pakai paling sering: helper authorize di dalam method, dengan model sebagai argumen. Instance user diambil otomatis dari guard yang sedang aktif:

use App\Models\Post;

public function update(Request $request, Post $post)
{
    $this->authorize('update', $post);

    // hanya sampai sini kalau user adalah pemilik post
    $post->update($request->validated());
}

Catatan penting dari yang saya baca di dokumentasi Laravel 13: controller di versi terbaru tidak lagi mewarisi AuthorizesRequests secara otomatis seperti dulu, jadi sebagian developer memilih memanggil Gate::authorize('update', $post) secara eksplisit. Keduanya sah — yang penting konsisten. Di Blade, pengecekannya begini:

@can('update', $post)
    <a href="{{ route('posts.edit', $post) }}">Edit</a>
@endcan

Kapan Memakai Gate, Kapan Policy?

Dari pengalaman belajar saya, patokan praktisnya begini:

  • Gate → izin yang tidak terikat model: “bisa lihat laporan”, “bisa akses mode admin”, “bisa export data”. Cukup satu closure.
  • Policy → izin per model: “bisa update Post ini”, “bisa hapus Comment ini”. Begitu logika mulai menyangkut kepemilikan data, pindah ke policy.

Ada juga Gate::before untuk administrator super yang “menembus” semua aturan, dan Gate::forUser($user) kalau ingin mengecek atas nama user tertentu (misalnya dari command Artisan/queue). Belum saya kuasai penuh, tapi saya catat dulu untuk eksplorasi berikutnya.

Kesimpulan Saya

Otorisasi adalah pagar kedua setelah login. Tanpa pagar ini, aplikasi yang “sudah bisa login” masih bocor: siapa pun yang login bisa menghapus data siapa pun. Gate memberi pagar cepat untuk kemampuan umum, policy memberi pagar rapi per model. Menurut saya kombinasi keduanya (Gate untuk aksi global, Policy untuk kepemilikan data) adalah pola yang paling masuk akal untuk proyek kecil sampai menengah. Episode depan saya mau coba praktik: bikin CRUD sederhana yang menggabungkan migration, Eloquent, form, dan otorisasi ini sekaligus.

Sumber

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *

Situs ini menggunakan Akismet untuk mengurangi spam. Pelajari bagaimana data komentar Anda diproses