User Role based Authentication and Access Control in Laravel

This tutorial gives a step by step guide on how to setup Role based authentication in Laravel along with it’s native authentication system.

We are using Laravel 5.5 for this tutorial.

Role Based Authentication / Authorization

Let’s just take a moment to understand role based authorization and what you can achieve with this. Let’s say you are building a application which will be used by variety of customers. There are parts of your application (a method, some routes or full controller) which should only be accessible by customers that have a certain privilege.

This is where you can make use of role based authentication system. It requires a couple of extra tables to be created in your database to define all the roles that exist in your application and also to map your user to certain roles.

Let’s dive into the steps.

To showcase the role based authentication.Lets create two sections in the application one is Admin and another is Super-Admin. After user is logged in they require a specific role privilege to enter this area of application.

Setup Laravel Authentication

If you have done fresh Laravel installation and haven’t yet configured the Laravel’s out of box authentication.

Please Setup Laravel Authentication before starting with Role based Authentication system.

Create Controllers

Let’s create two new controller’s AdminController and SuperAdminController

php artisan make:controller AdminController

php artisan make:controller SuperAdminController

Add index method to both the controller

    //Index method for Admin Controller
    public function index()
    {
        return view('admin.home');
    }

    //Index method for SuperAdmin Controller
    public function index()
    {
        return view('superadmin.home');
    }

index method from AdminController returns the home page from admin folder and index method for SuperAdmin Controller returns the home page which is in superadmin view folder.

 

Create Views

Let’s  now build home page views for both admin and superadmin sections of the application.

Create new folder admin under resources > views and add new file home.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">Admin Dashboard</div>

                <div class="panel-body">
                    @if (session('status'))
                        <div class="alert alert-success">
                            {{ session('status') }}
                        </div>
                    @endif

                    This is Admin Dashboard. You must be privileged to be here !
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Next, Create new folder superadmin under resources > views and add new file home.blade.php


@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">Super Admin Dashboard</div>

                <div class="panel-body">
                    @if (session('status'))
                        <div class="alert alert-success">
                            {{ session('status') }}
                        </div>
                    @endif

                        This is Admin Dashboard. You must be super privileged to be here !
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Add route entry into routes / web.php file

Route::get('/admin', 'AdminController@index');

Route::get('/superadmin', 'SuperAdminController@index')

Create the Role model and setup migration

php artisan make:model Role -m

This will create a Model class for the roles table and will also create a migrations file under database > migrations

Edit the CreateRolesTable class under migrations folder

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateRolesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('roles', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('description');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('roles');
    }
}

Create Migration for the role_user table

We need another table , which hold’s that data of which role is assigned to which user.

php artisan make:migration create_role_user_table

Edit the CreateRoleUserTable class in the migrations folder:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateRoleUserTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('role_user', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('role_id')->unsigned();
            $table->integer('user_id')->unsigned();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('role_user');
    }
}

Next, we need to provide many-to-many relationship between User and Role

Add roles() method to your User.php class

    public function roles()
    {
        return $this
            ->belongsToMany('App\Role')
            ->withTimestamps();
    }

Add users() to your Role.php class

    public function users()
    {
        return $this
            ->belongsToMany('App\User')
            ->withTimestamps();
    }

Create tables and add data for testing

You can now run the migrate command to create the tables in database

php artisan migrate

Running the migrate command creates following tables in your database. You can choose to fill the data for testing either manually or via Seeding.

I have created two Roles with name ROLE_ADMIN and ROLE_SUPERADMIN. Users assigned the role of ROLE_ADMIN should have access to Admin Section of Application. Same applies to super admin users.

You can register new user’s by going into /register url, after you have added few user’s you can assign roles to user in role_user table.

I have assigned some sample roles to the user.

Just a few more steps, Don’t give up !

Modify User.php 

Open user.php and add these tiny methods which will be used to check if user has a particular role or roles

public function authorizeRoles($roles)
{
  if ($this->hasAnyRole($roles)) {
    return true;
  }
  abort(401, 'This action is unauthorized.');
}
public function hasAnyRole($roles)
{
  if (is_array($roles)) {
    foreach ($roles as $role) {
      if ($this->hasRole($role)) {
        return true;
      }
    }
  } else {
    if ($this->hasRole($roles)) {
      return true;
    }
  }
  return false;
}
public function hasRole($role)
{
  if ($this->roles()->where(‘name’, $role)->first()) {
    return true;
  }
  return false;
}

With the above methods, if you are looking to check just against a single role you can make use of hasRole method.

Or You can check against multiple roles by passing an array to authorizeRoles method.

Currently we are only looking to compare against a single role, We will make use of hasRole method. Let’s go ahead and create the Middleware for the same.

Create Middleware

We will create a new middleware CheckRole 

php artisan make:middleware CheckRole

Modify the CheckRole.php file under app > Middleware

<?php

namespace App\Http\Middleware;

use Closure;

class CheckRole
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next, $role)
    {
        if (! $request->user()->hasRole($role)) {
            abort(401, 'This action is unauthorized.');
        }
        return $next($request);
    }
}

We have modified the handle method middleware to check for given role.

Next step is to register the middleware we just created. Open Kernal.php which is located under App > and modify array $routeMiddleware to include the role middleware.

 

    protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'role' => \App\Http\Middleware\CheckRole::class,
    ];

 

Modify Controllers

Open AdminController.php. Below code in constructor method will check if the logged in user has role ROLE_ADMIN associated with it.

    public function __construct()
    {
        $this->middleware('auth');
        $this->middleware('role:ROLE_ADMIN');
    }

same for SuperAdminController.php

    public function __construct()
    {
        $this->middleware('auth');
        $this->middleware('role:ROLE_SUPERADMIN');
    }

That’s it !  Only privileged user can access the certain parts of your application.

 

 

Related Articles

54 comments On User Role based Authentication and Access Control in Laravel

  • Hi,

    It is still working ! Congrats, during two days, I have followed about 6 tutorials about Laravel ACL, Roles and Permissions (mostly videos + github files) without success. I thought, I could never make it works and then I found your tutorial. I said to myself, common it is 09:00 pm, let’s give a try ! It took me 45 minutes and now it is working as expected with 4 different roles and dashboards !
    Your explanation where concise but precise and enough to understand the process and the different steps to go through.

    Thanks again, you made my day.

    Cheers,

    Marc

  • tnx a lot!!! i m using laravel 6.5but still it helped man!

  • I got this error for http://localhost/myProject/public/admin/ URL

    Access forbidden!

    You don’t have permission to access the requested directory. There is either no index document or the directory is read-protected.

    If you think this is a server error, please contact the webmaster.
    Error 403

  • Wonderful,

    Where is the entire source code?

    Regards

  • Nice work thank u so much but how to group route in this way (middleware)

  • I got the following error:
    “Use of undefined constant ‘name’ – assumed ‘‘name’’ (this will throw an Error in a future version of PHP)”
    Please help me….

  • Hello
    thanks for your tutorial, i done everything ok and add some rules, the custom error page, and only admin can register new users.

    i need two more roles, for users to see especific views, and i’m trying define the role in the register form.
    any idea how to get this done?

    thanks you

  • Hello SIr, Thanks for this tutorial….Im get a error while I was going to migrate.
    Cannot declare class CreateRoleUserTable, because the name is already in use

  • Nice tutorial. Thanks a lot.

  • I’m new to Laravel and still trying to figure out how things work.

    I understand how the relationship between users and roles are defined, but then this relationship is not directly by a key in one of either tables, but through another, third table: role_users.

    But I don’t see this table mentioned anywhere in the code, nor are any foreign keys defined in the migration for it. So how does Eloquent know to look up the values for User->roles() or Role->users() in the role_users table?

  • I found my problem, it was missing the $role variable in the handle method, but found a new error:

    (1/1) ErrorException
    Use of undefined constant ‘name’ – assumed ‘‘name’’

    in User.php (line 60)

    Where was ‘

    ($this->roles()->where(‘name’, $role)->first())

    I put ‘

    ($this->roles()->where(‘name’, $role)->first())

    Now it works! Thanks!

  • I get this error:

    (1/1) ErrorException
    Undefined variable: role

    in CheckRole.php (line 18)

  • This is a good article. It is working for rest full API backend

  • Hi,

    I’m still learning Laravel atm, the role based authentication is working fine but instead of getting a white page with the text ‘This action in unauthorized’ it is throwing an error with the same response instead.

    Here is the screenshot of the error:
    https://s13.postimg.org/6eeak2taf/error.jpg

    I’m using Windows 10 and Wampserver.

  • When I try to go to localhost:8000/admin or localhost:8000/superadmin I get “Sorry, the page you are looking for could not be found.”

    Should there be anything added to the web.php routes?

    • Alex,
      Yes you need to add routes for /admin and /superadmin in your routes/web.php file. I have modified the Tutorial to include the same.
      Thanks

  • It will be more handy to add another entity and relate it with roles. That entity should be routes defined in /config/web.php to offer an UI that allows controlling of access roles via every public route in the application.

  • Nice tutorial

  • In my case, this line must be added to app/Http/Kernel.php, under protected $routeMiddleware

    ‘role’ => \App\Http\Middleware\CheckRole::class

    It will look like this:

    protected $routeMiddleware = [
    ‘auth’ => \Illuminate\Auth\Middleware\Authenticate::class,
    ‘auth.basic’ => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    ‘bindings’ => \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ‘can’ => \Illuminate\Auth\Middleware\Authorize::class,
    ‘guest’ => \App\Http\Middleware\RedirectIfAuthenticated::class,
    ‘throttle’ => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    ‘role’ => \App\Http\Middleware\CheckRole::class,
    ];

  • Sir, whenever I have to login with the users role. How can I redirect the user with its desired page. For example I’m login as an admin, I want to redirect to the admin index. Still studying laravel at this moment.

    • You can customize the post-authentication redirect location by defining a redirectTo property on the LoginController, RegisterController, and ResetPasswordController:

      protected $redirectTo = '/';

      • Put this into your LoginController.php

        protected function authenticated($request, $user)
        {
        if($user->hasRole(‘ROLE_ADMIN’)) {
        return redirect(‘/admin’);
        } elseif ($user->hasRole(‘ROLE_SUPERADMIN’)) {
        return redirect(‘/superadmin’);
        } else {
        return redirect(‘/home’);
        }
        }

        That will redirect to each of your

Leave a reply:

Your email address will not be published.

Site Footer