Kita sudah berkali-kali memasang auth pada route, tetapi middleware itu selama ini adalah kotak hitam. Pada chapter ini kita membukanya dari dua arah: membangun versi sederhananya sendiri agar logikanya tidak lagi misterius, lalu membaca source aslinya di vendor/laravel/framework untuk melihat mengapa tim Laravel merancangnya berbeda. Hasil akhirnya, perilaku yang selama ini kita andalkan — redirect tamu ke login, JSON 401 untuk API — akan punya penjelasan baris demi baris.
Versi Sederhana Buatan Kita #
Middleware EnsureUserIsAuthenticated kita memeriksa status login lewat Auth::check(), lalu memilih bentuk penolakan berdasarkan jenis request:
// app/Http/Middleware/EnsureUserIsAuthenticated.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserIsAuthenticated
{
public function handle(Request $request, Closure $next): Response
{
if (! Auth::check()) {
if ($request->expectsJson()) {
return response()->json(['message' => 'Unauthenticated.'], 401);
}
return redirect()->guest(route('login'));
}
return $next($request);
}
}Alur keputusannya tiga cabang. Pertama, Auth::check() bernilai false untuk tamu, sehingga penolakan dijalankan. Kedua, $request->expectsJson() mendeteksi client API — pengirim header Accept: application/json — dan menerima JSON 401, karena mengirim halaman HTML login ke client API tidak berguna. Ketiga, browser biasa diarahkan ke route login; method redirect()->guest() dipilih agar URL semula tersimpan di session sebagai url.intended, siap dipulihkan setelah login.
Kita memilih Auth::check() alih-alih memanggil Auth::user() karena pemeriksaan ini hanya butuh jawaban ya atau tidak, tanpa mengambil data pengguna. Facade Auth juga bisa diganti dengan dependency injection contract Illuminate\Contracts\Auth\Factory melalui constructor bila tim lebih menyukai gaya itu — persis seperti yang dilakukan class asli yang akan kita lihat sebentar lagi. Pemilihan status 401 mengikuti semantik HTTP: permintaan sah tetapi butuh kredensial, berbeda dari 403 yang berarti kredensial ada namun tidak berhak.
Menguji Ketiga Perilaku #
Tiga feature test berikut menguji masing-masing cabang — tamu web, tamu API, dan pengguna yang sudah login:
public function test_tamu_web_diredirect_ke_login(): void
{
$response = $this->get('/dasbor');
$response->assertRedirect(route('login'));
echo 'tamu_web: status=', $response->getStatusCode(),
', target=', $response->headers->get('Location'),
', intended=', $this->app['session']->get('url.intended'), PHP_EOL;
}
public function test_tamu_json_menerima_401(): void
{
$response = $this->getJson('/dasbor');
$response->assertUnauthorized();
echo 'tamu_json: status=', $response->getStatusCode(),
', isi=', $response->getContent(), PHP_EOL;
}Output yang Dihasilkan:
tamu_web: status=302, target=http://localhost:8000/login, intended=http://localhost:8000/dasbor
tamu_json: status=401, isi={"message":"Unauthenticated."}
user_login: status=200, isi="Dasbor pengguna"Output pertama menunjukkan redirect 302 sekaligus intended URL tersimpan; output kedua menunjukkan JSON 401; output ketiga membuktikan pengguna login melewati gerbang.
Source Asli: Authenticate di Vendor #
Sekarang bandingkan dengan class asli Illuminate\Auth\Middleware\Authenticate:
// vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php
public function handle($request, Closure $next, ...$guards)
{
$this->authenticate($request, $guards);
return $next($request);
}
protected function authenticate($request, array $guards)
{
if (empty($guards)) {
$guards = [null];
}
foreach ($guards as $guard) {
if ($this->auth->guard($guard)->check()) {
return $this->auth->shouldUse($guard);
}
}
$this->unauthenticated($request, $guards);
}
protected function unauthenticated($request, array $guards)
{
throw new AuthenticationException(
'Unauthenticated.',
$guards,
$request->expectsJson() ? null : $this->redirectTo($request),
);
}Perbedaan desainnya terlihat jelas. Middleware asli tidak membangun response penolakan sendiri, melainkan melempar AuthenticationException — keputusan bentuk jawaban (redirect, JSON, halaman error) diserahkan ke exception handler terpusat. Middleware asli juga mendukung banyak guard lewat parameter variadik ...$guards, inilah asal sintaks auth:admin yang pernah kita pakai. Method redirectTo($request) hanya mengembalikan path tujuan; pada skeleton bawaan method tersebut bahkan tidak mengembalikan apa-apa (null), dan pengalihan tamu diatur lewat $middleware->redirectGuestsTo() di bootstrap/app.php.
Middleware guest: Kebalikannya #
Kebalikan auth adalah guest, yang diimplementasikan RedirectIfAuthenticated. Versi aslinya jauh lebih sederhana karena hanya bekerja untuk browser:
// vendor/laravel/framework/src/Illuminate/Auth/Middleware/RedirectIfAuthenticated.php
public function handle(Request $request, Closure $next, string ...$guards): Response
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect($this->redirectTo($request));
}
}
return $next($request);
}Bila pengguna sudah login namun membuka halaman login, middleware langsung mengembalikan redirect — controller login tidak pernah berjalan. Method redirectTo di class itu mencari route bernama dashboard atau home sebagai tujuan bawaan.
| Aspek | Versi kita | Authenticate asli |
|---|---|---|
| Menolak tamu | Return response langsung | Throw AuthenticationException |
| Bentuk jawaban | Diputuskan di middleware | Diputuskan exception handler |
| Dukungan guard | Tunggal, default | Variadik auth:web,api |
| Kustomisasi | Edit class | redirectGuestsTo() tanpa edit vendor |
Pendekatan exception punya dua keuntungan yang tidak langsung terlihat. Seluruh titik gagal login di aplikasi — middleware, controller, bahkan package — bisa melempar AuthenticationException yang sama dan hasil bentuknya tetap konsisten. Exception juga otomatis tercatat di log serta bisa dipantau alat pemantauan error, sementara response 401 buatan middleware hanya dilihat pengirim request. Karena itu versi sederhana kita cocok untuk belajar dan kebutuhan khusus, tetapi untuk produksi tetap gunakan middleware auth bawaan yang sudah teruji di jutaan aplikasi.
Memahami dalam-dalam middleware autentikasi menuntaskan sisi penjaga gerbang. Selanjutnya kita bergeser ke sisi pengolahan: mengubah isi request sebelum controller, memotong response lebih awal, dan pekerjaan setelah response terkirim. Rujukan resmi ada di dokumentasi authentication .