Multi-User Authentication with Laravel | Simple Approach
Sometimes, there is a need to accommodate diverse user types with varying roles and permissions in the application. These types of users have different expectations of the application based on their roles, for example, consider two user and admin types of users. Different approaches can be taken to handle multi-guard authentication in Laravel. In this blog, the first and easiest approach will be discussed.
Database Design
First of all, we need to dedicate our database structure. In this method, just one table is responsible for saving the information of users, this table will contain a role column with an integer type to indicate the role of the user.
Project Initialization
Secondly, clone this GitHub repository, and follow the steps to run the project with Docker in the localhost. You can check the whole steps in this blog.
Multi-Authentication Code Implementation
Finally, we will implement two types of users to be authenticated, the first user is an admin and the second one is just a simple user, to get the multi-guard feature implemented, follow these steps:
- Create the user migration by running this command:
docker compose exec app php artisan make:migration create_users_table
2. The created migration will contain the below code structure.
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->integer('role');
$table->string('email')->unique();
$table->string('password');
$table->timestamps();
});
}
3. By default you have a User model in the app/Models
directory of the project so edit the fillable attribute of the model to support the role column of the table, then add role constant to the model.
const ROLE_ADMIN = 1;
const ROLE_USER = 2;
protected $fillable = [
'role',
'email',
'password',
];
4. Actually, we don’t have real users to get this project work and we have to add a seeder to create a list of different types of users. Create the UserSeeder class to seed some users' data.
class UserSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$users = [
[
'email' => 'admin@gmail.com',
'password' => Hash::make(1234567),
'role' => User::ROLE_ADMIN
],[
'email' => 'user@gmail.com',
'password' => Hash::make(1234567),
'role' => User::ROLE_USER
]
];
User::query()->insert($users);
}
}
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run(): void
{
$this->call(UserSeeder::class);
}
}
Finally, run docker compose exec app php artisan db:seed
5. Install Passport
In this step, we need the Passport package to be installed. Follow all steps in this documentation to do so. Finally, update the User model to use the Laravel\Passport\HasApiTokens
trait.
6. Define Authentication Guards in the config/auth.php
'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
7. Login User
In the next step, need to create a controller and define a route for handling the login action. The route/api.php and the LoginController will have the below codes:
class LoginController extends Controller
{
public function __invoke(Request $request): JsonResponse
{
/** @var User $user */
$user = User::query()->where('email', $request->get('email'))->first();
if (empty($user)) {
return response()->json([
'message' => 'The user does not exist'
]);
}
if (Hash::check($request->get('password'), $user->password)) {
return response()->json([
'message' => 'success',
'user' => $user,
'token' => $user->createToken('Laravel Personal Access Client')->accessToken
]);
}
return response()->json([
'message' => 'Email or password are wrong'
], 422);
}
}
Route::middleware('guest')->group(function () {
Route::post('login', LoginController::class);
});
8. Create Middleware
To handle the permissions of the users we have to create middleware and add to the routes which need specific access and permission. In this blog we will make two differents middleware to overcome this requirement, The first one is the AdminMiddleware that will add to the routes that are allowed for admins and the second one is UserMiddleware which will define the accessable routes for usual users. Actually you can create a single middleware and handle all these tasks there, but if it is better to respect the principle of Single Responsiblity and avoid doing so, because as your project goes larg this class will need to handle more roles and permissions as well. Here is the code of created middlewares:
class AdminMiddleware
{
public function handle(Request $request, Closure $next): Response
{
if (auth()->check() && auth()->user()->role === User::ROLE_ADMIN) {
return $next($request);
}
return response()->json([
'message' => 'Unauthenticated'
], 401);
}
}
class UserMiddleware
{
public function handle(Request $request, Closure $next): Response
{
if (auth()->check() && auth()->user()->role === User::ROLE_USER) {
return $next($request);
}
return response()->json([
'message' => 'Unauthenticated'
], 401);
}
}
After definition of middlewares, the $routeMiddleware
attribute of the app/Http/Kernel.php
will update as below:
protected $routeMiddleware = [
'admin' => AdminMiddleware::class,
'user' => UserMiddleware::class,
]
9. Limit Routes Accessiblity by Middleware
There are some routes that are accessable for just admins, also may have some routes for users. For example, the admin/dashboard
should be protected of users access such as user/dahsboard
which should not pass an admin.
Route::middleware('auth')->group(function () {
Route::middleware('admin')->group(function () {
Route::get('admin/dashboard', function () {
return response()->json(['message' => 'welcome, this is admin!']);
});
});
Route::middleware('user')->group(function () {
Route::get('user/dashboard', function () {
return response()->json(['message' => 'welcome, this is user!']);
});
});
});
To sum it up, the multi-authentication is one of the basic requirements of an application and we cover that as simple as possible, but consider if your project grows you will need more types of users to be authenticated, so this described will not a good choice to handle it.