How to use multiple Guards in a Laravel application

Keywords: PHP Database Laravel SQLite

The article was forwarded from the professional Laravel developer community with the original link: https://learnku.com/laravel/t...

If you use Laravel For a while, you should have heard a lot about multiple identities.You should also have heard a lot about certification watchers.However, if you are unfamiliar with Laravel, multiple authentication allows different categories of users to access different/similar parts of the same application.

There are many reasons you might want to use multiple authentication in your Laravel application.For example, you have a large application running throughout the company.Customers also interact with the company's products and services through the same application.The app should also have a blog, which is managed by a department in the company.

From the program above, you can see that there are already three groups of users.Customers, we can let them access the application using a specific authentication process; authors, they should have another authentication process, and even have permission to enable a more robust content management process; and other members of the company, you should show them different menus of functionality based on their different roles.
Now let's see how to create multiple authentication for different categories of users.

Requirement

  1. Knowledge Reserve PHP (Version >= 7.1.3).
  2. Knowledge Reserve Laravel (Version 5.6.x).
  3. Computer installed Composer (Version >= 1.3.2).
  4. Computer installed Laravel Framework .

start

If you've met all the criteria on the list above, you can continue with this tutorial and we'll create an application with three user classes - admin, writer, user.We will set up different guards restrictions for these three user classes.

Create an application

We create a new Laravel application and execute the following command at the terminal to create a new Laravel application.

    $ laravel new multi-auth
    $ cd multi-auth

Create a database

We use the SQLite database in our applications.It is a lightweight and very fast file type database.We can create a database from the command line:

    $ touch database/database.sqlite

In your application, open the.env file and configure the following:

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

Modify to:

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

This will ensure that our applications use the SQLite database driver.

Create a database migration

We will create data table migration files for admins and writers tables, similar to those for users tables.These are simple user lists that you can expand in the future to suit your needs.

Create admins data table migration file

Run the following command to create an admins data table migration file:

    $ php artisan make:migration create_admins_table

In the database/migrations directory, open and modify the admins data table migration file:

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

We created a simple data table migration file and defined the fields of the data table we want to use.Eloquent gives us a way to define the data type of a database table field.We use them to define the data type of the table field.

Remember, you can configure your table as you like.

Create migration for writers

Create a data migration table for writers with the following commands:

    $ php artisan make:migration create_writers_table

Open the migration table file you just created to make changes:

    database/migrations/<timestamp>_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 file and defined a few required fields.Eloquent provides many ways to define field types for database tables, so it's easier to do.

Migrate Database

Now that we have defined the tables, we start migrating the database:

    $ php artisan migrate

Setting up the model

Our applications have different categories of users stored in different database tables, and to authenticate users using these different tables, we must define models for them.These models are similar to user models and extend verifiable classes.

Admin Model

Execute the following command to create an Admin model:

    $ php artisan make:model Admin

Open the Admin model file app/Admin.php 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 want to authenticate user access to a model without using the default user watcher, you need to specify a watcher.In this example, the watcher is admin.

In the fillable array, a database field is specified that the user can manipulate.That is, tell Laravel explicitly:

When I execute the create and update methods, I pass you the fields I want to manipulate as arrays, but you can only insert the fields specified in the fillable array into the database.

This prevents users from manipulating fields that we don't want to change.

In the hidden array, you can specify fields that you do not want to be returned.

Writers Model

Start creating the model for Writer by running the following command:

    $ php artisan make:model Writer

Then we open the Writer model and replace it with the following code:

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

Define Guards

Laravel's guard defines how to authenticate each request.There are some guards for authentication by default, but they can also be customized.This allows you to use Laravel's default authentication system and custom Admin and Writer models.

Open config/auth.php and add a new guard as follows:

    // config/auth.php

    <?php

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

Two new guards, admin and writer, were added and providers set up.Authenticate through the provider.

Now complete the providers array:

    // config/auth.php

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

Based on the watcher defined in the previous step, we refined the information of the providers array.The reason to use eloquent as the driver is that we use Eloquent ORM to interact with the database.

Suppose we want to use another ORM such as RedBeanPHP To operate on the database, set the driver to redbeanphp instead of eloquent.As for models, they are the model classes that will be specifically accessed for validation.

Setup Controller

In order for these watchers to play their respective roles, we have two options: modifying the existing validation controller and creating a new one.You can choose according to your specific needs.In this example, we choose the former.

Modify LoginController

Open the LoginController and make the following edits:

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

Restrict access to methods in the controller by means of middleware.We subdivided the types of visitors so that once you log in as one type of user, if you want to switch identities again, you will be directed to a predefined authentication page when you log in as another type of user.

For example: If I log on as an administrator on a computer, my colleague and author will not be able to log on to his account as an author.

This validation is important so that there is no potential risk to the application data due to the confusion of session information.

Now define admins login:

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

We have set up a method to return to the administrator's login page.We'll use the same page for all user types and only change the URL s they send to.It saves us a lot of code that we can avoid writing.

We also defined the adminLogin method, which checks to see if the correct credentials were provided.Then we try to log in to the user using admin guard.It is important to set this guard when attempting to log in so that Auth Facade will check for the correct table matching credentials.It will also set up our authentication so that we can restrict pages based on the type of user logged in.
We redirect the authenticated user to a specific URL and return the unauthenticated user to the login page.

Now let's do the same for 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'));
    }
    [...]

Our login is set up.Hooray!!!

Modify RegisterController

Open RegisterController and make the following edits:

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

We've set up the middleware and the controller will use it in the future, just like we do with LoginController.

Now, let's set up a way for different users to return to different registration pages:

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

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

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

This is similar to what we do when we display different login pages.

Now we can define a way to create admin:

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

Next, let's define how to create writer s:

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

Registration is complete.

Set Validation Page

We will use Laravel's own scaffolding to generate pages and controllers for the verification system.Execute the following command:

    $ php artisan make:auth

This will generate the template file resources/views/auth, along with the corresponding routes, to complete the basic validation operation.Cool right?

Open the login.blade.php file and make the following edits:

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

Here we will determine if there is a url parameter.If so, take this parameter to the form submission route.It also modifies the header information of the form page to show the type of user based on different login parameters.

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

    // 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 copied the operation of the login page.

Create pages that authenticated users will access

Now that we've set up the login and registration pages, let admin and writers see them when they verify the pages.Open the terminal and run the following command to create a new file.Next, we'll insert the snippet into the file.

    $ 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 this 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 template file and edit it 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 template file and replace it with:

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

Set Route

Our app is almost ready.Let's define the routes for all the methods created so far.Open routes/web.php and replace with:

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

Modify our user's authenticated redirection

It is important to modify the way users are redirected when they are authenticated.By default, Laravel redirects all authenticated users to /home.If you do not modify the redirection, you will get the following error.

So to solve this, open the app/Http/Controllers/Middleware/RedirectIfAuthenticated.php file and replace it with:

    // 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 RedirectIfAuthenticated middleware receives the auth guard as a parameter.This middleware is triggered when we attempt to access any page for an authenticated user.Then, we can determine what types of authentication users have and redirect them accordingly.

Modify Authentication Exception Handler

Something annoying happens when the user is redirected.If the user tries to access/writer but is not authenticated, the user is redirected to/login/writer, and they are redirected to/login which is not what we want.

To ensure that when users try to access/write, they are redirected to/login/write or are also used for/admin, we must 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'));
        }
    }

The unauthenticated method we just added solves the problem we are experiencing.It receives an AuthenticationExpection exception by default, which carries this protection information.Unfortunately, we can't access it because it is protected (hopefully Laravel 5.7 will provide a way to access it).

Our solution is to use request_is().This will check the URL we are trying to access.If we don't have absolute URLs or if we have routing groups, it can also check the URL pattern.

In our example, we first check to see if we have received a JSON request and handle the exception separately.Then we check if we are trying to access/admin or any URL that starts with admin.We redirect the user to the appropriate login page.We also check writer.

This is a good solution for us, but it means that we must know the absolute URL we want to access or at least that all routes protected by our guards have the same prefix.

Run the application

Now that our application is ready, run the following command to start it:

    $ php artisan serve

It should normally be available at http://localhost:8000.

Register authors and administrators by visiting http://localhost:8000/register/writer and http://localhost:8000/register/admin respectively. Then log in to authors and administrators by visiting http://localhost:8000/login/writer and http://localhost:8000/login/admin.

epilogue

In this tutorial, we delve into Laravel authentication.We have defined multiple guards to handle multiple authentication and access controls.We also handle redirections for authenticated users and for unauthenticated users.

If you fully follow this guide, you will be able to serve applications of different user types (possibly [multi-tenant]). https://en.wikipedia.org/wiki... ) Application) Set up basic authentication.Nevertheless, try to expand what you see and share what you think.

The source code for the application in this article can be found in GitHub.

Posted by ThEoNeTrUeAcE on Mon, 18 Nov 2019 19:21:46 -0800