In most modern web applications, user roles are essential. Whether it’s an admin managing the platform or a basic user accessing restricted resources — Role-Based Access Control (RBAC) is the go-to strategy.
Laravel, being the opinionated framework it is, gives us two powerful tools to implement authorization logic: Gates and Policies. In this post, we’ll walk through setting up RBAC using both.
What is RBAC?
RBAC is a security paradigm that restricts system access based on roles assigned to users. Each role has specific permissions, and those permissions determine what the user can or cannot do.
For example:
- Admin → Can manage users, edit posts, delete comments
- Editor → Can edit posts but not delete them
- User → Can only read content
Laravel doesn’t enforce RBAC by default — but it gives us the tools to build it ourselves using Gates and Policies.
Step 1: Setting Up Roles in the Laravel
Let’s assume you already have a Laravel app with a users table.
Add a role
column to your users
table
php artisan make:migration add_role_to_users_table --table=users
Then edit the migration file:
// database/migrations/xxxx_xx_xx_add_role_to_users_table.php
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('role')->default('user'); // values: user, editor, admin
});
}
Run the migration:
php artisan migrate
Step 2: Using Gates for Simple RBAC
Gates are perfect for quick, role-based checks.
Define Gates in AuthServiceProvider
// app/Providers/AuthServiceProvider.php
use Illuminate\Support\Facades\Gate;
public function boot()
{
$this->registerPolicies();
Gate::define('access-admin', function ($user) {
return $user->role === 'admin';
});
Gate::define('edit-posts', function ($user) {
return in_array($user->role, ['admin', 'editor']);
});
}
Step 3: Use Gates in Controller or Blade
// In Controller
if (Gate::denies('access-admin')) {
abort(403);
}
{{-- In Blade --}}
@can('edit-posts')
<a href="/posts/edit">Edit Post</a>
@endcan
That’s it, you’ve now added basic RBAC using Gates.
When to Use Policies?
Policies are like mini-classes tied to a specific model, perfect for fine-grained access control.
Let’s walk through using Policies for managing posts.
Step-by-Step: Policies for Post Permissions
Step 1: Create a Policy
php artisan make:policy PostPolicy --model=Post
This will generate a policy with methods like view
, update
, delete
, etc.
Step 2: Define Role-Based Rules
// app/Policies/PostPolicy.php
public function update(User $user, Post $post)
{
return $user->id === $post->user_id || $user->role === 'admin';
}
public function delete(User $user, Post $post)
{
return $user->role === 'admin';
}
Step 3: Register the Policy
Laravel does this automatically when using --model=Post
, but double-check:
// app/Providers/AuthServiceProvider.php
protected $policies = [
Post::class => PostPolicy::class,
];
Step 4: Use in Controller
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// Update logic here...
}
Or in Blade:
@can('delete', $post)
<form method="POST" action="{{ route('posts.destroy', $post) }}">
@csrf
@method('DELETE')
<button>Delete</button>
</form>
@endcan
Combining Gates + Policies + Middleware
You can combine everything:
// app/Http/Middleware/AdminMiddleware.php
public function handle($request, Closure $next)
{
if (auth()->user()?->role !== 'admin') {
abort(403, 'Unauthorized');
}
return $next($request);
}
Then use it on your routes:
Route::middleware(['auth', 'admin'])->group(function () {
Route::resource('users', UserController::class);
});
Laravel doesn’t dictate how to implement RBAC, it just empowers you with the tools. Whether you use Gates for quick checks, Policies for model-specific rules, or a dedicated roles/permissions system (like Spatie’s Laravel-Permission package), you can build a secure, maintainable access system in your Laravel apps.