Effortless Multi-Authentication with Laravel Sanctum: A Swift Guide for Seamless Implementation

Effortless Multi-Authentication with Laravel Sanctum: A Swift Guide for Seamless Implementation

Introduction

Creating a system with multiple authentications using Laravel Sanctum may seem challenging, requiring modifications to your config/app.php configuration file. However, a quick and easy solution exists to achieve your goal. This article demonstrates how to implement multiple authentications with Laravel Sanctum.

Prerequisites

Before you delve into Laravel Sanctum, ensure you have the following prerequisites:

  1. Laravel Project: Set up a Laravel project either on your local environment or a server.

  2. Composer: Make sure you have Composer installed, as it's essential for managing Laravel packages.

  3. Basic Laravel Knowledge: Having a grasp of Laravel concepts such as routes, controllers, and migrations will prove helpful.

After installing a fresh instance of Laravel, the next step involves creating models for the users you plan to authenticate. For instance, consider the roles of students and teachers. Now, proceed to generate models and migrations for both the student and teacher entities.

php artisan make:model Student -m
php artisan make:model Teacher -m

Next, set up the Student and Teacher models by extending the Authenticatable class and incorporating the HasApiTokens trait.

Your models should now appear as follows:

/** Student Model */
class Student extends Authenticatable
{
    use HasApiTokens;
}
/** Teacher Model */
class Teacher extends Authenticatable
{
    use HasApiTokens;
}

Let's proceed by adding columns to both the students' and teachers' tables.

/** Student Table*/    
public function up(): void
    {
        Schema::create('students', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->decimal('password');
            $table->timestamps();
        });
    }
/** Teacher Table*/  
public function up(): void
    {
        Schema::create('teachers', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->decimal('password');
            $table->timestamps();
        });
    }

We are now ready to implement the authentication logic. Create two separate controllers: StudentAuthController and TeacherAuthController, respectively.

php artisan make:controller StudentAuthController
php artisan make:controller TeacherAuthController

In the StudentAuthController, let's start by implementing various functionalities. We will include the logic for user registration, login, and logout.

/** Student Register */
public function register(Request $request)
    {
        $student = Student::create([
            'name' => $request->input('name'),
            'email' => $request->input('email'),
            'password' => $request->input('password'),
        ]);

        $token = $student->createToken('student-token', ['actAsStudent'])->plainTextToken;

        return response()->json(['token' => $token, 'student' => $student], 201);
    }

/** Student Login */
public function login(Request $request)
    {
        $student= Student::where('email', $request->input->input('email'))->first();

        if (!$student|| !Hash::check($request->input('password'), $student->password)) {
            return response()->json(['message' => 'Email or password incorrect'], 401);
        }
        $token = $student->createToken('student-token', ['actAsStudent'])->plainTextToken;
        return response()->json(['token' => $token, 'student' => $student], 200);
    }

/** Student Logout*/
public function logout(Request $request)
    {
        $request->user()->currentAccessToken()->delete();
        return response()->json(['message' => 'Logged out successfully'], 200);
    }

Similarly, we will replicate the process in the TeacherAuthController.

/** Teacher Register */
public function register(Request $request)
    {
        $teacher= Teacher::create([
            'name' => $request->input('name'),
            'email' => $request->input('email'),
            'password' => $request->input('password'),
        ]);

        $token = $teacher->createToken('teacher-token', ['actAsTeacher'])->plainTextToken;

        return response()->json(['token' => $token, 'teacher' => $teacher], 201);
    }

/** Teacher Login */
public function login(Request $request)
    {
        $teacher = Teacher::where('email', $request->input->input('email'))->first();

        if (!$teacher || !Hash::check($request->input('password'), $teacher->password)) {
            return response()->json(['message' => 'Email or password incorrect'], 401);
        }
        $token = $teacher->createToken('teacher-token', ['actAsTeacher'])->plainTextToken;
        return response()->json(['token' => $token, 'teacher' => $teacher], 200);
    }

/** Teacher Logout*/
public function logout(Request $request)
    {
        $request->user()->currentAccessToken()->delete();
        return response()->json(['message' => 'Logged out successfully'], 200);
    }

With our models configured, the next step involves creating middleware to ensure that students cannot access teachers' routes and vice versa.

php artisan make:middleware StudentMiddleware
php artisan make:middleware TeacherMiddleware

In the middleware, our objective is to inspect each incoming request for the "actAsStudent" or "actAsTeacher" ability. If the request lacks these abilities, we will respond with a message indicating that the user is not authorized.

class Student
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        if (!$request->user()->tokenCan('actAsStudent')) {
            return response()->json('Not Authorized', 401);
        }
        return $next($request);
    }
}
class Teacher
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        if (!$request->user()->tokenCan('actAsTeacher')) {
            return response()->json('Not Authorized', 401);
        }
        return $next($request);
    }
}

We need to include these middlewares in the middlewareAliases array located in the app/Http/Kernel.php file.

'student' => \App\Http\Middleware\Student::class,
'teacher' => \App\Http\Middleware\Teacher::class,

Lastly, we should generate and attach these middlewares to our routes. As we are constructing APIs, our routes will be defined in the routes/api.php file.

/** Student Routes */
Route::controller(StudentAuthController::class)->group(function () {
    Route::group(['middleware' => ['auth:sanctum', 'student']], function () {
        Route::post('/student/logout', 'logout');
    });
    Route::post('/student/register', 'register');
    Route::post('/student/login', 'login');
});

/** Teacher Routes */
Route::controller(TeacherAuthController::class)->group(function () {
    Route::group(['middleware' => ['auth:sanctum', 'teacher']], function () {
        Route::post('/teacher/logout', 'logout');
    });
    Route::post('/teacher/register', 'register');
    Route::post('/teacher/login', 'login');
});

Conclusion

In conclusion, implementing a system with multiple authentications using Laravel Sanctum might initially seem complex. However, with the steps outlined in this article, you can efficiently achieve this goal. By setting up models, controllers, and middlewares tailored for different users, you can ensure secure authentication for distinct user roles. With Laravel's flexibility and tools like Sanctum, creating a robust authentication system that differentiates between users and restricts their access becomes a manageable task. This approach enhances the security and functionality of your application, providing a seamless experience for users.