How to implement multiple authentication in Laravel

cammanhhoang

Hoang Manh Cam

Posted on March 19, 2024

How to implement multiple authentication in Laravel

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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=/
Enter fullscreen mode Exit fullscreen mode

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');
            });
        });
    });
Enter fullscreen mode Exit fullscreen mode

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');
        });
    });
Enter fullscreen mode Exit fullscreen mode

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'));
}
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode
// 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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,
    ],
],
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
    });
}
Enter fullscreen mode Exit fullscreen mode

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();
    });
}
Enter fullscreen mode Exit fullscreen mode

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',
    ];
}
Enter fullscreen mode Exit fullscreen mode

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',
    ];
}
Enter fullscreen mode Exit fullscreen mode

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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');
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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.

💖 💪 🙅 🚩
cammanhhoang
Hoang Manh Cam

Posted on March 19, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related