How to Use Multiple Authentications in Your Laravel 10 App?

In order to use multiple authetication guards in a Laravel app we require PHP 8 or a higher version, Laravel 9 or 10. We would also require to have a basic understanding of how Laravel app works. We already know much about Laravel as we have done plenty tutorials for Laravel app. One of the things that we have seen most frequently used is guards while talking about multiple authentications. These things could be new to a newbie Laravel user but the pro-ones already get these terms. We are going to go-over about it as well.

Multiple authentications in Laravel helps us to direct different classes of users to to various parts of our Laravel 10 app. We get many reasons for having used multiple auth in our Laravel 10 app. Here are few instances:

#1 If we use a Laravel app for a larger company with tons of parts

#2 If the whole organization like customers and employees are using the same Laravel 10 app for interacting.

#3 If its blog is taken care of by the dedicated team and there’s a lot of interaction going on

If we consider all of the above uses as our fundamentals we are dealing users in 3 types in our Laravel app:

Laravel Blog Dedicated Team – To keep the content management intact on the blog we need to assign different authentication to the dedicated team

Customers – In order to access the system we need to authorize the customers

Company Employees – They need to access the system built on Laravel for a variety of duties to be performed. Their roles could be different to perform for administrative purposes.

So for developing all those roles to multiple authentication we have to create various user classes

Once we have prerequisites such as basic understanding of PHP and Laravel 10 covered everything else would be taken care of as well. We are good to go! We have to keep in mind that technically we are going create our Laravel app with 3 use-classes or user classes– admin, user and writer auth rules. We would also restrict them to a certain position with the help of guards.

#1 Let’s Create The Laravel App Project

We will use this command and crate our laravel app:

$ laravel new multi-auth
$ cd multi-auth

#2 Let’s Create Database For Our Project

We are going to use SQLite you can use any database you want for your Laravel 10 app. We have reasons for that it’s very fast, minimalistic and lightweight. We are using this command to create our SQLite database:

$ touch database/database.sqlite

Next we have to change .env file in our Laravel app directory and add these changes in it:

    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=homestead
    DB_USERNAME=homestead
    DB_PASSWORD=secret

To:

    DB_CONNECTION=/absolute/path/to/database.sqlite

This will ensure our application uses the SQLite driver for database connections.

#3 Creating Migrations

Now we are going to add admins-writers-tables migrations as Laravel provides users with that. It could be very simple like a basic table which we can later modify according to our requirement. Like we normally add/remove stuff to the tables.

Adding migration for admins

For adding admins-migration we would use this command:

$ php artisan make:migration create_admins_table

We will open migrations directory in the database and make changes to the admins-migration-file like we have shown here:

    // database/migrations/<timestamp>_create_admins_table.php

    [...]
    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();
        });
    }
    [...]

You can see above how we have defined everything above and it’s a basic migration for admins table. We use Eloquent’s datatypes for our table columns to define them and change them however we want to. One thing is important to understand we can always configure our tables as we want them to.

Adding migration for writers

First off we would use this command to create writers table:

$ php artisan make:migration create_writers_table

Next we would make changes to writers migration file according to the specifics like we have shown here:

database/migrations/_create_writers_table.php
[...]
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();
});
}
[...]

We just created a simple migration and defined the columns we want the writers table to have. Eloquent provides methods that represent datatypes of our database table, so it is easy to decide what we want each one to be.

#4 Database Migration

After editing the tables and defining rules according to our needs we would migrate the database by using this command:

$ php artisan migrate

#5 Models Set up

We should understand that we have different user-classes in our Laravel 10 app so different databse tables are being used. We need to define models for these database tables for authentication. These models would increase authenticable class as these models are the user-model.

Admin model

We are using this command for to make admins model. See the example here:

$ php artisan make:model Admin

Open the Admin model in app/Admin.php and add the following:

    // 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',
        ];
    }

As far as using the guard is concerned. If we are not using default guard we need to define the guar we would use for model authentication.
For instace we are using admin guard. The database columns that are fillable by putting in the fillable array informs Laravel about these model details. Take only these selected items (read: fillable array items) when I call create/update function. It’d help us prevent a condition when an app user tries to bypass the checks and inserts data that we do not want them to.

We also would not let them return columns while returning the model API/View in any scenario.

Writers model

We are using this command for creating writers model:

$ php artisan make:model Writer

Once created we would go into Writers Model make these changes:

// app/Writer.php
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',
];
}

#6 Defining The Guards

Laravel guards allows us to define how much authentication we can give to each request. The guards will help us use Laravel authentication system with the Admin and Writer models we created but we can make our own rules as well. Let’s see the example here:

Open config/auth.php and add the new guards edit as follows:

    // config/auth.php

    <?php

    [...]
    'guards' => [
        [...]
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],
        'writer' => [
            'driver' => 'session',
            'provider' => 'writers',
        ],
    ],
    [...]

Here we introduced 2 new guards to the admin and writer along with the providers for informing Laravel about authentication and validation.

Now, add the following to the providers array:

    // config/auth.php

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

 

Now, we have set up the providers we defined along with the guards above and use eloquent as a driver our database manager is Eloquent ORM already. If we want to use a different ORM such as RedBeanPHP to manage the database then we would definitely choose redbeanphp instead. We would pass the model that we want provider to use.

#7 Let’s Work On Our Controllers

We can create new auth controller or modify the default ones in order to use the guards. We would choose the one which suffice to our requirements. Here we are just making changes to the default controllers.

#8 Adding Changes to LoginController

We would go to the LoginController in app/Http/Controllers/Auth and edit as follows:

    // app/Http/Controllers/Auth/LoginController.php

    <?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');
        }
        [...]
    }

We have to restrict controller access so we would set the middleware. We must define and add necessary guests to the controller. It will redirect you to the defined auth page if a type of user is already logged in and we are trying to add another user-type. It makes other jobs very easy.

For instance if user-A is an admin and he has logged-in while the user-B is a writer who wants to use his account, the user-B would be restricted. It would keep the sessions clean and app data untampered.

We have add rules for admin:

    // app/Http/Controllers/Auth/LoginController.php

    [...]
    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'));
    }
    [...]

You’d notice we have added rules for admin-login that could be returned. We would not do many changes to this page except changing the URL for different user-types. We can write a separate script for that but that would also be the same. So to save it, it’s the best way. We can change color schema etc but there is no need for writing it over again.

We also defined the adminLogin method which checks that the right credentials are supplied. Then we attempt to log a user in with the admin guard. It is important we set this guard when attempting a login so that the Auth facade will check the right table matching credentials. It will also set up our authentication so we can restrict pages based on the type of user who is logged in.

We redirect an authenticated user to a specific URL and send an unauthenticated user back to the login page. As we try to login with that after setting up the guard the authentication will be checked through same table of user/passowrds. We can allow certain users to access certain parts of the app depending on predefined rules of auth for that. Our guards would restrict the user-types this way.

Next we’d take similar steps for the Writers:

    // 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 are done defining login rules and setting it up.

#9 Adding Changes to RegisterController

Let’s edit the RegisterController first like we have shown here:

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

    <?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');
        }
      [...]
    }

You can see controller’s middleware has been set up like LoginController’s earlier. It’s time to apply the necessary changes for return registration pages for user-types:

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

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

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

You’d notice similarities here with the Login pages we set up earlier. So we are able to explain a bit about admin methods we are using:

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

    [...] 
    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');
    }
    [...] 

So we will explain a bit about writer methods we are using:

    // 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');
    }
    [...]

Now our registration part has been finalized.

#10 Defining Auth Pages

For the right auth system we are using Laravel’s auth scaffolding to get our pages and controllers created. We have to use this command:

$ php artisan make:auth

You would notice the view files in resources/views/auth to take care of basic auth for Laravel 10 app. This is amazing stuff.

Now we have to go the login.blade.php file and make necessary changes as we have done here:

    // resources/views/auth/login.blade.php
    [...]
    <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>

Next we have to check for the url parameter has been passed or not to the page as we made the call. To use the url parameter we have to edit the forms section. We added changes to the form header as well to show the right user-type based on teh credentials used.

Let’s go the to register.blade.php file make the necessary changes like we have done here:

    // resources/views/auth/register.blade.php

    [...]
    <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>

We have done everything we did on the login page.

#11 Adding Necessary Pages Auth User Can Access

As we have set up the login-register page, now we are adding the admin and writer pages as they would use being authenticated. We would use this script to run the command in our terminal:

    $ 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

Next we would add this piece of code in the auth.blade.php:

    // 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>

Now let’s add this piece of code in our Laravel 10 app’s 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

Next go to the file writer.blade.php and add these changes:

    // 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

Next we have go to our file home.blade.php and make changes like we have shown here:

    // 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

 #12 Set up the routes

Our app is almost ready now. We need to add routes in order to access the pages we have generated or added until now. Let’s go to our file routes/web.php and add this code there:

    // routes/web.php

    <?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');

Next we have to add changes that would help users access the right pages after ensuring authentication. You’d know that in Laravel all users are redirected to /home by default. We would encounter an error like we have shown here if fail to add redirect-route.

So we have to address it. Lets go to the file app/Http/Controllers/Middleware/RedirectIfAuthenticated.php add this script in it like we have shown here:

    // 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);
        }
    }

Our RedirectIfAuthenticated middleware would receive guarad as our primary parameter. So it’d trigger if access any of the pages that’s supposed to be for authneticated users only. Then we can auth-type the user is allowed and redirect him to it:

Now we would add changes to auth exception handler

We have to modify the auth exception handler as definitely we want to redirect the users to the respective login pages so that admins/writers get redirected to distinctive login pages. We have to add exception changes. We would go to the file app/Exceptions add this piece of code there:

    // 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'));
        }
    }

So we get to redirect the users to the respective pages and our issue resolves. We would use request→is() that would make sure the url is correct or the pattern used for the URL is absolute or in other cases if a router group is being used. In any sense the URL gets checked.

Let’s say we get a JSON request and handle the exception specifically with respect to the specifics. After that we check admin acess or nature of the URL. Then the respective users get redirected to separate login-pages. Same goes for Writer access pages.

THIS way we get to define everything and every respective page for access from writer to admin etc. whatever roles are there. Our laravel guard would protect every route with the same prefix.

#13 Let’s Test Our Laravel 10 App Now

Now we are done with every part of our app and have done the auth for everyhting we decided and made rules including the respective access pages and guarding the ones we don’t want to.

Let’s use this command to run our app:

$ php artisan serve

We can visit it on http://localhost:8000

We need to make sure that we are vising it on http://localhost:8000/register/writer and http://localhost:8000/register/admin to register writers and admins respectively. Then visit http://localhost:8000/login/writer and http://localhost:8000/login/admin to login the writers and admins respectively.

So that’s how we can understand and add rules for Laravel authentication in Laravel 10 app. We explained and introduced guards, handlers for access control and also redirecting respective users to relevant pages. We also learned how we can restrict users by using simple scripts.