Route selama ini kita lindungi satu per satu, tetapi di aplikasi nyata ratusan route butuh perlindungan yang sama. Laravel menyediakan dua middleware simetris untuk itu: auth menahan tamu di luar area login, dan guest membalik arah — pengguna yang sudah login tidak perlu lagi melihat halaman login. Keduanya bukan sekadar gerbang: perilaku redirect-nya — ke mana tamu diarahkan, bagaimana ia kembali setelah login, dan bagaimana API ditolak — menentukan pengalaman pengguna. Chapter ini membedah semuanya sampai ke guard khusus.
Melindungi Route dengan Middleware auth #
Cara paling ringkas memasang pengawal adalah mengelompokkan route yang butuh perlindungan:
use App\Http\Controllers\AuthController;
Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/dashboard', [AuthController::class, 'dashboard'])->name('dashboard');
});
Route::middleware('auth')->group(function () {
Route::get('/ringkasan', [AuthController::class, 'ringkasan'])->name('ringkasan');
});Middleware auth memanggil Auth::check(); bila gagal, ia melempar AuthenticationException. Untuk request web biasa, Laravel 13 sudah mengatur bawaannya di ApplicationBuilder: tamu di-redirect ke route bernama login — karena itulah aplikasi kita wajib punya route dengan nama itu. Perilaku ini bisa kita sesuaikan lewat bootstrap/app.php:
->withMiddleware(function (Middleware $middleware): void {
$middleware->redirectGuestsTo(fn () => route('login'));
$middleware->redirectUsersTo(fn () => route('dashboard'));
})Intended URL: Kembali ke Halaman yang Dituju #
Redirect saja tidak cukup — pengguna yang diusir dari /dashboard tentu ingin kembali ke sana setelah login, bukan ke beranda. Mekanismenya bekerja dalam dua langkah. Saat middleware mengarahkan tamu ke login, method guest() pada redirector menyimpan URL semula ke session dengan kunci url.intended. Kemudian controller login memanggil redirect()->intended() yang membaca kunci itu kembali:
if (Auth::attempt($credentials, $remember)) {
$request->session()->regenerate();
return redirect()->intended('/dashboard');
}Argumen '/dashboard' hanya cadangan bila intended URL kosong — misalnya pengguna membuka halaman login secara langsung. Feature test berikut membuktikan rantai lengkapnya:
public function test_tamu_diarahkan_ke_login_dengan_intended_url(): void
{
$response = $this->get('/dashboard');
$response->assertRedirect(route('login'));
$this->assertSame(
'http://localhost:8000/dashboard',
$this->app['session']->get('url.intended')
);
}
public function test_setelah_login_kembali_ke_halaman_yang_diminta(): void
{
User::factory()->create([
'email' => '[email protected]',
'password' => 'rahasia-budi-123',
]);
$this->get('/ringkasan'); // ditolak, intended tersimpan
$response = $this->post('/login', [
'email' => '[email protected]',
'password' => 'rahasia-budi-123',
]);
$response->assertRedirect('http://localhost:8000/ringkasan');
}Output yang Dihasilkan:
tamu_ke_dashboard: status=302, target=http://localhost:8000/login, intended=http://localhost:8000/dashboard
login_menuju_intended: status=302, target=http://localhost:8000/ringkasanTamu API: 401, Bukan Redirect #
Redirect ke halaman login hanya masuk akal untuk browser. Client API atau JavaScript yang memanggil route terlindungi harus menerima status 401 bergaya JSON — mengikuti Accept: application/json pada request:
public function test_permintaan_json_mendapat_401(): void
{
$response = $this->getJson('/dashboard');
$response->assertUnauthorized();
}Output yang Dihasilkan:
json_tanpa_login: status=401, isi={"message":"Unauthenticated."}Middleware yang sama, response yang berbeda — Laravel membaca header Accept dan memilih bentuk penolakan yang sesuai.
Middleware guest Menolak yang Sudah Login #
Kebalikannya juga penting: pengguna yang sedang login tidak boleh mengisi formulir login lagi. Middleware guest melempar mereka keluar dari halaman-halaman tamu:
Route::get('/login', [AuthController::class, 'formLogin'])->name('login')->middleware('guest');
Route::post('/login', [AuthController::class, 'login'])->middleware('guest');Ke mana mereka diarahkan? Secara bawaan, RedirectIfAuthenticated mencari route bernama dashboard atau home, lalu URI GET /dashboard atau /home, dan jatuh ke / bila tidak ada satupun. Konfigurasi $middleware->redirectUsersTo() pada bootstrap/app.php menggantikan seluruh tebak-tebakan itu. Pembuktiannya singkat:
$user = User::factory()->create(['email' => '[email protected]']);
$response = $this->actingAs($user)->get('/login');
$response->assertRedirect(route('dashboard'));Output yang Dihasilkan:
user_login_buka_login: status=302, target=http://localhost:8000/dashboardGuard Lain dengan auth:admin #
auth membaca guard default web. Aplikasi dengan dua jenis pengguna — member dan admin, misalnya — bisa memakai guard terpisah dengan sintaks auth:nama_guard. Kita daftarkan dulu guard di config/auth.php:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'users',
],
],Lalu pasang di route dan uji bahwa login di guard web tidak diakui:
Route::get('/admin/laporan', [AuthController::class, 'adminLaporan'])
->name('admin.laporan')->middleware('auth:admin');Output yang Dihasilkan:
guard: web_ke_admin=302, admin_ke_admin=200, isi="Laporan admin untuk [email protected]."Test tersebut login lewat actingAs($user) — guard web — dan ditolak di route admin (302), lalu login ulang lewat actingAs($user, 'admin') dan diterima (200).
Semua perilaku yang kita bahas di section ini layak diuji otomatis — dan pengujian authentication punya pola serta jebakannya sendiri. Chapter penutup section ini membedah pola-pola tersebut. Rujukan resminya ada di dokumentasi Authentication .