Integrating LDAP and Email Authentication in Laravel Filament

david ibitoyedavid ibitoye
3 min read

Overview

In this implementation, we enable dual authentication for users in a Laravel Filament application. Users can log in using either:

  • LDAP Authentication (via an LDAP server)

  • Email Authentication (via a traditional email/password-based authentication stored in the database)

Step 1: Install Required Packages

To implement LDAP authentication, we need to install the adldap2/adldap2-laravel package, which allows easy integration of LDAP with Laravel.

  1. Install the LDAP package:

     composer require directorytree/ldaprecord-laravel
    
  2. Publish the configuration files for the LDAP package:

     php artisan vendor:publish --provider="LdapRecord\Laravel\LdapServiceProvider"
    

Step 2: Configure LDAP Authentication

  1. Edit the LDAP Configuration (config/ldap.php)

    Open the config/ldap.php configuration file and set it up according to your LDAP server's details. Here's an example of what it might look like:

     'connections' => [
    
             'default' => [
                 'hosts' => [env('LDAP_HOST', '127.0.0.1')],
                 'username' => env('LDAP_USERNAME', 'cn=user,dc=local,dc=com'),
                 'password' => env('LDAP_PASSWORD', 'secret'),
                 'port' => env('LDAP_PORT', 389),
                 'base_dn' => env('LDAP_BASE_DN', 'dc=local,dc=com'),
                 'timeout' => env('LDAP_TIMEOUT', 5),
                 'use_ssl' => env('LDAP_SSL', false),
                 'use_tls' => env('LDAP_TLS', false),
                 'use_sasl' => env('LDAP_SASL', false),
                 'sasl_options' => [
                     // 'mech' => 'GSSAPI',
                 ],
             ],
    
  2. Configure the Authentication Guard:

    In config/auth.php, add a custom LDAP guard. For example:

     'guards' => [
             'web' => [
                 'driver' => 'session',
                 'provider' => 'users',
             ],
    
             'db' => [
                 'driver' => 'session',
                 'provider' => 'db_users', // New provider for DB users
             ],
         ],
    
  3. Set up LDAP User Provider:

    Also in config/auth.php, add a user provider for the LDAP users:

    ```plaintext

'providers' => [ 'users' => [ 'driver' => 'ldap', 'model' => LdapRecord\Models\OpenLDAP\User::class, 'rules' => [], 'scopes' => [], 'database' => [ 'model' => App\Models\User::class, 'sync_passwords' => false, 'sync_attributes' => [ 'name' => 'cn', 'email' => 'mail', 'username' => 'uid' ], ], ],

'db_users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ], ],



### **Step 3: Handle Authentication Logic**

1. \*\*Modify the Login logic (\*\**app/Filament/Admin/Pages/Login.php*)

    In this step, you need to modify the login logic to allow both email and LDAP-based logins.

    For example, you can check if the email entered by the user matches an LDAP record, and if it doesn't, attempt to authenticate the user from the database:

    ```plaintext
    public function authenticate(): ?LoginResponse
        {
            try {
                $this->rateLimit(5);
            } catch (TooManyRequestsException $exception) {
                $this->getRateLimitedNotification($exception)?->send();

                return null;
            }

            $data = $this->form->getState();

            if (($data['login_type'] ?? 'username') === 'username') {
                // LDAP
                if (Filament::auth()->attempt([
                    'uid' => $data['uid'],
                    'password' => $data['password'],
                ], $data['remember'] ?? false)) {
                    $user = Filament::auth()->user();
                    if (
                        ($user instanceof FilamentUser) &&
                        (! $user->canAccessPanel(Filament::getCurrentPanel()))
                    ) {
                        Filament::auth()->logout();
                        $this->throwFailureValidationException();
                    }
                    session()->regenerate();
                    return app(LoginResponse::class);
                } else {
                    throw ValidationException::withMessages([
                        'data.uid' => __('filament-panels::pages/auth/login.messages.failed'),
                    ]);
                    return null;
                }
            } else {
                // Database
                if (Auth::guard('db')->attempt([
                    'email' => $data['email'],
                    'password' => $data['password'],
                ], $data['remember'] ?? false)) {
                    $user = Auth::guard('db')->user();
                    if (
                        ($user instanceof FilamentUser) &&
                        (! $user->canAccessPanel(Filament::getCurrentPanel()))
                    ) {
                        Auth::guard('db')->logout();
                        $this->throwFailureValidationException();
                    }
                    Auth::guard('web')->login($user, $data['remember'] ?? false);
                    session()->regenerate();
                    return app(LoginResponse::class);
                } else {
                    throw ValidationException::withMessages([
                        'data.email' => __('filament-panels::pages/auth/login.messages.failed'),
                    ]);

                    return null;
                }
            }

            $this->throwFailureValidationException();
        }
  1. Modify the Form

     public function form(Form $form): Form
         {
             return $form
                 ->schema([
                     Radio::make('login_type')
                         ->label('Login With')
                         ->options([
                             'username' => 'Username (LDAP)',
                             'email' => 'Email'
                         ])
                         ->default('username')
                         ->inline()
                         ->live()
                         ->required(),
    
                     // $this->getEmailFormComponent(), 
                     TextInput::make('uid')
                         ->label(__('Username'))
                         ->required()
                         ->visible(fn ($get) => $get('login_type') === 'username')
                         ->autocomplete()
                         ->autofocus()
                         ->extraInputAttributes(['tabindex' => 1]),
    
                     TextInput::make('email')
                         ->label('Email')
                         ->email()
                         ->required()
                         ->visible(fn ($get) => $get('login_type') === 'email')
                         ->autocomplete()
                         ->extraInputAttributes(['tabindex' => 1]),
    
                     $this->getPasswordFormComponent(),
    
                     // MyCaptchaField::make('captcha'),
                     $this->getRememberFormComponent(),
                 ])
                 ->statePath('data');
         }
    

Step 4: Add your credentials to your env file

After making these changes, clear your cache to ensure the configurations are applied correctly:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=employee
DB_USERNAME=root
DB_PASSWORD=

LDAP_CACHE=false
LDAP_LOGGING=true
LDAP_CONNECTION=default
LDAP_HOST=ldap.forumsys.com 
LDAP_USERNAME=null
LDAP_PASSWORD=null
LDAP_PORT=389
LDAP_BASE_DN="dc=example,dc=com"
LDAP_TIMEOUT=5
LDAP_SSL=false
LDAP_TLS=false
LDAP_SASL=false

Step 5: Clear Cache

After making these changes, clear your cache to ensure the configurations are applied correctly:

bashCopyphp artisan config:clear
php artisan cache:clear
php artisan route:clear

Conclusion

By following these steps, you’ve successfully integrated both LDAP and email-based authentication in your Laravel Filament application. Users can now log in with either their LDAP credentials or traditional database email/password authentication, giving you flexibility in user management.

0
Subscribe to my newsletter

Read articles from david ibitoye directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

david ibitoye
david ibitoye