Laravel is so powerful for such a young framework. I am constantly surprised at how functionality is built in and so well thought out. One example of this incredible design, is Laravel’s ability to not only spin up authentication with one command in the terminal, but also to expand that authentication with so many awesome features like user roles, permissions, multi-authentication, social login, and more.

What is Multi-Authentication?

When i use the term multi-authentication, I want to clarify what I am talking about. I am talking about authenticating against multiple user models. It is important to note that this is different user models, and not just user roles. User roles can be achieved in a much simpler and less invasive process than what I am going to teach here. Today we are learning about authenticating against 2 (or more) different User models.

An example of this would be an ecommerce application. You have customers that have a login to your system and you store their information in the database. You process their orders, store their order history, payment tokens (because storing payment info directly would be sketchy…), manage their wishlists, and more. Customers need to be able to login to store and manage this data and will have profile pages. On the other hand, you also have employees of the company that need to log into another type of backend which allows them to manage products, refund orders, update shipping information, add coupons, and more. These are two entirely exclusive types of users. User roles would not work very elegantly in this situation because there are entirely different backends views/controllers, routes, and middleware between the two user types. In addition it wouldn’t make sense to share the same database table for these two different types of users because they are so different. Employee users will have database relationships with content that customers need no part in, and vice versa. This is a scenario when managing two different user models makes sense, Employees and Customers.

Let’s briefly look at the flip side of this, a case when Multi-authentication would be a BAD idea. Let’s explore the setup of a content management system or a blog. In this scenario you definitely want different types of users, but not necessarily different user models. We will have Authors that log into a backend so they can edit their own posts, Editors that log into the same backend to edit their posts, and also edit other Author’s posts. Finally we have Admins who have access to their own posts, everyone else’s posts, and global settings. In this example User Roles and Permissions is a better management paradigm than Multi-Authentication because these users can all be managed in the same model. Admins might have access to some more pages than other users, but they are still the same type of user as an Author or Editor, only they have more privileges. So we can store them in the same user model, maintain one backend and just restrict certain features or privileges to certain User Roles.

I point out this distinction because many loyal followers and YouTube Subscribers have been following this tutorial in the second example given above, when User Roles makes more sense. Before following this tutorial, take a moment to conceptualize your app and understand if this is the right path to take. Do you really need two user models, or are you looking for one model with multiple user permissions? I would say that 8/10 applications are better suited for User Roles than Multi-Authentication.

So you have considered your app and know that Multi-Authentication is the right type of Authentication for you? Great, carry on and let’s build!

Multi-Authentication in Laravel 5.4 Series

This is a long series covering this in 5 easy steps. Videos are available on YouTube covering the content in detail. The videos might get long as they cover lots of details and concepts, teaching you how this system works. The written tutorials are great for those looking for an easy step-by-step guide on doing this.

Part 1: Setting Up Multiple User Models

Part 2:

Part 3:

Part 4:

Part 5:

Let’s Get Started

To begin the process of setting up Multi-Authentication in Laravel, we will run the default authentication script. Hopefully you are already familiar with this and what it does. The reason we start from this is that it will give us the baseline to work with. It will set up on of our users right out of the box, and then we will use what it generates to work on our second user type.

In the terminal run the authentication scaffolding:

php artisan make:auth

Now we have a lot of new files in our application that we can start working with. The authentication will be set up around our default User.php model. I will be using the User model as our default customer in this tutorial. We will also set up an Admin user type with another model Admin.php which we will set up momentarily.

Now before we can use the authentication all that is needed after running the scaffolding command is to run our migrations. But before we run our migrations for the user model, we want to make a few more migrations to store our new user type.

If you go to database/migrations/ you will see two migrations that come with every Laravel app which build a users table in our database and a passwords table. These tables store our primary users and our “forgot my password” resets. For us to continue on, we need to create a new table to store our second user model, in our case admins because we are creating admins that manage content or something for our example app. We don’t need to make a second table for our passwords, because the two models can share the same passwords table (we will learn about this in Part 5).

So let’s go back to the terminal to generate a migration for a new admins table.

php artisan make:migration create_admins_table --create=admins

This creates a new migration named “create_admins_table” and the --create= flag tells the scaffolding that we are creating a new table named “admins” so that it can pre-populate the migration for us. This isn’t necessary, but it makes things easier.

Now we need to edit our new migration file to add whatever content we need to store for our admins and generate the table.

<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateAdminsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('job_title');
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('admins');
    }
}

As you see on lines 16-25, i have customized the table for our admins. This is a basic example, but you can add whatever fields you need for your admin. One column worth mentioning is the $table->rememberToken(); this is a special reserved column type in Laravel migrations that sets up the necessary column parameters to accept our “remember me” functionality, you make sure you add this column in the table somewhere. Now, save the migration file and then run the migrations to update our database.

Note: this should go without saying, but make sure you have visited your .env file or config/database.php file and set up the database connection and created an empty database to run the migration before actually running them.

php artisan migrate

Now we have tables set up for both user types. Time to set up the models to talk to these tables.

Setting Up Both Models

The good news is that the primary user model is set up already. We don’t need to do any magic to get this working. You might want to customize the mass-assignable fields in the $fillable array or customize the table (if you are making a table like “customers” instead of “users”) by editing the $table attribute. But that is really all you need to worry about to get the first model working.

To create our second model, we will be duplicating the default User.php model and renaming it to reflect our second user type (in our case Admins). Duplicate it now, and rename it as you wish. I will rename mine Admin.php.

My Admin.php code looks like this now:

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Admin extends Authenticatable
{
    use Notifiable;

    protected $guard = 'admin';

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password', 'job_title',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

I want to mention a few important parts of this code.

Line 5-6: We need to import Notifiable and Authenticatable. Notifiable is used for allowing us to send notifications to this user type through the Notifiable/Notifications api that Laravel has been using since Laravel 5.3 and doubled down on in Laravel 5.4 (without this most system notifications and packages that send out notifications will not work). Authenticatable on the other hand imports all the authentication code that Laravel ships with, without this you are just creating a normal model, and not a model that you can log in or authenticate against.

Line 10: enables Notifiable as I mentioned above

Line 12: sets the guard that we will authenticate against. This correlates with settings in config/auth.php and we will be setting this up in the next step. As of right now put whatever you want to call this type of user and remember it. We will set it up next.

The rest of the code in this file is standard model stuff, mainly to handle hidden and fillable fields to protect against mass-assignment. Update the fillable fields in line 20 to correlate with the columns in your table that you created in the migration that you want mass-assigned.

Setting Up Our Guards

Laravel ships with the ability to authenticate against multiple types of users. The way it handles multiple user type authentication is a system called “guards”. We can set up as many of these guards as we want and guards are how we tell Laravel which tables and drivers to use for different user types. Setting up guards is easy, and is the main focus of building a multi-auth setup.

To see the guards Laravel gives you by default, and to create new ones, we can visit our authentication config file. This is located in config/auth.php. Below you will find an example of this file in its default state with the comments taken out for brevity. The comments on this file are very detailed and worth a read when you apply these changes yourself in your next Laravel project.

<?php

return [

    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
    ],


    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ]
    ],

... (continues)

In this file we see the defaults at the top. This usually doesn’t need to be messed with, but this defines the default guard to authenticate against if no guard is specified. By default this is set to a guard called “web”.

By reading this file we can see we have two guards created. One guard is called “web” and the other is “api”. Both of these guards use the users provider to access data.  Providers are defined in this same file, just below guards, and are basically configuration shorthands that describe how to access data. If we look in the providers array we see the users provider links to the eloquent driver and the App\User model. This tells Laravel to user Eloquent ORM to interact with the database and use the User model. It basically sets up interacting with the users database.

The only difference between the api and the web guards is the driver they use. The api driver uses a token driver and the web guard uses the session driver. This explains to Laravel how to maintain the authentication. Guards that use the session driver can be remembered using sessions and cookies. This means that users don’t need to log in on every request. Because it uses sessions and cookies however, that means that the user needs to be physically looking at the page and accessing it in a browser. This is where the token driver comes in, the token driver requires authentication on every request via an api token. API authentication is a tutorial for another day, but basically Laravel will look for an api_token field in the header of every request and authenticate a user in for every request they make. This is how an API is handled, and is suitable when you have created an API for your Laravel app. For this tutorial we are focused on the session driver, for users that are navigating the site in a browser.

We can create our new guard for our new user type by appending another array into the Guards array.

<?php
return [

    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],
        'admin-api' => [
            'driver' => 'token',
            'provider' => 'admins',
        ],
    ],
    
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Admin::class,
        ],
    ],

... (continues)

As you can see, this time we will set up a guard called “admin” which correlates with the App\Admin.php model we set up in the previous step where I defined $guard="admin". I will use the session driver and the admins provider.

We need to also set up the admins provider and you can see i did that in the providers array. I linked this provider to my App\Admin::class model (make sure to get that ::class syntax on the end). For the most part you can always use the eloquent driver. Your other option would be “query_builder” but I don’t see much use to use it unless you were going rogue and had decided to use a different ORM other than eloquent in your project.

So now our guard for admins works. Laravel knows how to authenticate against it. We will be using this guard a lot going forward and you will understand its purpose better as you see how we use guards to declare many aspects of authentication in Laravel.

Testing It

We can test our functionality right now by creating a new view for our admins dashboard. This would represent a page that admin users have access to and customers do not have access to. We will protect it with our new guard so that only admins have access to it.

In your routes/web.php file, lets create a new route. We will put the url as /admin and link it to the AdminController@index.

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

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

Now we need to make an AdminController with an index method. To do this, I will simply duplicate the HomeController that already exists and rename it AdminController.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */

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

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return view('admin');
    }
}

In this controller it is simply a copy of HomeController, but I made 2 minor changes. First, in the Index function we changed the view to ‘admin’ so that it displays a view named admin (which we haven’t made yet). Lastly, I changed the middleware from “auth” to “auth:admin”. This is how we define the guard to protect this page against. If we simply set the middleware to be “auth” then we will inherit the default guard that you saw in the config/auth.php file earlier (in this case Users). So if we want to authenticate against a different guard, then we define it after a colon. So “auth:admin” says to display the page only to logged in admin users. If we used “auth:api” then it would authenticate against the “api” guard.

So by setting this to the admin guard in the middleware, we know that when we try to access this /admin url that it will require us to be logged in as an admin.

Lets create the “admin” view now and test this. Create a view in /resources/views/admin.blade.php. I made this by duplicating the home.blade.php file that is already in there and renaming it admin.blade.php. This is easy for demonstration purposes. I won’t show the code here, it is just a view file, with plain HTML. There isn’t anything relevant to the tutorial in the view code. Any HTML would work the same. The protection, requiring admin access happens at the controller level, not the view level.

Now let’s test our app in the browser. Register a new user by clicking the “Register” button and filling out the form. This will create a new “User” (the default user type) and log us in. Once logged in, we should have access to the /home url, because that is the user dashboard (you wouldn’t have access to this if you were logged out. But now if we go to the /admin url, we will get redirected back to /home and we will not have access to this page. This shows that our admin guard is working. We can not access the /admin unless we are logged in as an admin.

Testing Part 1

What’s Next?

This series is a 5 part series about completing the setup of Multi-Authentication in Laravel 5.4+. To continue this tutorial, please skip to the next part that interests you.

Part 1: (This Page) Setting up multiple models
Part 2: Views and Routes for Second Model
Part 3: Editing Authentication Middleware and Redirects
Part 4: Logging users out
Part 5: Forgot my Password Customization