This project is a showcase of authentication system for multi-users use. The application will have Teacher and Student, which will be authenticated with the same Register and Login form. The Teacher and Student can have more custom data such as name, degree, school, speciality...
1 - Clone
$ git clone https://github.com/mehdijai/laravel-multi-user-auth.git
cd laravel-multi-user-auth
2 - Install Composer packages
$ composer install
3 - Install NPM packages
$ npm install
4 - Create .env and copy .env.example content.
5 - Setup Database and update .env
6 - Generate App Key
$ php artisan key:generate
7 - Migrate Database changes
$ php artisan migrate
8 - Create pre-defined roles
$ php artisan db:seed
9 - Optimize and clear cache
$ php artisan optimize:clear
10 - Serve application
$ php artisan serve
1 - Install Laravel globally with composer if you didn’t already.
$ composer install laravel
2 - Create a new Laravel project with the command bellow:
$ laravel new multiusers
To handle authentication
1 - Install Breeze via composer
$ composer require laravel/breeze --dev
2 - Publish Breeze assets
$ php artisan breeze:install
3 - Install and compile NPM packages
$ npm install && npm run dev
4 - Migrate database
$ php artisan migrate
1 - Create “Role” model with migration and controller
$ php artisan make:model Role -mc
2 - Add these line to Models/Role.php
class Role extends Model
{
...
protected $table = "roles";
protected $fillable = ['name'];
}
4 - Add "name" field to migration
$table->string("name");
5 - Add 'role_id' to $fillable
in Model/User.php
protected $fillable = [
'name',
'email',
'password',
'role_id'
];
6 - Create migration to add "role_id" field
$ php artisan make:migration add_role_id --table=users
7 - Update migration file:
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->foreignId('role_id')->constrained('roles');
});
}
...
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('role_id');
});
}
1 - Update DatabaseSeeder.php
use App\Models\Role;
...
Role::create([
'id' => 1 ,
'name' => 'teacher'
]);
Role::create([
'id' => 2 ,
'name' => 'student'
]);
2 - Seed database
$ php artisan db:seed
1 - Create Teacher model
$ php artisan make:model Teacher -mc
2 - Add fillable and relationship to user
use Illuminate\Database\Eloquent\Relations\BelongsTo;
...
protected $table = 'teachers';
protected $fillable = ['name', 'user_id'];
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
...
3 - Add "name" and "user_id" field to migration
$table->foreignId('user_id')->constrained('users');
$table->string("name");
1 - Create Student model
$ php artisan make:model Student -mc
2 - Add fillable and relationship to user
use Illuminate\Database\Eloquent\Relations\BelongsTo;
...
protected $table = 'students';
protected $fillable = ['name', 'user_id'];
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
...
3 - Add "name" and "user_id" field to migration
$table->foreignId('user_id')->constrained('users');
$table->string("name");
1 - Initialize middleware
$ php artisan make:middleware checkRole
2 - Set handle function This middleware will redirect the user according to its role
public function handle(Request $request, Closure $next, string $role)
{
if ($role == 'teacher' && auth()->user()->role_id != 1) {
return redirect()->route('dashboard');
}
if ($role == 'student' && auth()->user()->role_id != 2) {
return redirect()->route('teacher.index');
}
return $next($request);
}
3 - Register middleware
Update app/Http/Kernel.php
protected $routeMiddleware = [
...
'role' => \App\Http\Middleware\checkRole::class
];
We will add an input to enable users to choose their roles
Add the code bellow to resources/views/auth/register.blade.php
under Email input (Or wherever you want)
<!-- Role -->
<div class="mt-4">
<x-label for="role" :value="__('Role')" />
<select class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 block mt-1 w-full" name="role" id="role" required :value="old('role')">
<option value="1">Teacher</option>
<option value="2">Student</option>
</select>
</div>
You can update the form based on the role. e.g Degree, Seciality, School...
Move to App\Http\Controllers\Auth\RegisterUserController.php
Update "store" function
use App\Models\Student;
use App\Models\Teacher;
...
$request->validate([
...
'role' => ['required', 'numeric', 'exists:roles,id'],
]);
$user = User::create([
...
'role_id' => $request->role,
]);
if($request->role == 1){
Teacher::create([
'name' => $request->name,
'user_id' => $user->id
]);
}else if($request->role == 2){
Student::create([
'name' => $request->name,
'user_id' => $user->id
]);
}
This is optional!
I changed HOME constant from "/dashboard" to "/student" in App\Providers\RouteServiceProvider.php
public const HOME = '/student';
NOTE: this will be the default route after authenticating. Be aware of that when you change Routes inside web.php
Add routes to web.php
.
The HOME constant will redirect the user to Student route by default. if the user is teacher it will be redirected to teacher router (Role middleware).
So, if you didn't change HOME, you should make one of the bellow prefixes "dashboard" to prevent "Route not found" Errors.
use App\Http\Controllers\StudentController;
use App\Http\Controllers\TeacherController;
...
Route::prefix('teacher')->name('teacher.')->middleware(['auth:sanctum', 'verified', 'role:teacher'])->group(function () {
Route::get('/', [TeacherController::class, 'index'])->name('index');
});
Route::prefix('student')->middleware(['auth:sanctum', 'verified', 'role:student'])->group(function () {
Route::get('/', [StudentController::class, 'index'])->name('dashboard');
});
1 - TeacherController
public function index()
{
return view("teacher.index");
}
2 - StudentController
public function index()
{
return view("student.index");
}
Create two folders inside views. One for teachers named teacher
, the other for students named student
.
Inside each one create blade file named index; index.blade.php
.
for testing purposes I will add just the below code. Change 'as Student'
based on the file:
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Dashboard') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
You're logged in as Student!
</div>
</div>
</div>
</div>
</x-app-layout>