LDAP Authentication in Laravel Application
LDAP stands for Lightweight Directory Access Protocol. It is a lightweight client-server protocol for accessing directory services. A directory is similar to database but contains more descriptive and attribute -based information. It is a hierarchical database where data is stored in tree like structure, where leaf node holds the actual data. Microsoft Active Directory, Oracle Directory, Open LDAP are some familiar products that uses LDAP. LDAP authentication authenticates the user via LDAP server such as Active Directory. For such, user should have valid directory record in LDAP server to get authorized to access certain system or services.
For implementing LDAP authentication in Laravel, authentication service passs user credentials to LDAP server for validation. Further authorization is handled by Laravel itself after authentication.
Requirements
To start, we must have php-ldap exension installed and enabled. If you already have it installed and enabled, you can skip this step. I have php version 7.2 in my system, so I’m installing extension for php7.2 (php7.2-ldap). Install extension as per your php version.
$ apt install php7.2-ldap
After installation completes, check enabled php modules with following command. It lists all active php extensions/modules.
$ php -m
Is ldap is not listed in the result, search for extension=php7.2_ldap.so in php.ini file and uncomment to enable.
Authentication
Now lets setup our application. We will have a fresh Laravel installation to start with.
composer create-project --prefer-dist laravel/laravel ldap_authentication
I’ll be using custom Authentication controller to handle authentication. You can use laravel make:auth
command to generate basic user authentication/registration controller, views and routes that can be used for basic authentication tasks.
Create a helper file called LDAP.php under App/Helpers/ folder. We will keep all methods to interact with LDAP here.
namespace App\Helpers; class LDAP { public $conn, $config; public function __construct() { $this->config = [ 'host' => env('LDAP_HOST', 'host'), 'base_dn' => env('LDAP_BASE_DN', 'dc=domain,dc=com'), 'account_suffix' => env('LDAP_ACCOUNT_SUFFIX', '@domain.com'), 'port' => env('LDAP_PORT', xxx) ]; } public function auth($uname, $pwd) { $this->conn = ldap_connect($this->config['host'], $this->config['port']); $bindDn = "uid=$uname,ou=people," . $this->config['base_dn']; ldap_set_option($this->conn, LDAP_OPT_PROTOCOL_VERSION, 3); if (ldap_bind($this->conn, $bindDn, $pwd)) { return $this->directoryEntry($uname, $bindDn); } return false; } public function directoryEntry($username, $dn) { $ldap_connection = $this->conn; $columns = ['givenname', 'sn', 'mail', 'uid', 'cn']; $filter = "(uid=$username)"; $result = ldap_read($ldap_connection, $dn, $filter, $columns); if ($result) { $entries = ldap_get_entries($ldap_connection, $result); if ($entries['count'] > 0) { for ($i = 0; $i < $entries['count']; $i++) { $temp = []; foreach ($columns as $col) { $temp[$col] = array_key_exists($col, $entries[$i]) ? $entries[$i][$col][0] : ''; } } } } ldap_unbind($ldap_connection); return $temp; } }
Let’s look at out LDAP.php helper class.
First we have two private variables, $conn and $config. $conn variable is use to hold the ldap connection resource and $config variable to bundle ldab server connection configs.
In constructor method __construct(), we initialized $config variable from .env file for ldap server, base_dn, account suffix and ldap server port.
auth() method we defined is the actual method which defines all processs to handle ldap server connection and authentication. ldap_connect() method opens connection with the ldap server and ldap_bind() method does the authentication and returns true or false for success or failure respectively.
directoryEntry() method gets the directory entry detail form ldap server after authentication, which we can use to create user session or authorization token, in case of token based authorization.
Then in AuthenticationController, authenticate() method definition will be like this:
namespace App\Http\Controllers; class AuthenticationController extends Controller { private $request; public function __construct(Request $request) { $this->request = $request; } public function authenticate(Request $request) { // validations... $user = $this->ldap_authentication(); if ($user) { // additional process for session initialization or token generation ... return redirect()->route('home'); } else return redirect()->back()->withErrors(['Invalid Credentials']); } private function ldap_authentication() { $authObject = $this->ldap->auth($this->request->username, $this->request->password); if ($authObject) return $authObject; else return false; } }
Here, I’m defining separate method called ldap_authentication() to handle ldap authentication as I intend to swap authentication from LDAP to DB authentication interchangeably. that way, I can segregate different authentication methods definitions.
Here, we called ldap_authentication() inside authenticate() method to validate user credentials and create session/authorization token or return error message accordingly.
This completes our simple LDAP authentication for our laravel application. This is basic bare-bone for LDAP authentication. We can add exception handling, data validation methods and/or get user data from user model (if we have user data stored in user model and using LDAP for authentication purpose only) for further authorization handling.
For implementing in Lumen, its same as in Laravel. Just that post authentication process for authorization i.e authorization token generation, will differ, where Laravel may use session handling.