Memisahkan authentication dari authorization: menerapkan Gates berbasis closure dan Policies per-model, $this->authorize() di controller, direktif @can di Blade, serta dasar RBAC/roles di Laravel 13.

Di episode 10 kalian belajar "siapa yang boleh masuk" — itu authentication. Episode ini menjawab pertanyaan berikutnya yang jauh lebih bernuansa: "siapa yang boleh melakukan apa?" — itulah authorization. Sebuah user yang sudah login belum tentu boleh mengedit post orang lain, menghapus data, atau melihat halaman admin.
Menaruh logika izin di dalam controller dengan if berantakan adalah resep bug keamanan. Laravel menyediakan Gates dan Policies sebagai dua pendekatan yang rapi, terpusat, dan mudah diuji.
| Authentication | Authorization |
|---|---|
| Siapa kalian? | Apa yang boleh kalian lakukan? |
| Login, session, kredensial | Izin, role, kepemilikan data |
| Jawaban: identitas | Jawaban: ya/tidak |
| Di episode 10 | Di episode ini |
Mereka berpasangan: authorization selalu berjalan setelah authentication. Tanpa auth, kita tidak punya user untuk diperiksa izinnya.
Gates cocok untuk izin yang tidak terikat pada model tertentu — misalnya akses admin atau fitur global. Definisikan di AppServiceProvider::boot():
use Illuminate\Support\Facades\Gate;
Gate::define('manage-users', function (User $user) {
return $user->is_admin;
});Penggunaan di kode:
if (Gate::allows('manage-users')) {
// tampilkan panel admin
}
if (Gate::denies('manage-users')) {
abort(403);
}Gate menerima user otomatis sebagai argumen pertama, dan bisa menerima data tambahan — misalnya post yang sedang dicek:
Gate::define('update-post', function (User $user, Post $post) {
return $user->id === $post->user_id;
});Policies adalah cara yang lebih terorganisir untuk izin yang terkait erat dengan model. Generate dengan artisan:
php artisan make:policy PostPolicy --model=PostFile yang dihasilkan berisi method siap isi:
<?php
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
class PostPolicy
{
public function view(User $user, Post $post): bool
{
return $user->id === $post->user_id || $user->is_admin;
}
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
public function delete(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
}Laravel 13 memakai auto-discovery: policy dengan nama PostPolicy untuk model Post ditemukan otomatis tanpa registrasi manual.
Di dalam controller, authorize() menyingkat seluruh logika izin menjadi satu baris:
public function update(UpdatePostRequest $request, Post $post)
{
$this->authorize('update', $post);
$post->update($request->validated());
return redirect()->route('posts.index');
}Jika user tidak berhak, Laravel otomatis melempar 403. Dengan Form Request + authorize(), controller jadi sangat bersih dan seluruh kebijakan tersentralisasi di satu file PostPolicy.
Di tampilan, gunakan @can untuk menampilkan/menyembunyikan aksi sesuai izin:
@can('update', $post)
<a href="{{ route('posts.edit', $post) }}">Edit</a>
@endcan
@can('delete', $post)
<form method="POST" action="{{ route('posts.destroy', $post) }}">
@csrf
@method('DELETE')
<button>Hapus</button>
</form>
@endcanTip
Aturan praktis pemisahan: Gates untuk izin global (misalnya manage-users, export-data), Policies untuk izin per model (update Post, delete Comment). Jangan memakai keduanya untuk tujuan yang sama secara tumpang tindih — konsistensi lebih penting daripada kelengkapan.
Laravel tidak punya sistem role bawaan, tapi mudah dibangun dengan relasi:
public function roles()
{
return $this->belongsToMany(Role::class);
}
public function hasRole(string $role): bool
{
return $this->roles()->where('name', $role)->exists();
}Gate::define('edit-settings', function (User $user) {
return $user->hasRole('admin');
});Untuk sistem RBAC yang lebih lengkap (permission, super-admin, cache), package populer seperti Spatie Laravel-Permission menawarkan solusi siap pakai — tapi memahami pola di atas terlebih dahulu sangat disarankan sebelum memakai package.
Authorization mudah diuji karena berbasis pure logic — tidak ada session:
use App\Models\Post;
use App\Models\User;
test('only owner can update post', function () {
$owner = User::factory()->create();
$post = Post::factory()->for($owner)->create();
$this->actingAs($owner)->put("/posts/{$post->id}", [...])->assertOk();
$other = User::factory()->create();
$this->actingAs($other)->put("/posts/{$post->id}", [...])->assertForbidden();
});Inti yang harus dibawa pulang:
$this->authorize('update', $post) di controller, @can di Blade.belongsToMany sebelum naik ke package RBAC.Di episode 12 selanjutnya kita akan membahas validation & error handling — aturan validasi lengkap, custom rules, Validator manual, Exception handler, custom exceptions, dan halaman error HTTP yang ramah pengguna. Sampai jumpa di episode 12!