DEV Community

Cover image for Multiple authentication in Laravel 9
Lâm Kim Phú
Lâm Kim Phú

Posted on

Multiple authentication in Laravel 9

Problem

In a basic web app, we usually have two parts, app and cms. App is for user to use and cms for staff to manage content in that system. So to design this web app, we need to have a role. User with role admin can access cms site and user without role admin cannot access cms. But what if you want to use 2 difference models for security, for performance? How can we authenticate with 2 difference models?

Introduce app

This is a web app have two parts, app and CMS. Model Staff can only access CMS part and model User can only access app part. Since Laravel have default guard authentication for User model, we will discuss more about Staff model

Configure guard

First of all, you need to add new guard to existing guard in config/auth.php.

   'guards' => [
        ...
        'staff' => [
            'driver' => 'session',
            'provider' => 'staffs',
        ],
    ],
Enter fullscreen mode Exit fullscreen mode

In this configuration, I add new guard name staff, this guard use driver is session and provider name staffs. Currently, we do not have any provider name staffs, so let's define it.

    'providers' => [
        ...
        'staffs' => [
            'driver' => 'eloquent',
            'model' => App\Models\Staff::class,
        ],
    ],
Enter fullscreen mode Exit fullscreen mode

With this definition, we define provider name staffs using driver eloquent and model we want to use is App\Models\Staff. Last but not least, we need to configure password part so we can reset password with Staff model.

    'passwords' => [
        ...
        'staffs' => [
            'provider' => 'staffs',
            'table' => 'staff_password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],
Enter fullscreen mode Exit fullscreen mode

This is quite simple. We use provider name staffs, table is staff_password_resets, expire and throttle is 60 minutes. Since we use staff_password_resets to reset password then remember to migrate it.

Ensure

After configuration, we need to ensure these things:

  • Migrate table for reset password It will look like this one:
public function up()
    {
        Schema::create('staff_password_resets', function (Blueprint $table) {
            $table->string('email')->index();
            $table->string('token');
            $table->timestamp('created_at')->nullable();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('staff_password_resets');
    }
Enter fullscreen mode Exit fullscreen mode
  • Have Staff model
<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class Staff extends Authenticatable
{
    use HasApiTo   'guards' => [
        ...
        'staff' => [
            'driver' => 'session',
            'provider' => 'staffs',
        ],
    ],kens, HasFactory, Notifiable;

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

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}
Enter fullscreen mode Exit fullscreen mode
  • Have data in table

Data in Staff table

Use new guard

Let's try out new guard. In login logic, you will have code like this:

Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))

This one will not work because it will use default guard which is user guard. If we want to use new guard, we need to explicitly use it. Change it to this:

Auth::guard('staff)->attempt($this->only('email', 'password'), $this->boolean('remember'))

So this one tell Laravel to use guard name staff instead of default. Try to login and check the session, you will see that we login successfully and have a session. However, we cannot go to page which ask for authentication. Why? Cause in these pages, we still not use new guard yet. This is my route:

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
Enter fullscreen mode Exit fullscreen mode

Take a look at middleware(['auth', 'verified']). It will go to auth and verified middlewar before run callback in Route::get. Verified middleware simply check verified_at colume in table is null or not and auth middleware check that user is authenticated or not. Cause we just use auth middleware then again, it use default guard which is user guard. We need to tell it to use staff guard by passing parameter to middleware. Change it to this one:

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth:staff', 'verified'])->name('dashboard');
Enter fullscreen mode Exit fullscreen mode

As you can see, we change to use auth middleware with staff guard instead of user guard. Then try to login again and you can access /dashboard route:

Dashboard after login
Perfection. You rock.

Testing

Lastly, we need to test to make sure that user in User table cannot use their data to login to /dashboard. This is user in User table:

User in User table
Try to login using this credential:

Try to login with User table
Failed. Great, this is what we expect, it cannot find any records cause when using staff guard, it just only take a look in Staff table. That's why, it did not found any records. That's all. Thanks for reading.

Top comments (0)