Hoang Manh Cam
Posted on March 19, 2024
Building a flexible multiple authentication system in Laravel is a crucial task for many web applications. This system allows different types of users, such as administrators and regular users, to log in through different authentication processes. This not only enhances the security of the application but also provides a more personalized user experience.
In Laravel, this can be achieved by leveraging its powerful and extensible authentication system. However, setting up multiple authentication can be a bit tricky as it involves several steps including installing Laravel, creating authentication scaffolding, defining user models, updating migration files, creating auth controllers, defining routes, creating views, updating middleware, and running migrations.
In the following guide, we will walk through each of these steps in detail, providing code examples and explanations to help you understand and implement a flexible multiple authentication system in your Laravel application.
Getting started
Install Laravel
First, you need to install Laravel using Composer. Open your terminal and run the following command:
composer create-project --prefer-dist laravel/laravel projectName
Replace projectName with the name you want to give to your project.
Install Laravel Breeze
Laravel Breeze is a simple authentication scaffolding built on Blade and Tailwind. You can install it using the following commands:
cd projectName
composer require laravel/breeze --dev
php artisan breeze:install
These commands will install the authentication controllers, views, and routes.
After running the breeze:install
command, you'll need to compile your assets using NPM:
npm install
npm run dev
Finally, run the migrations with php artisan migrate
.
Start the Laravel Development Server
You can start your Laravel development server using the php artisan serve
command and visit http://localhost:8000
in your web browser to see your application.
Setting Up Multiple Authentication
Modify .env
file
You need to add the following lines to your .env
file to set up multiple domains for each authentication type:
APP_ADMIN_DOMAIN=http://localhost
APP_ADMIN_PREFIX=/admin
APP_USER_DOMAIN=http://localhost
APP_USER_PREFIX=/
Add Route Files
You need to create two separate route files for admin and user:
routes/admin.php
Route::domain(env('APP_ADMIN_DOMAIN'))
->prefix(env('APP_ADMIN_PREFIX'))
->namespace('Admin')->group(function () {
Route::as('admin.')->group(function () {
Auth::routes([
'register' => false,
'reset' => false,
'verify' => false,
]);
Route::middleware('auth:admin')->group(function () {
Route::get('/home', function () {
return view('admin.home');
})->name('admin.home');
});
});
});
routes/user.php
Route::domain(env('APP_USER_DOMAIN'))
->prefix(env('APP_USER_PREFIX'))
->as('user.')->namespace('User')->group(function () {
Auth::routes([
'register' => true,
'reset' => true,
'verify' => true,
]);
Route::middleware('auth:user')->group(function () {
Route::get('/home', function () {
return view('user.home');
})->name('user.home');
});
});
Then you need to register these routes in your RouteServiceProvider
:
// app/Providers/RouteServiceProvider.php
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
$this->mapAdminRoutes(); // Add this line
$this->mapUserRoutes(); // Add this line
}
protected function mapAdminRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/admin.php'));
}
protected function mapUserRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/user.php'));
}
This code will register the routes defined in routes/admin.php
and routes/user.php
with the Laravel router. The mapAdminRoutes
and mapUserRoutes
methods are responsible for this. They tell Laravel to load the routes from these files and apply the web middleware group to them.
Finally you need to update the Authenticate
and RedirectIfAuthenticated
middlewares to handle the authentication and redirection based on the user type.
// app/Http/Middleware/Authenticate.php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*/
protected function redirectTo(Request $request): ?string
{
if (!$request->expectsJson()) {
if (Route::is('admin.*')) {
Auth::shouldUse('admin');
return route('admin.login');
} elseif (Route::is('user.*')) {
Auth::shouldUse('user');
return route('user.login');
}
}
return null;
}
}
// app/Http/Middleware/RedirectIfAuthenticated.php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next, string ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
if (Route::is('admin.*')) {
Auth::shouldUse('admin');
return redirect(RouteServiceProvider::ADMIN_HOME());
} elseif (Route::is('user.*')) {
Auth::shouldUse('user');
return redirect(RouteServiceProvider::USER_HOME());
}
}
}
return $next($request);
}
}
Then update app/Providers/RouteServiceProvider.php
: You need to define the ADMIN_HOME
and USER_HOME
constants. These constants should return the URLs for the admin and user home pages, respectively. You can use the env function to get these URLs from your .env
file:
public static function ADMIN_HOME()
{
self::$admin_home = env('APP_ADMIN_DOMAIN') . env('APP_ADMIN_PREFIX');
return self::$admin_home;
}
public static function USER_HOME()
{
self::$user_home = env('APP_USER_DOMAIN') . env('APP_USER_PREFIX');
return self::$user_home;
}
Now, your application should correctly set the authentication guard and redirect the user based on the domain of the incoming request.
Set Up Authentication Guards and Providers
Guards define how users are authenticated for each request. In your config/auth.php
file, you need two guards: user
and admin
. Each guard uses the session driver and a user provider, which specifies how the users are retrieved from the database. The user providers are also defined in the config/auth.php
file. Each provider uses the eloquent driver and specifies the model to use. The users provider uses the User model, and the admins provider uses the Admin model.
'guards' => [
'user' => [
'driver' => 'session',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
],
Setting Up Migration Files
Open your terminal and navigate to your project directory, then run the following commands:
php artisan make:migration create_users_table --create=users
php artisan make:migration create_admins_table --create=admins
Open the create_users_table.php
file and update the up method as follows:
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
Similarly, open the create_admins_table.php
file and update the up method as follows:
public function up()
{
Schema::create('admins', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
These migration files define the structure of your users and admins tables. You can run these migrations using the php artisan migrate
command. This will create the tables in your database.
Setup Models and Controllers
Models
Create or update file app/Models/User.php
with the following code:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
}
Also app/Models/Admin.php
:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class Admin extends Authenticatable
{
use Notifiable;
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
}
Controllers
Create or update file app/Http/Controllers/User/Auth/LoginController.php
with the following code:
<?php
namespace App\Http\Controllers\User\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use App\Providers\RouteServiceProvider;
class LoginController extends Controller
{
use AuthenticatesUsers;
protected $redirectTo;
public function __construct()
{
$this->middleware('guest')->except('logout');
$this->redirectTo = RouteServiceProvider::USER_HOME();
}
}
Create a new file app/Http/Controllers/Admin/Auth/LoginController.php
and add the following code:
<?php
namespace App\Http\Controllers\Admin\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use App\Providers\RouteServiceProvider;
class LoginController extends Controller
{
use AuthenticatesUsers;
protected $redirectTo;
public function __construct()
{
$this->middleware('guest:admin')->except('logout');
$this->redirectTo = RouteServiceProvider::ADMIN_HOME();
}
protected function guard()
{
return Auth::guard('admin');
}
}
These are the basic files for your Models and Controllers. You can add more methods and properties as per your requirements.
Setup Views
Since you're using Laravel Breeze, it already provides a good starting point for authentication views. However, you'll need to customize these views for each user type (user and admin).
User Views
For the user views, you can use the existing views provided by Breeze. These are located in the resources/views/auth
directory. The main views are:
-
login.blade.php
: This is the login form for users. -
register.blade.php
: This is the registration form for users. -
passwords/email.blade.php
: This is the form where users can request a password reset link. -
passwords/reset.blade.php
: This is the form where users can reset their password.
After login Laravel will redirect user to route /home
so we need to create a view for that. Create a new file resources/views/user/home.blade.php
and add the following code:
<!-- resources/views/user/home.blade.php -->
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('User Dashboard') }}</div>
<div class="card-body">
Hello User, {{ Auth::user()->name }}
</div>
</div>
</div>
</div>
</div>
@endsection
You can customize these views as needed for your application.
Admin Views
For the admin views, you'll need to create new views. You can follow the structure of the user views, but place them in a separate directory for admins. For example, you could create a resources/views/admin/auth
directory and add the following views:
-
login.blade.php
: This is the login form for admins. -
passwords/email.blade.php
: This is the form where admins can request a password reset link. -
passwords/reset.blade. php
: This is the form where admins can reset their password.
Here's a basic example of what the login.blade.php
view might look like:
<!-- resources/views/admin/auth/login.blade.php -->
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Admin Login') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('admin.login') }}">
@csrf
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<div class="col-md-6 offset-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
<label class="form-check-label" for="remember">
{{ __('Remember Me') }}
</label>
</div>
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Login') }}
</button>
@if (Route::has('admin.password.request'))
<a class="btn btn-link" href="{{ route('admin.password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
For the view home
after login successfully, create a new file resources/views/admin/home.blade.php
and add the following code:
<!-- resources/views/admin/home.blade.php -->
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Admin Dashboard') }}</div>
<div class="card-body">
Hello Admin, {{ Auth::guard('admin')->user()->name }}
</div>
</div>
</div>
</div>
</div>
@endsection
Now, after login, you can redirect the user to their respective home view based on their user type.
Testing out the application
To test the application, we need to create test accounts for both the user and the admin. Since we don't have a registration functionality, we will use Laravel's Tinker to manually create these accounts.
First, open a terminal and navigate to your Laravel project directory. Then, start a Tinker session by running the following command:
php artisan tinker
Once you're in the Tinker session, you can create an admin and a user with the following commands:
// Create an admin
$admin = new \App\Models\Admin;
$admin->email = 'admin@gmail.com';
$admin->name = 'admin';
$admin->password = bcrypt('secret');
$admin->save();
// Create a user
$user = new \App\Models\User;
$user->email = 'user@gmail.com';
$user->name = 'user';
$user->password = bcrypt('secret');
$user->save();
After running these commands, you should have an admin with the email admin@gmail.com
and password secret
, and a user with the email user@gmail.com
and password secret
.
Now, you can test the application by logging in with these accounts.
Summary
In this article, we have successfully implemented a multiple authentication system in Laravel for two user types: User
and Admin
. We have created separate migration files, models, controllers, views, and routes for each user type. We have also updated the Authenticate
and RedirectIfAuthenticated
middleware to handle multiple guards and updated the auth.php
configuration file to include User and Admin guards and providers.
We have also defined separate domains and prefixes for User and Admin in our .env
file. Furthermore, we have created views for login, home, and password reset for both User and Admin.
However, there are a few things that need to be checked or considered for a complete and robust authentication system. These include implementing the password reset functionality for both User and Admin, implementing the registration functionality if registration is allowed, implementing proper validation for all forms, implementing necessary security measures like hashing passwords, protecting against CSRF attacks, and testing all functionalities thoroughly.
This basic setup can be further enhanced based on the application's requirements, such as adding user roles, permissions, email verification, two-factor authentication, etc.
Posted on March 19, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.