Laravel UI

Introduction

Important: Before getting started, please complete the configuration guide.

Laravel UI provides basic authentication scaffolding out-of-the-box.

This guide will show you how to integrate LdapRecord-Laravel using this scaffolding.

Debugging

inside your config/ldap.php file, ensure you have logging enabled during the setup of authentication. Doing this will help you immensely in debugging connectivity and authentication issues.

If you encounter issues along the way, be sure to open your storage/logs directory after you attempt signing in to your application and see what issues may be occurring.

In addition, you may also run the below artisan command to test connectivity to your LDAP server:

php artisan ldap:test

Sessions

Before we begin, if you are using the database session driver, you must change the user_id column from its default type. This is due to LDAP Object GUID's being stored as the user's ID, which is not compatible with the unsigned big integer type:

From:

Schema::create('sessions', function (Blueprint $table) {
    // ...
    $table->foreignId('user_id')->nullable()->index();
    // ...
});

To:

Schema::create('sessions', function (Blueprint $table) {
    // ...
    $table->string('user_id')->nullable()->index();
    // ...
});

Login Controller

For this example application, we will authenticate our LDAP users with their email address using the LDAP attribute mail.

For LdapRecord to properly locate the user in your directory during sign in, we will override the credentials method in the LoginController:

// app/Http/Controllers/Auth/LoginController.php

use Illuminate\Http\Request;

protected function credentials(Request $request)
{
    return [
        'mail' => $request->email,
        'password' => $request->password,
    ];
}

As you can see above, we set the mail key which is passed to the LdapRecord authentication provider.

A search query will be executed on your LDAP directory for a user that contains the mail attribute equal to the entered email that the user has submitted on your login form. The password key will not be used in the search.

If a user cannot be located in your directory, or they fail authentication, they will be redirected to the login page normally with the "Invalid credentials" error message.

You may also add extra key => value pairs in the credentials array to further scope the LDAP query. The password key is automatically ignored by LdapRecord.

Updating Blade Views

Since an LdapRecord model instance will be returned when calling Auth::user() instead of an Eloquent model, you must change any references from:

Auth::user()->name

To:

Auth::user()->getName()

Using Usernames

In corporate environments, users are often used to signing in to their computers with their username. You can certainly keep this flow easy for them - we just need to change a couple of things.

First, let's jump into our auth/login.blade.php view and update our input field to use username instead of email:

<!-- resources/views/auth/login.blade.php -->

<!-- Before... -->
<input
  id="email"
  type="email"
  class="form-control @error('email') is-invalid @enderror"
  name="email"
  value="{{ old('email') }}"
  required
  autocomplete="email"
  autofocus
/>

<!-- After... -->
<input
  id="username"
  type="text"
  class="form-control @error('username') is-invalid @enderror"
  name="username"
  value="{{ old('username') }}"
  required
  autocomplete="username"
  autofocus
/>

After changing the HTML input, we now must modify our LoginController to use this new field. We do this by overriding the username method, and updating our credentials method:

// app/Http/Controllers/Auth/LoginController.php

use Illuminate\Http\Request;

public function username()
{
    return 'username';
}

protected function credentials(Request $request)
{
    return [
        'samaccountname' => $request->username,
        'password' => $request->password,
    ];
}

You can now sign in to your application using usernames instead of email addresses.

Displaying LDAP Error Messages

When a user fails LDAP authentication due to their password / account expiring, account lockout, or their password requiring to be changed, specific error codes will be sent back from your server. LdapRecord can interpret these for you and display helpful error messages to users upon failing authentication.

To add this functionality, you must add the following trait to your LoginController:

LdapRecord\Laravel\Auth\ListensForLdapBindFailure

Example:

// app/Http/Controllers/Auth/LoginController.php

// ...

use LdapRecord\Laravel\Auth\ListensForLdapBindFailure;

class LoginController extends Controller
{
    use AuthenticatesUsers, ListensForLdapBindFailure;

    // ...

However, this feature will only register automatically if your LoginController resides in the default App\Http\Controllers\Auth namespace. If you have changed the location of your LoginController, you must modify the constructor and add the following method call to register the LDAP listener:

// app/Http/Controllers/Auth/LoginController.php

// ...

use LdapRecord\Laravel\Auth\ListensForLdapBindFailure;

class LoginController extends Controller
{
    use AuthenticatesUsers, ListensForLdapBindFailure;

    public function __construct()
    {
        $this->middleware('guest')->except('logout');

        $this->listenForLdapBindFailure();
    }

    // ...
}

Altering the Response

By default, when an LDAP bind failure occurs, a ValidationException will be thrown which will redirect users to your login page and display the error. If you would like to modify this behaviour, you will need to override the method handleLdapBindError.

This method will include the error message as the first parameter and the error code as the second.

This is useful for checking for specific Active Directory response codes and returning a response:

// app/Http/Controllers/Auth/LoginController.php

// ...

class LoginController extends Controller
{
    // ...

    use ListensForLdapBindFailure {
        handleLdapBindError as baseHandleLdapBindError;
    }

    protected function handleLdapBindError($message, $code = null)
    {
        if ($code == '773') {
            // The users password has expired. Redirect them.
            abort(redirect('/password-reset'));
        }

        $this->baseHandleLdapBindError($message, $code);
    }

    // ...
}

Refer to the Password Policy Errors documentation to see what each code means.

Changing the Error Messages

If you need to modify the translations of these error messages, create a new translation file named errors.php in your resources directory at the following path:

The vendor directory (and each subdirectory) will have to be created manually.

Laravel >= 9

lang/
└── vendor/
    └── ldap/
        └── en/
            └── errors.php

Laravel <= 8

resources/
└── lang/
    └── vendor/
        └── ldap/
            └── en/
                └── errors.php

Then, paste in the following translations in the file and modify where necessary:

<?php

return [
    'user_not_found' => 'User not found.',
    'user_not_permitted_at_this_time' => 'Not permitted to logon at this time.',
    'user_not_permitted_to_login' => 'Not permitted to logon at this workstation.',
    'password_expired' => 'Your password has expired.',
    'account_disabled' => 'Your account is disabled.',
    'account_expired' => 'Your account has expired.',
    'user_must_reset_password' => 'You must reset your password before logging in.',
    'user_account_locked' => 'Your account is locked.',
];
Generated on September 7, 2024
Edit on GitHub