Laravel Auth Guards Gates Policies Authorization Flow
Selesai membaca dokumentasi ini?
Kembali ke rujukan utama Laravel untuk melanjutkan topik lainnya.
Kembali ke rujukan utama Laravel untuk melanjutkan topik lainnya.
Di Laravel, autentikasi dan otorisasi dibagi ke beberapa lapisan yang kelihatannya mirip, tapi fungsinya berbeda.
Kalau disederhanakan:
Di Laravel, auth sering terlihat mudah karena banyak hal sudah disediakan oleh framework. Tapi kalau pemisahannya kabur, masalahnya cepat muncul:
Memahami pembagian ini bikin akses kontrol lebih rapi, aman, dan gampang dirawat.
Urutannya penting:
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()authweb dan apiContoh paling umum:
if (Auth::check()) {
$user = Auth::user();
}Atau di controller:
public function index()
{
$user = auth()->user();
}Guard adalah cara Laravel menentukan user mana yang sedang dianggap aktif pada request tertentu.
Guard menjawab pertanyaan seperti:
Dengan kata lain, guard adalah lapisan identitas, bukan aturan izin.
Guard dipakai saat aplikasi punya lebih dari satu mekanisme autentikasi atau lebih dari satu sumber identitas.
Contoh:
webapi atau SanctumBiasanya ada di config/auth.php.
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],Auth::guard('web')->user();
Auth::guard('api')->check();Atau middleware:
Route::middleware('auth:web')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});Kalau identitas salah, authorization berikutnya ikut salah juga.
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:
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;
});
}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>
@endcanPakai gate kalau:
Contoh yang cocok:
Policy adalah kelas yang mengelompokkan aturan otorisasi untuk satu model atau satu resource domain.
Policy lebih cocok untuk pertanyaan seperti:
Karena terikat ke model, policy biasanya lebih jelas untuk aplikasi yang punya ownership, relasi, atau status data yang kompleks.
use App\Models\Post;
use App\Models\User;
class PostPolicy
{
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id;
Laravel bisa mendeteksi policy lewat konvensi, atau kamu daftarkan eksplisit bila perlu.
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>
@endcanPakai policy kalau:
Contoh umum:
PostPolicyCommentPolicyOrderPolicyInvoicePolicyKalau kamu menemukan kalimat seperti:
itu biasanya policy.
Kalau kalimatnya seperti:
itu biasanya gate.
Controller adalah tempat yang paling umum untuk menempelkan authorization check karena dia sudah berada dekat dengan route dan resource.
$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.
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.
Gate::authorize()Gate::authorize('access-admin-panel');Ini enak untuk pengecekan gate yang ringkas di controller.
Middleware cocok untuk memutus request lebih awal saat syarat aksesnya sifatnya umum.
Route::middleware('auth')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});Middleware auth memastikan request berasal dari user yang sudah terautentikasi.
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});Middleware bagus untuk:
Tapi kalau aturannya bergantung pada model yang sedang diakses, policy biasanya lebih tepat.
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:
Ini jebakan paling sering.
Kalau user sudah login, bukan berarti dia otomatis boleh update semua data.
authauth cuma memastikan user sudah masuk. Itu belum cukup untuk melindungi resource.
Contoh masalah:
Kalau check akses ditulis langsung di banyak controller, logikanya cepat duplikatif.
Lebih sehat:
Kalau gate kamu mulai menerima banyak parameter model dan kondisi ownership, biasanya itu tanda policy lebih cocok.
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.
Dalam praktik, Laravel sering mengarahkan login failure ke 401/redirect, sedangkan authorization failure ke 403.
@can di Blade bagus untuk menyembunyikan tombol, tapi itu bukan pengaman utama.
View bisa disembunyikan, tapi route dan controller tetap harus dicek.
Frontend hanya membantu UX. Security tetap harus ditegakkan di backend.
Kalau frontend menyembunyikan tombol delete, tapi endpoint backend tidak dicek, itu bukan proteksi.
Policy yang isinya terlalu banyak bercampur keputusan bisnis kompleks bisa jadi sulit dibaca.
Kalau sudah terlalu gemuk, pertimbangkan:
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.
Route::middleware('auth')->group(function () {
Route::get('/posts/{post}/edit', [PostController::class, 'edit']);
Route::put('/posts/{post}', [PostController::class, 'update'
class PostController extends Controller
{
public function edit(Post $post)
{
$this->authorize('update', $post);
return view('posts.edit', compact
class PostPolicy
{
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
}Flow ini sehat karena:
Saat kamu bingung harus taruh check di mana, pakai urutan ini:
auth middleware / guardauth untuk menolak request dari user yang belum login.authorizeResource() bisa mengurangi boilerplate.view, create, update, delete.