Create a custom Drupal 7 authentication driver/provider in Laravel 4

Published on Dec 31, 2013

Note: this is a quick and dirty write-up, I'll check this article for spelling errors and the like when I have more time.

A project I'm currently working on has some non-standard requirements (which project doesn't right). A Drupal 7 website exists and we'd like to re-use the existing and future users that will be managed in the Drupal installation. These users should be able to easily login to our Laravel application. Since you arrived on this blog post, you are probably looking at a similar situation yourself.

The official documentation on extending authentication in Laravel can be rather vague so I hope this helps shed some light on how you can write a custom authentication provider yourself. I consider myself a Laravel beginner so this might not be the most elegant solution to this problem. If there is anything that can be improved, please share your thoughts in the comments.

How Drupal manages users

In our setup, Drupal runs in its own database. Let's call that database "drupal7". In a default installation, the Drupal table where users are stored is called "users" and the important columns for us are "name", "uid" and "pass".

Creating up a custom authentication driver in Laravel

The first thing you'll want to do is create a custom authentication driver in Laravel that will be responsible for checking a user password against the Drupal database. Only that mechanisms needs to be customised while keeping authentication in Laravel intact. That way, we can easily continue to use the useful and pratical methods Laravel already ships with when it comes to user login management.

Step 1: app/config/database.php

First, we will create a database connection that will allow our Laravel app to connect to the Drupal database, no matter where it's running. Find your database.php config file and add the following connection details to the already existing connections array:


'mysqldrupal' => array(
            'driver'    => 'mysql',
            'host'      => 'localhost',
            'database'  => 'drupal7',
            'username'  => 'youruser',
            'password'  => '',
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
        )

Later in this tutorial, we will use this connection when validating user credentials, since Drupal has stored these for us. Make sure you change the hostname and other details so that it points to your own Drupal database and the server where it's running on.

Step 2: app/config/auth.php

Now that we have that in place, we will make sure Laravel knows we'll be using a custom authentication driver when authenticating users. Find the auth.php config file and change the default driver from 'eloquent' to 'drupal'. Keep in mind that we don't have an authentication driver called 'drupal' yet, but we'll take care of that in the following steps.

'driver' => 'drupal',

Step 3: app/start/global.php

Now the real work begins. We'll be registering our own "drupal" authentication driver within Laravel. Find the global.php file in your installation and add the following. I have added this right below the ClassLoader definition. This step is also documented in the Laravel docs.


Auth::extend('drupal', function($app) {
   return new \Illuminate\Auth\Guard(new \GoodBytes\Auth\DrupalUserProvider, $app['session.store']);
});

Top put simply, this will create a new authentication method called "drupal" (pick any other name if you want to). Do you notice the \GoodBytes\Auth\DrupalUserProvider reference? That's a custom class I have created in my own namespace. Don't worry, you can simply follow my naming convention or you can now create your own classes and namespaces instead.

Step 4: app/GoodBytes/Auth/DrupalUser.php

This file will be our own definition of a user in the system. The documentation states that a "UserInterface" implementation should be returned by any custom authentication provider so that's what we'll do. This class describes where your users are stored in a database and what they look like. For example, you will notice that I have set the timestamps option to false. The database abstraction layer called Eloquent in Laravel normally likes to insert timestamps when records are added to the database, but since this is a Drupal table, we don't want Laravel to do that.

Laravel will simply be reading records when authentication users anyway so added timestamps wouldn't be that useful. Make sure you tell this class to use the "mysqldrupal" connection. That way, whenever we use this class, Laravel knows we're talking to the Drupal database by using the connection we created in step 1. By default, Eloquent assumes the primary key of all your tables is "id", but in Drupal it's "uid". Set the primaryKey property accordingly. Here's the full code I used:

getKey(); // uid column
    }

    /*
        @return The getAuthPassword should return the user's hashed password.
    */
    public function getAuthPassword(){
        return $this->pass; // the Drupal hashed password column
    }

}

Don't worry yet if Laravel barfs up errors, we're not done yet. One thing to keep in mind is that we need to run composer again to make sure our classes and namespaces are known in the system. See step 7 if you feel excited and can't wait for that.

Step 5: app/GoodBytes/Auth/password.inc

Drupal users will probably recognize the file name "password.inc" right away. If not, that's the file in Drupal that is responsible for creating and authenticating users. We need this file, because it contains a method called "user_check_password()" that compares a give password and username against the Drupal database. Drupal hashes and salts passwords in a specific way so we'll simply use their own methods to check whether or not a login and password combination is correct. Simply download Drupal and copy this file into your Laravel installation.

Step 6: app/GoodBytes/Auth/DrupalUserProvider.php

This is the core of our authentication process. We need to provide a class that implements the Laravel UserProviderInterface. This forces us to implement three methods that will ultimately be in charge of validating user credentials. Check the official documentation again and you will start to see the logic behind implementing custom auth drivers.

get()->first();
        return $user;
    }

    public function validateCredentials(UserInterface $user, array $credentials){
        return user_check_password($credentials['pass'], $user);
    }
}

Step 7: composer.json

Composer uses an autoloading mechanism so that we don't have to include a bunch of classes manually. If we want our own classes to be included, we need to edit composer.json in the root of your project and add our "app/GoodBytes" folder to the classmap. Do that, run "composer dump-autoload" from the command line and you should be all set! You should be able to avoid the command line for this by adding your class directories to the global.php file, but I haven't tried that myself.

Step 8: finishing up

Without a login form there won't be much to test so let's to that first. Create a simple view with a login form like this (I created views/drupal/login.php):

<form action="" method="post">
<label for="username">Username</label> 
<input id="username" name="username" type="text" /> 
<label for="password">Password</label> 
<input id="password" name="password" type="password" /> 
<input type="submit" value="Login" />
</form>

Then in your routes.php file:


Route::get('/drupal/logout', function(){
    Auth::logout();
    return Response::make('Your are now logged out...');
});

Route::get('/drupal/login', function()
{
    // print flash data "message", see "Redirects" on http://laravel.com/docs/responses
    echo Session::get("message");
    return View::make('drupal/login');
});

Route::post('/drupal/login', function(){
    // get the login and password from our form when a user posts the form
    $login = Input::get('username');
    $pass = Input::get('password');

    // let laravel make an attempt at logging in
    // 1) this will run retrieveByCredentials() to find a user in DrupalUserProvider
    // 2) after that, the returned user record will be checked for valid credentials in validateCredentials()
    if(Auth::attempt(array('name' => $login, 'pass' => $pass)))
    {
        // return some flash data: http://laravel.com/docs/responses
        return Redirect::to('drupal/login')->with('message', 'Authentication Succeeded');
    }
    else
    {
        return Redirect::to('drupal/login')->with('message', 'Authentication Failed');
    }
});

Conclusion

Writing your own authentication driver can be difficult especially when you are just starting out with Laravel but it all makes sense in the end and it shows how flexible Laravel really is. I hope this little write-up helps you implementing your own solution. If you happen to get stuck your best bet is to seek for help in the Laravel chat room or the forums where there is always a helpful and experienced Laravel developer like Dayle Rees [thanks for your support Dayle!] to help you out. Good luck!

No comments? But that’s like a Gin & Tonic without the ice?

I’ve removed the comments but you can shoot me a message on LinkedIn to keep the conversation going.