Laravel Multi Auth using Guards and Spatie Permission with Example [API Authentication]

Parth Patel
6 min readOct 1, 2021

In my older article, I’ve described how to set up multi-auth using the guard on the web side. You can check that here.

In this tutorial, we will set up multi-auth for API authentication. As a developer, sometimes we need to develop a system where we have multiple types of roles and different roles have different permissions.

Below are the steps on how to configure Laravel to authenticate from different tables:

Step 1: Install Laravel

First of all, we need to install a fresh Laravel 8 version application in our system using the below command, So open the terminal and run the below command:

composer create-project --prefer-dist laravel/laravel demo

Step 2: Database Configuration

In this step, we will make/change a database configuration for our project. So let’s open the .env file and fill in all details like as bellow:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=database_name
DB_USERNAME=database_username
DB_PASSWORD=database_password

Step 3: Install Passport

Laravel Passport is an OAuth 2.0 server implementation for API authentication using Laravel. Since tokens are generally used in API authentication, Laravel Passport provides an easy and secure way to implement token authorization on an OAuth 2.0 server.

In this step we need to install passport via the Composer package manager, so on your terminal run the below command:

composer require laravel/passport

Once the package installs successfully, we require to get default migration to create new passport tables in our database. Passport comes with the database setup to store its access tokens and 0Auth2 client activities, run below command for setup:

php artisan migrate

Next, we need to install passport using the command, it will create token keys for security. To create that run the below command:

php artisan passport:install

Step 4: Install Spatie

In this step we will install spatie laravel permission package via the Composer package manager, so on your terminal run the below command:

composer require spatie/laravel-permission

If the service provider will not be registered automatically then we need to manually add the service provider in the config/app.php file:

'providers' => [
....
Spatie\Permission\PermissionServiceProvider::class
]

We can also change on the Spatie package, for that run below command and get config file in config/permission.php and migration files.

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

Now, we will run the below command for migration:

php artisan migrate

Step 5: Add Permission Middleware

Spatie package provides its in-built middlewares role and permission. To use that we have to add middleware in the app/Http/Kernel.php file.

....protected $routeMiddleware = [....'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,]

Step 6: Create migration and model for admins

To make the model and migration for admins table, run the following command:

php artisan make:model Admin -m

It will create migration and model both. From the database/migrations directory, open the admin's migrations file, and edit it as follows:

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

Now that we have defined our tables, let us migrate the database:

php artisan migrate

Step 7: Passport Configuration

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

use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasFactory, Notifiable, HasApiTokens, HasRoles;
protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
}

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

use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;
use Laravel\Passport\HasApiTokens;
class Admin extends Authenticatable
{
use HasFactory, Notifiable, HasApiTokens, HasRoles;
protected $guard = 'admin'; protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
}

Step 8: Add Passport in Service Provider

Now open app/Providers/AuthServiceProvider.php file and add route function in the boot function.

Passport::routes();

Step 9: Define the guards

Laravel guards define how users are authenticated for each request. Laravel by default provides some guards for authentication. You can check the default guards in the config/auth.php file.

Now before adding a new guard, first, we need to add the provider for that because all authentication drivers have a user provider. Let’s add a user provider for admin users where the driver is eloquent and the model is an Admin model class. After adding the provider array will look like below:

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

now we will create a new guard for admin users(admin-api), where the driver will be a passport(because we are using this for APIs) and the provider is admins. after adding that guard array will look like below:

'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'admin-api' => [
'driver' => 'passport',
'provider' => 'admins',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
]
]

Step 10: Updates in the controllers

Now, to login into the system as an admin user, first, we use the admin-api guard while login. We will add the below code in the login function of the login controller:

Auth::guard('admin-api')->attempt(['email' => $request->email, 'password' => $request->password], $request->get('remember'))

just make sure whenever you need to use attempt() or login(), and the driver is “token” or “passport” we need to set the driver as session. so, in our example we are using passport driver and to set driver as session add this line config(['auth.guards.admin-api.driver' => 'session']); before calling attempt().

If the user credentials exist in the database we will create a token like this:

if(Auth::guard('admin-api')->attempt(['email' => $request->email, 'password' => $request->password], $request->get('remember'))) {    //generate the token for the user
$token = auth()->guard('admin-api')->user()
->createToken('authToken')->accessToken;
}

APIs are generally stateless and do not use sessions, we generally use tokens to keep state between requests. So, we use this passport token to authenticate the user.

If we don’t pass the guard, laravel selects the default guard which is the web guard. You can change it in the config/auth.php file under the default array. and wherever you require to get the auth user data while using guard you can get it as follow:

Auth::guard('admin-api')->user();

Step 11: Assing role using Spatie

Using Spatie a role can be created like a regular Eloquent model, like this:

use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
$role = Role::create(['name' => 'admin']);
$permission = Permission::create(['name' => 'edit products']);

You can assign the role to a user like this:

$user = User::findOrFail($id);
$user->assignRole(['admin']);

You can also get the permissions associated with a user like this:

$permissions = $user->permissions;

And using the pluck method, pluck() you can get the role names associated with a user like this:

$roles = $user->roles()->pluck('name');

Other methods available to us include:

  • givePermissionTo(): Allows us to give permission to a user or role
  • revokePermissionTo(): Revoke permission from a user or role
  • hasPermissionTo(): Check if a user or role has a given permission
  • assignRole(): Assigns role to a user
  • removeRole(): Removes role from a user
  • hasRole(): Checks if a user has a role
  • hasAnyRole(Role::all()): Checks if a user has any of a given list of roles
  • hasAllRoles(Role::all()): Checks if a user has all of a given list of role

Spatie uses Laravel’s native @can directive to check if a user has a certain permission. You can check here for how we can use different directives.

@can('edit products')
//
@endcan

Spatie allows us to manage user permissions and roles using different methods, you can find all the methods in the official documentation.

Step 12: Use Spatie as middleware:

We can also use a spatie role as a middleware. To use a role as a middleware we need to add middleware on the group of routes or any specific route.

Route::group(['middleware'=> ['role:admin-api|admin']], function() {
Route::get('/products/{id}', [ProductController::class, 'edit']);
});

In the above sample ['role:admin-api|admin'] role is the middleware, admin-api is the guard which we created in the config/auth.php file and the admin is the role which we have assigned to the user. You can also use permission to authorize the routes. For more information check here.

Whenever any route from that group is called, the system will first check that the logged-in user is an admin user or not. If the logged-in user is not an admin then it returns an error message and if the user is an admin then the request proceeds further.

This is it!!!

--

--

Parth Patel

Software Developer. Laravel, Livewire, Vue, Angular, Ionic