Now, let's discuss Middleware and authorization. We will create a special users.is_admin
column in the DB and restrict non-admin users from access to manage categories.
Route Groups and Middleware
We have already seen the auth
Middleware, which allows only logged-in users to access Routes. The auth
is one of the Middlewares offered by the Laravel framework.
Middleware runs some checks before Routes, and if it returns false, it shows errors or redirects to an error page. For example, if someone wants to visit the /dashboard
URL protected with auth
Middleware, they will automatically be redirected to the login
page.
Now, look at our current Routes file.
routes/web.php:
use App\Http\Controllers\CategoryController;use App\Http\Controllers\ProfileController;use Illuminate\Support\Facades\Route; Route::get('/', function () { return view('welcome');}); Route::get('/dashboard', function () { return view('dashboard');})->middleware(['auth', 'verified'])->name('dashboard'); Route::middleware('auth')->group(function () { Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');}); Route::resource('categories', CategoryController::class); require __DIR__.'/auth.php';
I see two problems here:
- Middleware
auth
is used for both the Dashboard and the Profile group: can we somehow combine them? - The Categories resource doesn't use
auth
, so it's available to every public user without logging in.
In the routes/web.php
, we already have one Route group for the profile links. We can move both Dashboard and Categories routes to that group.
The syntax to have grouped Routes is to use what you need on a Route
facade like middleware
, prefix
, etc., and then have a group
method with a closure and add all the Routes in the closure.
routes/web.php:
Route::get('/', function () { return view('welcome');}); Route::get('/dashboard', function () { return view('dashboard');})->middleware(['auth', 'verified'])->name('dashboard'); Route::middleware('auth')->group(function () { Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); Route::get('/dashboard', function () { return view('dashboard'); })->middleware(['verified'])->name('dashboard'); Route::resource('categories', CategoryController::class); }); require __DIR__.'/auth.php'; Route::resource('categories', CategoryController::class);
If you try to access any of these routes as a guest, you should be redirected to the login page.
As you can see, we moved the Dashboard inside the auth
Middleware group. Still, we can additionally assign more Middleware checks like verified
(is user's email verified) to any Route within a group.
Here's the GitHub commit for this change.
Admin User: DB Structure
Let's add a new column to the users
table to define whether the user is an admin.
php artisan make:migration add_is_admin_to_users_table
Here's what's inside our Migration:
public function up(): void{ Schema::table('users', function (Blueprint $table) { $table->boolean('is_admin')->default(false); });}
If we run php artisan migrate
, we will have this column:
Categories: Only for Admin Users
Now, let's create our custom Middleware to check if a user is an admin. Middleware can be created using an Artisan command.
php artisan make:middleware IsAdminMiddleware
Middlewares are created in the app/Http/Middleware
folder. The default structure of the Middleware is the handle()
, which returns the next request. Before the return, you can add conditions your application needs and abort the request if needed.
In our created Middleware, if the is_admin
field in the User
table is false, we must abort with the "403 Forbidden" status. Here's the syntax.
app/Http/Middleware/IsAdminMiddleware.php:
namespace App\Http\Middleware; use Closure;use Illuminate\Http\Request;use Symfony\Component\HttpFoundation\Response; class IsAdminMiddleware{ public function handle(Request $request, Closure $next): Response { if (! auth()->check() || ! auth()->user()->is_admin) { abort(403); } return $next($request); }}
The currently authenticated user can be obtained using Laravel helpers auth()->user()
and then call the field liks is_admin
.
Also, there's auth()->check()
, which returns true
if the user is logged in.
Next, we can use the Middleware in our Routes by providing the class name directly.
routes/web.php:
use App\Http\Middleware\IsAdminMiddleware; // ... Route::middleware('auth')->group(function () { Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); // ... Route::resource('categories', CategoryController::class) ->middleware(IsAdminMiddleware::class); });
If you try to access categories Routes with a user who isn't an admin, you will see a forbidden error message:
So, this is how you use Route Groups and Middlewares in Laravel to restrict access to some routes.
Here's the GitHub commit for this lesson.
In the next lesson, we will practice to create another CRUD, managing Posts.
No comments or questions yet...