Multi-Authentication in Laravel

tudip-logo

Tudip

07 August 2019

For any application, we have different numbers of users that have roles and permissions which differentiate the users, we need to have multi-auth functionality in our application

Laravel provides the ability to authenticate users with different user roles, permissions, multi-authentication, social login, and more.

Prerequisites:

  • PHP 7.1
  • MySQL
  • Laravel 5.5

Run following commands on the terminal,

laravel new multi-auth

And go to your project by running below commands
cd multi-auth

Create the database

And configure the .env file by adding the credentials, for example:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_multi_auth
DB_USERNAME=root
DB_PASSWORD=root

Migration 

We are going to create migrations for both roles ‘admins and writers’ tables. Default Laravel comes with a users migration.

Now we will create a migration for admins, open a terminal and execute below commands

php artisan make:migration create_admins_table

As soon as you run the above command, add below lines into the migration file

public function up()
    {
            Schema::create('admins', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->boolean('is_super')->default(false);
            $table->rememberToken();
            $table->timestamps();
        });
    }

By adding the above lines it will create a simple migration file, with defined columns that we to add in the admin table.

Create migration for writers

php artisan make:migration create_writers_table

Similarly, we add below line in the above migration file

public function up()
    {
        Schema::create('writers', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->boolean('is_editor')->default(false);
            $table->rememberToken();
            $table->timestamps();
        });
    }

Now we will migrate the database by running the command:

php artisan migrate

Now we will set up the models for admin, run the below command:

php artisan make:model Admin

Now, open the Admin model which is inside the “app” and add the following code:

   // app/Admin.php
    <?php
    namespace App;
    use Illuminate\Notifications\Notifiable;
    use Illuminate\Foundation\Auth\User as Authenticatable;
    class Admin extends Authenticatable
    {
        use Notifiable;
        protected $guard = 'admin';
        protected $fillable = ['name', 'email', 'password'];

        protected $hidden = [
            'password', 'remember_token',
        ];
    }

When you are planning to use model for the authentication and not willing to use default user guard (will explain what are guard later in the document), we will use the admin guard and it is very important to specify the guard name

Will create writers model as well

To create model for the writers, run the below command:

php artisan make:model Writer

Now we will add the below code in “Writer” model

// app/Writer.php
    <?php
    namespace App;
    use Illuminate\Notifications\Notifiable;
    use Illuminate\Foundation\Auth\User as Authenticatable;
    class Writer extends Authenticatable
    { 
 use Notifiable;        

protected $guard = 'writer';
      protected $fillable = ['name', 'email', 'password'];
        protected $hidden = [
            'password', 'remember_token',
        ];
    }

It is very important to specify the guard which you want to use, in our case, we will sue the admin guard.

Writers model

run the command to make the model for the writer’s role

php artisan make:model Writer

open the Writer model and add below code

<?php
    namespace App;
    use Illuminate\Notifications\Notifiable;
    use Illuminate\Foundation\Auth\User as Authenticatable;

    class Writer extends Authenticatable
    {
        use Notifiable;
        protected $guard = 'writer';
        protected $fillable = [
            'name', 'email', 'password',
        ];
        protected $hidden = [
            'password', 'remember_token',
        ];
    }

Now, the most important thing “Guards”

Guards are basically to authenticate the user and we can also create our own as per the role and guards allow to use Laravel’s default authentication system with our Admin and Writer

Now, we will add our new guards, open the auth.php inside the config file directory

<?php
    'guards' => [
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],
        'writer' => [
            'driver' => 'session',
            'provider' => 'writers',
        ],
    ],

We have added 2 new guards “admin” and “writer” and we will set the providers

When we tried to use the guard, providers are basically to provide the information about what to use for authentication or validation. Now, we will add the following to the provider’s array,

'providers' => [
        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Admin::class,
        ],
        'writers' => [
            'driver' => 'eloquent',
            'model' => App\Writer::class,
        ],
    ],

Now, we have set up the providers along with the guards

Controller Setup:

For authentication, we are using guards, we can create the new one or we can change the existing one, so let’s modify the existing controller

Modifying the LoginController:

Inside “app/Http/Controllers/Auth” open login controller

<?php
    namespace App\Http\Controllers\Auth;
    use App\Http\Controllers\Controller;
    use Illuminate\Foundation\Auth\AuthenticatesUsers;
    use Illuminate\Http\Request;
    use Auth;
    class LoginController extends Controller
    {
        public function __construct()
        {
            $this->middleware('guest')->except('logout');
            $this->middleware('guest:admin')->except('logout');
            $this->middleware('guest:writer')->except('logout');
        }
    }

Just update the constructor in the LoginController

Set the middleware accordingly to restrict access for this controller or its methods.

Define all the types of guests in the controller as it is very important because, if 1 user is logged in and we tried with another user type, then it will redirect you to its authentication page.

Open Login controller inside “app/Http/Controllers/Auth” directory and add below code

public function showAdminLoginForm()
    {
        return view('auth.login', ['url' => 'admin']);
    }

public function adminLogin(Request $request)
    {
        $this->validate($request, [
            'email' => 'required|email',
            'password' => 'required|min:6'
        ]);
        if (Auth::guard('admin')->attempt(['email' => $request->email, 'password' => $request->password], $request->get('remember'))) {

            return redirect()->intended('/admin');
        }
        return back()->withInput($request->only('email', 'remember'));
    }

Above checks are very important, to avoid confusion,

After login we will redirect user to a specific URL and if the user is unauthenticated then it will back to the login page. Now, we will add the same thing but for the writers,

Open “app/Http/Controllers/Auth/LoginController.php”

public function showWriterLoginForm()
{
        return view('auth.login', ['url' => 'writer']);
}

public function writerLogin(Request $request)
{
        $this->validate($request, [
            'email' => 'required|email',
            'password' => 'required|min:6'
]);
 if (Auth::guard('writer')->attempt(['email' => $request->email, 'password' => $request->password], $request->get('remember'))) {
            return redirect()->intended('/writer');
 }
   return back()->withInput($request->only('email', 'remember'));
 }

Now, we have set the login :)

We will now Modify RegisterController:

Open “RegisterController.php” inside “app/Http/Controllers/Auth” directory

<?php
    namespace App\Http\Controllers\Auth;
    use App\User;
    use App\Admin;
    use App\Writer;
    use App\Http\Controllers\Controller;
    use Illuminate\Support\Facades\Hash;
    use Illuminate\Support\Facades\Validator;
    use Illuminate\Foundation\Auth\RegistersUsers;
    use Illuminate\Http\Request;


    class RegisterController extends Controller
    {
        public function __construct()
        {
            $this->middleware('guest');
            $this->middleware('guest:admin');
            $this->middleware('guest:writer');
        }
    }

We have set up the middleware similar we did with Login controller.

Open “RegisterController.php” inside “app/Http/Controllers/Auth/”, add below code

    public function showAdminRegisterForm()
    {
        return view('auth.register', ['url' => 'admin']);
    }

    public function showWriterRegisterForm()
    {
        return view('auth.register', ['url' => 'writer']);
    }

What we did for showing different login pages, this is also similar to that and now, we will define our own methods for creating an admin

Open  app/Http/Controllers/Auth/RegisterController.php and add below code,

protected function createAdmin(Request $request)
    {
        $this->validator($request->all())->validate();
        $admin = Admin::create([
            'name' => $request['name'],
            'email' => $request['email'],
            'password' => Hash::make($request['password']),
        ]);
        return redirect()->intended('login/admin');
    }


Next thing is, define methods for creating a writer, inside the “RegisterController

// app/Http/Controllers/Auth/RegisterController.php

    protected function createWriter(Request $request)
    {
        $this->validator($request->all())->validate();
        $writer = Writer::create([
            'name' => $request['name'],
            'email' => $request['email'],
            'password' => Hash::make($request['password']),
        ]);
        return redirect()->intended('login/writer');
    }

Hurray! registration is now completed!

Now, we will set up the authentication pages, run below command

php artisan make:auth

It will generate view files in resources/views/auth along with routes to handle basic authentication

Now, open the login.blade.php file and edit as follows:

<div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header"> {{ isset($url) ? ucwords($url) : ""}} {{ __('Login') }}</div>
                    <div class="card-body">
                        @isset($url)
                        <form method="POST" action='{{ url("login/$url") }}' aria-label="{{ __('Login') }}">
                        @else
                        <form method="POST" action="{{ route('login') }}" aria-label="{{ __('Login') }}">
                        @endisset
                            @csrf
    </div>

Open the register.blade.php file and edit as follows:

<div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header"> {{ isset($url) ? ucwords($url) : ""}} {{ __('Register') }}</div>
                    <div class="card-body">
                        @isset($url)
                        <form method="POST" action='{{ url("register/$url") }}' aria-label="{{ __('Register') }}">
                        @else


<form method="POST" action="{{ route('register') }}" aria-label="{{ __('Register') }}">
@endisset
@csrf
</div>

Now, we have replicated what we did for login page here,

Create the pages that authenticated users will access:

touch resources/views/layouts/auth.blade.php
touch resources/views/admin.blade.php
touch resources/views/writer.blade.php
touch resources/views/home.blade.php

Insert below code block into the auth.blade.php file:

   // resources/views/layouts/auth.blade.php

    <!DOCTYPE html>
    <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <!-- CSRF Token -->
        <meta name="csrf-token" content="{{ csrf_token() }}">

        <title>{{ config('app.name', 'Laravel') }}</title>

        <!-- Scripts -->
        <script src="{{ asset('js/app.js') }}" defer></script>

        <!-- Fonts -->
        <link rel="dns-prefetch" href="https://fonts.gstatic.com">
        <link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css">

        <!-- Styles -->
        <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    </head>
    <body>
        <div id="app">
            <nav class="navbar navbar-expand-md navbar-light navbar-laravel">
                <div class="container">
                    <a class="navbar-brand" href="{{ url('/') }}">
                        {{ config('app.name', 'Laravel') }}
                    </a>
                    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                        <span class="navbar-toggler-icon"></span>
                    </button>

                    <div class="collapse navbar-collapse" id="navbarSupportedContent">
                        <!-- Left Side Of Navbar -->
                        <ul class="navbar-nav mr-auto">

                        </ul>

                        <!-- Right Side Of Navbar -->
                        <ul class="navbar-nav ml-auto">
                            <!-- Authentication Links -->
                           <li class="nav-item dropdown">
                                <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                    Hi There <span class="caret"></span>
                                </a>

                                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                                    <a class="dropdown-item" href="{{ route('logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

                                    <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        </ul>
                    </div>
                </div>
            </nav>

            <main class="py-4">
                @yield('content')
            </main>
        </div>
    </body>
    </html>

Next, insert this code block into the admin.blade.php file:

   // resources/views/admin.blade.php

    @extends('layouts.auth')
    @section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">Dashboard</div>

                    <div class="card-body">
                        Hi boss!
                    </div>
                </div>
            </div>
        </div>
    </div>
    @endsection

 

Open the writer.blade.php file and edit as follows:

   // resources/views/writer.blade.php

    @extends('layouts.auth')
    @section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">Dashboard</div>

                    <div class="card-body">
                        Hi there, awesome writer
                    </div>
                </div>
            </div>
        </div>
    </div>
    @endsection

Finally, open the home.blade.php file and replace with the following:

   // resources/views/home.blade.php

@extends('layouts.auth')
    @section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">Dashboard</div>

                    <div class="card-body">
                         Hi there, regular user
                    </div>
                </div>
            </div>
        </div>
    </div>
    @endsection

Now, we will set up the routes

Let us define the routing, we are almost ready to run the application.

Open the routes/web.php file and replace with the following:

<?php
    Route::view('/', 'welcome');
    Auth::routes();

    Route::get('/login/admin', 'Auth\LoginController@showAdminLoginForm');
    Route::get('/login/writer', 'Auth\LoginController@showWriterLoginForm');
    Route::get('/register/admin', 'Auth\RegisterController@showAdminRegisterForm');
    Route::get('/register/writer', 'Auth\RegisterController@showWriterRegisterForm');

    Route::post('/login/admin', 'Auth\LoginController@adminLogin');
    Route::post('/login/writer', 'Auth\LoginController@writerLogin');
    Route::post('/register/admin', 'Auth\RegisterController@createAdmin');
    Route::post('/register/writer', 'Auth\RegisterController@createWriter');

    Route::view('/home', 'home')->middleware('auth');
    Route::view('/admin', 'admin');
    Route::view('/writer', 'writer');

Now, we modify how users are redirected if they are authenticated
Laravel by default redirects all authenticated users to /home page. If we do not modify the redirection, we will get the below error,

Open the app/Http/Controllers/Middleware/RedirectIfAuthenticated.php

<?php
    namespace App\Http\Middleware;
    use Closure;
    use Illuminate\Support\Facades\Auth;
    class RedirectIfAuthenticated
    {
        public function handle($request, Closure $next, $guard = null)
        {
            if ($guard == "admin" && Auth::guard($guard)->check()) {
                return redirect('/admin');
            }
            if ($guard == "writer" && Auth::guard($guard)->check()) {
                return redirect('/writer');
            }
            if (Auth::guard($guard)->check()) {
                return redirect('/home');
            }
            return $next($request);
        }
    }

The above middleware receives the auth guard as a parameter. This middleware is triggered when we try to visit any page for authenticated users.

We can then determine the type of authentication the user has and redirect them accordingly.

Now, will modify authentication exception handler:

To user tries to visit /writer they are redirected to /login/writer page and same for /admin, we have to modify the exception handler.

Open the handler file in app/Exceptions and add the following:

// app/Exceptions/Handler.php
    <?php
    namespace App\Exceptions;
    use Exception;
    use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
    use Illuminate\Auth\AuthenticationException;
    use Auth; 
    class Handler extends ExceptionHandler
    {
        protected function unauthenticated($request, AuthenticationException $exception)
        {
            if ($request->expectsJson()) {
                return response()->json(['error' => 'Unauthenticated.'], 401);
            }
            if ($request->is('admin') || $request->is('admin/*')) {
                return redirect()->guest('/login/admin');
            }
            if ($request->is('writer') || $request->is('writer/*')) {
                return redirect()->guest('/login/writer');
            }
            return redirect()->guest(route('login'));
        }
    }

Lasty, run the application

php artisan serve

It should generally be available on http://localhost:8000

 

Don’t forget to visit to register writers and admins respectively.

http://localhost:8000/register/writer

http://localhost:8000/register/admin

 

Then visit below url for login the writers and admins respectively.

http://localhost:8000/login/writer

http://localhost:8000/login/admin

 

Conclusion

In this, we defined multiple guards to handle multiple authentications and access control. Also, handled redirection for authenticated user and redirection for an unauthenticated user.

Request a quote