Laravel Auth Guards Gates Policies Authorization Flow
Definisi
Di Laravel, autentikasi dan otorisasi dibagi ke beberapa lapisan yang kelihatannya mirip, tapi fungsinya berbeda.
- Auth adalah payung besar untuk mekanisme identitas dan akses pengguna.
- Guard menentukan bagaimana Laravel mengenali siapa user yang sedang aktif.
- Gate adalah aturan otorisasi berbasis kemampuan/aksi, biasanya sederhana dan tidak selalu terikat ke satu model tertentu.
- Policy adalah aturan otorisasi yang terstruktur per model atau resource.
- Authorization flow adalah urutan keputusan dari request masuk, user dikenali, lalu akses diizinkan atau ditolak.
Kalau disederhanakan:
- authentication menjawab: siapa kamu?
- authorization menjawab: kamu boleh melakukan apa?
Kenapa topik ini penting
Di Laravel, auth sering terlihat mudah karena banyak hal sudah disediakan oleh framework. Tapi kalau pemisahannya kabur, masalahnya cepat muncul:
- login berjalan, tapi akses ke resource masih bocor
- middleware auth dipakai untuk semua hal, padahal sebagian harusnya policy
- gate dibuat untuk aturan yang sebenarnya per-model
- policy dipanggil terlambat, sehingga controller jadi terlalu gemuk
- error 403 dan 401 tertukar
Memahami pembagian ini bikin akses kontrol lebih rapi, aman, dan gampang dirawat.
Alur besar authentication dan authorization
Urutannya penting:
- request masuk
- Laravel mencoba mengenali user lewat guard
- kalau user tidak terautentikasi, request diputus lebih awal
- kalau user valid, authorization dijalankan
- kalau lolos, controller boleh lanjut
Auth di Laravel
Auth bukan satu kelas tunggal yang menyelesaikan semua hal. Ia adalah ekosistem mekanisme login, session, guard, user retrieval, dan pengecekan status user.
Yang sering dipakai sehari-hari:
auth()helperAuth::user()Auth::check()Auth::id()- middleware
auth - guard
webdanapi
Contoh paling umum:
if (Auth::check()) {
$user = Auth::user();
}Atau di controller:
public function index()
{
$user = auth()->user();
}Guard
Definisi
Guard adalah cara Laravel menentukan user mana yang sedang dianggap aktif pada request tertentu.
Guard menjawab pertanyaan seperti:
- apakah request ini pakai session cookie?
- apakah request ini pakai token?
- user dari provider mana yang harus diambil?
Dengan kata lain, guard adalah lapisan identitas, bukan aturan izin.
Kapan dipakai
Guard dipakai saat aplikasi punya lebih dari satu mekanisme autentikasi atau lebih dari satu sumber identitas.
Contoh:
- aplikasi web memakai session-based auth dengan guard
web - API memakai token-based auth dengan guard
apiatau Sanctum - panel admin punya guard sendiri
Contoh konfigurasi
Biasanya ada di config/auth.php.
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],Cara pakai
Auth::guard('web')->user();
Auth::guard('api')->check();Atau middleware:
Route::middleware('auth:web')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});Inti praktisnya
- guard menentukan identitas
- policy/gate menentukan izin
Kalau identitas salah, authorization berikutnya ikut salah juga.
Gate
Definisi
Gate adalah aturan authorization yang sederhana dan ekspresif. Ia cocok untuk pengecekan kemampuan tertentu yang tidak harus menempel ke model tertentu.
Contoh pertanyaan yang cocok untuk gate:
- apakah user boleh masuk dashboard admin?
- apakah user boleh mengelola setting global?
- apakah user boleh mengakses fitur ekspor?
Contoh definisi gate
Biasanya didefinisikan di App\Providers\AuthServiceProvider.
use Illuminate\Support\Facades\Gate;
public function boot(): void
{
Gate::define('access-admin-panel', function (User $user) {
return $user->is_admin;
});
}Cara pakai gate
if (Gate::allows('access-admin-panel')) {
// lanjut
}
abort_unless(Gate::allows('access-admin-panel'), 403);Di blade:
@can('access-admin-panel')
<a href="/admin">Admin</a>
@endcanKapan gate lebih tepat daripada policy
Pakai gate kalau:
- aturannya bersifat global atau lintas model
- yang dicek hanya kemampuan sederhana
- tidak ada resource spesifik yang jadi pusat keputusan
- kamu butuh rule cepat tanpa membuat class policy baru
Contoh yang cocok:
- masuk area admin
- export CSV
- melihat halaman billing
- toggling feature tertentu
Policy
Definisi
Policy adalah kelas yang mengelompokkan aturan otorisasi untuk satu model atau satu resource domain.
Policy lebih cocok untuk pertanyaan seperti:
- bolehkah user update post ini?
- bolehkah user hapus komentar ini?
- bolehkah user melihat order ini?
Karena terikat ke model, policy biasanya lebih jelas untuk aplikasi yang punya ownership, relasi, atau status data yang kompleks.
Contoh policy
use App\Models\Post;
use App\Models\User;
class PostPolicy
{
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;
}
}Registrasi policy
Laravel bisa mendeteksi policy lewat konvensi, atau kamu daftarkan eksplisit bila perlu.
Cara pakai policy
Di controller:
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// lanjut update
}Atau:
Gate::authorize('update', $post);Di blade:
@can('update', $post)
<button>Edit</button>
@endcanKapan policy lebih tepat daripada gate
Pakai policy kalau:
- izin bergantung pada data model tertentu
- keputusan authorization menyangkut ownership atau relasi
- resource punya banyak aksi: view, update, delete, restore, forceDelete
- kamu ingin aturan akses terkonsolidasi di satu tempat
Contoh umum:
PostPolicyCommentPolicyOrderPolicyInvoicePolicy
Gate vs Policy
Pakai gate kalau:
- yang dicek adalah kemampuan global
- logikanya singkat dan tidak bergantung ke model spesifik
- tidak perlu satu class terpisah untuk satu resource
Pakai policy kalau:
- yang dicek adalah akses ke model/resource tertentu
- ada ownership, status, atau relasi yang perlu dipertimbangkan
- kamu ingin struktur authorization yang rapi dan scalable
Aturan praktis
Kalau kamu menemukan kalimat seperti:
- “boleh melihat post ini?”
- “boleh update order ini?”
- “boleh hapus comment ini?”
itu biasanya policy.
Kalau kalimatnya seperti:
- “boleh masuk admin panel?”
- “boleh ekspor data?”
- “boleh akses fitur premium?”
itu biasanya gate.
Authorization di controller
Controller adalah tempat yang paling umum untuk menempelkan authorization check karena dia sudah berada dekat dengan route dan resource.
1. Pakai $this->authorize()
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
$post->update($request->validated());
}Kalau user tidak boleh, Laravel otomatis melempar exception authorization dan menghasilkan response 403.
2. Pakai authorizeResource()
Untuk resource controller, Laravel menyediakan binding yang rapi.
public function __construct()
{
$this->authorizeResource(Post::class, 'post');
}Lalu method seperti index, show, create, store, edit, update, destroy akan dipetakan ke policy yang sesuai.
Ini cocok kalau controller menangani resource CRUD standar.
3. Pakai Gate::authorize()
Gate::authorize('access-admin-panel');Ini enak untuk pengecekan gate yang ringkas di controller.
Authorization di middleware
Middleware cocok untuk memutus request lebih awal saat syarat aksesnya sifatnya umum.
Contoh middleware auth
Route::middleware('auth')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});Middleware auth memastikan request berasal dari user yang sudah terautentikasi.
Contoh guard-specific middleware
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});Middleware bukan pengganti policy
Middleware bagus untuk:
- cek login
- cek guard
- cek verifikasi email
- cek role umum yang sederhana
- cek akses ke route group tertentu
Tapi kalau aturannya bergantung pada model yang sedang diakses, policy biasanya lebih tepat.
Contoh yang sehat
Route::middleware(['auth'])->group(function () {
Route::get('/posts/{post}/edit', [PostController::class, 'edit']);
});Lalu di controller:
public function edit(Post $post)
{
$this->authorize('update', $post);
return view('posts.edit', compact('post'));
}Ini memisahkan:
- middleware untuk login
- policy untuk izin terhadap post
Flow yang sehat dari login sampai response
Jebakan umum dalam akses kontrol
1. Mengira autentikasi sama dengan otorisasi
Ini jebakan paling sering.
- authentication = user dikenali
- authorization = user boleh melakukan aksi
Kalau user sudah login, bukan berarti dia otomatis boleh update semua data.
2. Hanya mengandalkan middleware auth
auth cuma memastikan user sudah masuk. Itu belum cukup untuk melindungi resource.
Contoh masalah:
- semua user login bisa edit post orang lain
- semua user bisa membuka halaman admin kalau cuma dicek auth
3. Menaruh semua aturan di controller
Kalau check akses ditulis langsung di banyak controller, logikanya cepat duplikatif.
Lebih sehat:
- gate untuk rule global
- policy untuk rule per resource
- controller hanya memanggil authorization
4. Membuat gate untuk aturan per model yang seharusnya policy
Kalau gate kamu mulai menerima banyak parameter model dan kondisi ownership, biasanya itu tanda policy lebih cocok.
5. Lupa memeriksa authorization saat route model binding
Route model binding memudahkan akses ke model, tapi tidak otomatis melindungi data.
Route::get('/posts/{post}', [PostController::class, 'show']);Kalau show menampilkan data sensitif atau hanya boleh untuk owner, tetap perlu policy.
6. Salah membedakan 401 dan 403
- 401 Unauthorized: belum terautentikasi atau login tidak valid
- 403 Forbidden: user dikenali, tapi tidak punya izin
Dalam praktik, Laravel sering mengarahkan login failure ke 401/redirect, sedangkan authorization failure ke 403.
7. Meletakkan rule akses di view saja
@can di Blade bagus untuk menyembunyikan tombol, tapi itu bukan pengaman utama.
View bisa disembunyikan, tapi route dan controller tetap harus dicek.
8. Menganggap frontend sudah cukup
Frontend hanya membantu UX. Security tetap harus ditegakkan di backend.
Kalau frontend menyembunyikan tombol delete, tapi endpoint backend tidak dicek, itu bukan proteksi.
9. Policy terlalu besar dan berantakan
Policy yang isinya terlalu banyak bercampur keputusan bisnis kompleks bisa jadi sulit dibaca.
Kalau sudah terlalu gemuk, pertimbangkan:
- pecah rule ke helper/service
- pakai method policy yang jelas
- gunakan nama ability yang konsisten
10. Tidak konsisten antara ability name dan maknanya
Contoh ability seperti update atau delete sebaiknya benar-benar berarti aksi tersebut, bukan campuran logika yang kabur.
Nama ability yang bersih bikin authorize('update', $post) lebih mudah dipahami.
Contoh implementasi yang rapi
Route
Route::middleware('auth')->group(function () {
Route::get('/posts/{post}/edit', [PostController::class, 'edit']);
Route::put('/posts/{post}', [PostController::class, 'update']);
});Controller
class PostController extends Controller
{
public function edit(Post $post)
{
$this->authorize('update', $post);
return view('posts.edit', compact('post'));
}
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
$post->update($request->validate([
'title' => ['required', 'string', 'max:255'],
'body' => ['required', 'string'],
]));
return redirect()->route('posts.show', $post);
}
}Policy
class PostPolicy
{
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
}Flow ini sehat karena:
- middleware memastikan user login
- policy memastikan user punya hak atas post tersebut
- controller tetap fokus ke orchestration
Pola berpikir yang enak dipakai sehari-hari
Saat kamu bingung harus taruh check di mana, pakai urutan ini:
- Apakah user sudah login?
- pakai
authmiddleware / guard
- pakai
- Apakah aksesnya bersifat global?
- pertimbangkan gate
- Apakah aksesnya bergantung pada model/resource tertentu?
- pakai policy
- Apakah route hanya boleh diproses kalau semua syarat lolos?
- gabungkan middleware + policy di controller
Catatan implementasi
- Pakai guard untuk menentukan sumber identitas user.
- Pakai middleware
authuntuk menolak request dari user yang belum login. - Pakai gate untuk kemampuan global atau lintas resource.
- Pakai policy untuk aturan per model atau per resource.
- Jangan menaruh authorization hanya di view.
- Bedakan 401 dan 403 dengan jelas.
- Kalau resource punya relasi ownership, policy hampir selalu lebih natural.
- Kalau aksesnya cuma “boleh masuk area ini atau tidak”, gate sering cukup.
- Untuk CRUD standar,
authorizeResource()bisa mengurangi boilerplate.
Istilah
- Authentication: proses mengenali identitas user.
- Authorization: proses menentukan apakah user boleh melakukan aksi.
- Guard: mekanisme Laravel untuk membaca identitas user pada request.
- Gate: rule izin sederhana yang biasanya tidak terikat ke model tertentu.
- Policy: class izin yang dipakai untuk resource/model tertentu.
- Ability: nama aksi yang dicek, misalnya
view,create,update,delete. - 403 Forbidden: user ada, tapi tidak diizinkan.
- 401 Unauthorized: user belum terautentikasi atau kredensial tidak valid.
Referensi terkait
- Dokumentasi resmi Laravel: Authentication
- Dokumentasi resmi Laravel: Authorization
- Dokumentasi resmi Laravel: Gates
- Dokumentasi resmi Laravel: Policies
- Dokumentasi resmi Laravel: Middleware
- Dokumentasi resmi Laravel: HTTP Responses