Appearance
Protecting Routes with Middleware
In any web application, you often need to perform checks or run specific code before a user's request reaches its final destination (the controller logic). For example, is the user logged in? Do they have permission to see this page? Is their account active? This is precisely where middleware comes in.
Think of middleware as a series of gatekeepers or security checkpoints that a request must pass through on its way into your application. Each middleware can inspect the request, and based on its rules, it can either let the request pass through to the next gatekeeper, or it can turn the request away, often by redirecting the user or showing an error page. This concept is often called a "pipeline," where the request travels through layers of middleware before hitting the application logic.
Middleware is essential for building secure and robust Laravel applications. In this chapter, we'll explore how to use Laravel's built-in middleware and how to create our own custom middleware to protect our admin routes.
Using Built-in Middleware for Authentication
The most common use case for middleware is authentication—ensuring a user is logged in before they can access certain pages. Laravel makes this incredibly easy with its bundled auth
middleware.
Since this middleware is already registered in your application's HTTP Kernel (the central hub for request handling), you don't need to write any code for it. You just need to tell your routes to use it. We'll protect our entire /admin
section so that only logged-in users can even attempt to access it.
- Open your
routes/web.php
file. - Find the route group for the
admin
prefix. - Chain the
middleware()
method to the group definition and pass it an array containing'auth'
.
php
Route::middleware(['auth'])->prefix('admin')->group(function () {
Route::redirect('/', '/admin/records');
Route::get('records', Demo::class)->name('admin.records');
// ... other admin routes
});
1
2
3
4
5
2
3
4
5
With this single line, the auth
middleware is now guarding all routes within this group. Let's see what happens:
- If you are a guest (not logged in) and you try to visit
http://vinyl_shop.test/admin/records
, Laravel'sauth
middleware will intercept the request and automatically redirect you to the login page. - If you are an authenticated user (logged in), the middleware sees you are logged in, and lets you pass through to the requested admin page.
This is a great first step, but there's a problem: any logged-in user can currently access the admin routes, regardless of whether they are an administrator or a regular user. We need to get more specific.
Creating Custom Middleware for Authorization
Authentication answers the question, "Who are you?". Authorization answers the question, "What are you allowed to do?". We need a custom middleware to authorize only users with admin privileges.
Create the Admin Middleware
We'll create a new middleware class using an Artisan command.
- Open your terminal in the project directory.
- Execute the following command:
bash
php artisan make:middleware Admin
1
This command creates a new file at app/Http/Middleware/Admin.php
. Let's open it and add our authorization logic.
The core of any middleware is the handle()
method. It receives the incoming $request
and a Closure
called $next
. The $next($request)
part is the "pass" signal; it tells Laravel to send the request to the next middleware in the pipeline, and eventually, to the controller. If we don't call $next($request)
, the request is stopped dead in its tracks.
We'll add a simple check: does the currently logged-in user have their admin
flag set to true
?
- Open
app/Http/Middleware/Admin.php
. - Update the
handle()
method with the following logic.
php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class Admin
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next): Response
{
// Check if the authenticated user's 'admin' property is true.
if (auth()->user() && auth()->user()->admin) {
// If they are an admin, allow the request to proceed.
return $next($request);
}
// If not an admin, stop the request and show a 403 Forbidden error page.
return abort(403, 'Only administrators can access this page');
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Here’s a breakdown of the logic:
auth()->user()
retrieves the currently authenticatedUser
model. We check it exists first to be safe.->admin
accesses theadmin
property (database column) of that user.- If the condition is
true
, we callreturn $next($request);
to let them pass. - If the condition is
false
, we call Laravel'sabort()
helper. This immediately stops the request and generates an HTTP error response. We use403
, which is the standard code for "Forbidden," meaning "I know who you are, but you are not allowed to be here." The second argument provides a custom message for the error page.
NAMING CONVENTIONS
The name of a middleware class should be in PascalCase (e.g., Admin
, ActiveUser
) because it is a PHP class.
Create the Active User Middleware
Let's reinforce the concept by creating another middleware. What if we want to ban a user completely? We have an active
field in our users
table for this purpose. If a user's account is set to inactive, they shouldn't be able to access any authenticated page. We'll create a middleware that checks this and logs them out if they are inactive.
- Execute the following command in your terminal:
bash
php artisan make:middleware ActiveUser
1
- Open the new file at
app/Http/Middleware/ActiveUser.php
. - Update the
handle()
method:
php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class ActiveUser
{
public function handle(Request $request, Closure $next): Response
{
// Check if the authenticated user's 'active' property is true.
if (auth()->user() && auth()->user()->active) {
// If they are active, let the request proceed.
return $next($request);
}
// If the user is not active, first log them out.
auth('web')->logout();
// Then, abort the request with a 403 error and a helpful message.
return abort(403, 'Your account is not active. Please contact the administrator.');
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
This middleware is slightly different. If a user is inactive, we don't just block them; we actively log them out using auth('web')->logout()
. This ensures their session is terminated, and they can't browse other parts of the site until their account is reactivated.
Apply the Custom Middleware to Routes
Now that we have our custom gatekeepers, we need to assign them to their posts in routes/web.php
. The order of middleware is crucial. You must check for authentication (auth
) before you check for his status (ActiveUser
) and his authorization (Admin
). Otherwise, auth()->user()
would be null
for a guest, causing an error.
- Open
routes/web.php
. - Import the new middleware classes at the top of the file.
- Add
ActiveUser::class
andAdmin::class
to the admin route group's middleware array, afterauth
. - Add
ActiveUser::class
to the dashboard route ant to the general authenticated user routes group provided by the Livewire Starter Kit.
php
use App\Http\Middleware\Admin;
use App\Http\Middleware\ActiveUser;
// ... other imports
Route::middleware(['auth', ActiveUser::class, Admin::class])->prefix('admin')->group(function () {
Route::redirect('/', '/admin/records');
Route::get('records', Records::class)->name('admin.records');
Route::get('genres', Genres::class)->name('admin.genres');
// ...
});
Route::view('dashboard', 'dashboard')
->middleware(['auth', 'verified', ActiveUser::class])
->name('dashboard');
Route::middleware(['auth', ActiveUser::class])->group(function () {
Route::redirect('settings', 'settings/profile');
Route::get('settings/profile', Profile::class)->name('settings.profile');
Route::get('settings/password', Password::class)->name('settings.password');
Route::get('settings/appearance', Appearance::class)->name('settings.appearance');
});
require __DIR__ . '/auth.php';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Our routes are now fully protected with a pipeline of checks:
- Is the user logged in? (
auth
) - Is their account active? (
ActiveUser
) - Are they an administrator? (
Admin
)
A request to an admin route must pass all three checks to succeed.
Let's test our Admin
middleware.
- Log in as the non-admin user
jane.doe@example.com
(password:user1234
). - Try to visit https://vinyl_shop.test/admin/records.
- You'll be stopped by our middleware and see the 403 error page with our custom message.