Autentikasi (siapa kamu) vs Autorisasi (apa yang boleh kamu lakukan) adalah dua hal berbeda. Laravel punya 2 mekanisme utama untuk autorisasi: Gates (simple closure-based) dan Policies (class untuk authorize operasi pada model).
Gates — Untuk Logic Global
Gunakan Gate untuk cek yang tidak terikat ke model tertentu. Definisikan di service provider.
// app/Providers/AppServiceProvider.php (atau AuthServiceProvider)
use Illuminate\Support\Facades\Gate;
public function boot(): void
{
Gate::define("view-admin-panel", function (User $user) {
return $user->is_admin === true;
});
Gate::define("create-product", function (User $user) {
return in_array($user->role, ["admin", "merchant"]);
});
}
Di controller:
public function dashboard()
{
if (! Gate::allows("view-admin-panel")) {
abort(403);
}
// atau lempar exception otomatis:
Gate::authorize("view-admin-panel");
return view("admin.dashboard");
}
Policies — Untuk Authorize Operasi pada Model
Kalau kamu punya rule seperti "user hanya bisa edit/hapus post miliknya", gunakan Policy. Satu class per model, method per aksi.
php artisan make:policy PostPolicy --model=Post
// app/Policies/PostPolicy.php
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
class PostPolicy
{
public function viewAny(User $user): bool
{
return true; // semua user bisa lihat list
}
public function view(User $user, Post $post): bool
{
return $post->is_published || $post->user_id === $user->id;
}
public function create(User $user): bool
{
return $user->email_verified_at !== null;
}
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 || $user->is_admin;
}
}
Cara Pakai Policy
1. Di controller — authorize() method
class PostController
{
public function update(Request $request, Post $post)
{
$this->authorize("update", $post); // throw 403 kalau gagal
$post->update($request->validated());
return $post;
}
public function destroy(Post $post)
{
$this->authorize("delete", $post);
$post->delete();
return response()->noContent();
}
}
2. Di route — ->can() middleware
Route::put("/posts/{post}", [PostController::class, "update"])
->middleware("can:update,post");
can:update,post otomatis cari PostPolicy, panggil update(), pass Auth::user() + Post dari route binding.
3. Di Blade — @can directive
@can("update", $post)
<a href="{{ route("posts.edit", $post) }}">Edit</a>
@endcan
@cannot("delete", $post)
<p>Kamu bukan pemilik post ini.</p>
@endcannot
4. Di API — $user->can() method
if ($request->user()->can("delete", $post)) {
$post->delete();
}
Registering Policy
Laravel 11+ auto-discover policy kalau namanya standard (Model → ModelPolicy di folder app/Policies/). Kalau tidak, daftar manual di AuthServiceProvider:
protected $policies = [
Post::class => PostPolicy::class,
];
before() untuk Super-admin Bypass
Method before() dijalankan dulu sebelum method lainnya. Berguna untuk super-admin yang boleh apapun:
public function before(User $user, string $ability): ?bool
{
if ($user->is_super_admin) {
return true; // boleh semua aksi
}
return null; // lanjut ke method spesifik (view, update, dll)
}
Penting: return null kalau mau lanjut ke method spesifik. Return false akan langsung deny (sedikit berbeda semantik-nya).
Gate vs Policy — Kapan Pakai Apa?
| Gate | Policy | |
|---|---|---|
| Scope | Global / non-model | Terikat ke model |
| Location | Service provider closure | Dedicated class |
| Reusability | Sedang | Tinggi |
| Contoh | "view-admin-panel" | "update Post, delete Post" |
Rule of thumb: kalau rule-nya tentang "boleh tidak user X melakukan aksi Y pada model Z?", pakai Policy. Kalau sekadar "boleh tidak user X akses fitur Y?" (tanpa model), pakai Gate.
Checklist Authorization
- Setiap operasi CRUD pada model punya policy method (view, create, update, delete)
- Controller pakai
$this->authorize(...)atau->middleware("can:...") - Blade views pakai
@canuntuk hide UI yang user tidak punya akses - Jangan hanya mengandalkan hide UI — selalu enforce di backend
- Test policy dengan feature test: user A tidak bisa edit post user B