Write Your Own Auth-Driver for Laravel 4

Because of some requests I translated my post Ein eigener Auth-Driver in Laravel 4. Enjoy it!

Laravel 4 comes along with two authentication drivers: Database und Eloquent. It is perfect for databases with PDO-drivers. But if you want to authenticate a user against other databases like MongoDB or web services etc., you have to do some work. Implementing an own auth-drive in Laravel 4 is a little bit different than in Laravel 3, so I want to tell you how it works.

Configuration

At first we have to tell Laravel which auth-driver we want to use. We will do it in app/config/auth.php. There we will find a line

'driver' => 'eloquent',

Here we will declare our own auth-driver. In this post the driver is named example. Therefore, we modify the line as follows:

'driver' => 'example',

That is not enough. Laravel will comlain that is does not know an auth-driver called example. We have to declare how to use the driver example. We will do it app/start/global.php. Just add the following block:

Auth::extend('example', function($app) {
    $provider =  new \Example\Auth\ExampleUserProvider();

    return new \Illuminate\Auth\Guard($provider, $app['session']);
});

The UserProvider

Now we get to the core of the matter: We have to implement the class ExampleUserProvider. This is a possible implementation:

<?php

namespace Example\Auth;

use Illuminate\Auth\UserProviderInterface,
    Illuminate\Auth\GenericUser,
    Example\Domain\User\Service\UserService,
    Example\Domain\User\Entity\User;

class ExampleUserProvider
    implements UserProviderInterface
{
    /**
    * @var UserService
    */
    private $userService;

    public function __construct(UserService $userService)
    {
        $this->userService = $userService;
    } 

    /**
     * Retrieve a user by their unique identifier.
     *
     * @param  mixed  $identifier
     *
     * @return \Illuminate\Auth\UserInterface|null
     */
    public function retrieveByID($identifier)
    {
        /** @var User $user  */
        $user = $this->userService->findUserByUserIdentifier($identifier);

        if (!$user instanceof User) {
            return false;
        }

        return new GenericUser([
            'id'       => $user->getUserIdentifier(),
            'username' => $user->getUserName()
        ]);
    }

    /**
     * Retrieve a user by the given credentials.
     *
     * @param  array  $credentials
     *
     * @return \Illuminate\Auth\UserInterface|null
     */
    public function retrieveByCredentials(array $credentials)
    {
        /** @var User $user  */
        $user = $this->userService->findUserByUserName($credentials['username']);

        if (!$user instanceof User) {
            return false;
        }

        return new GenericUser([
            'id'       => $user->getUserIdentifier(),
            'username' => $user->getUserName()
        ]);
    }

    /**
     * Validate a user against the given credentials.
     *
     * @param \Illuminate\Auth\UserInterface $user
     * @param  array  $credentials
     *
     * @return bool
     */
     public function validateCredentials(\Illuminate\Auth\UserInterface $user, array $credentials)
     {
         $validated = $this->userService->validateUserCredentials(
             $credentials['username'],
             $credentials['password']
         );

         $validated = $validated && $user->userName = $credentials['username'];

         return $validated;
     }
}

In Laravel 4 a UserProvider has to implement the UserProviderInterface and with it the methods

  • retrieveByID($id),
  • retrieveByCredentials($credentials) und
  • validateCredentials($user, $credentials).

The User-Object

Auth::user() allows to access the logged in user. But int the session Laravel only stores the ID. If we want to access the other data of the logged in user, we have to retrieve it from the database or another source. This is the job of the method retrieveById($id).

retrieveById($id)

You have to pass an ID as parameter. This can be an integer, a string or any other type.

The method has to return an object implementing the interface UserInterface. In the simple case – like in this example – it is the GenericUser that comes along with Laravel. The returned object has to have the property id, because it is stored in the session.

Authentication

Auth::attempt($credentials) kicks off the authentication. In Laravel 4 the authentication is done in two steps. At first retrieveByCredentials($credentials) finds a user satisfying the credentials. In the second step validateCredentials($user, $credentials) checks whether the return object fits to the credentials.

It seems that there is Dwork done twice, but it makes sense. It is not always possible to find a user by username and password. In the example above we can only find a user by its username. In the DatabaseUserProvider, that comes along with Laravel 4, the user is found by its password. In both cases you have to check, whether the credentials fits to the user object. Furthermore it makes testing easier.

retrieveByCredentials($credentials)

You have to pass an array $credentials as parameter. Typically it contains the keys username und password. Basically it is the developers choice, which data are passed. It is just important that you get the object you want.

The method has to return an object implementing the interface UserInterface too. In the simplest case it is the GenericUser.

validateCredentials($user, $credentials)

You have to pass an object $user implementing UserInterface and an array $credentials as parameters.

True will be returned if $user and $credentials matches, otherweise false will be returned.

Conclusion

Implementing an own Auth-Drivers in Laravel 4 is no rocket science. Just implement a UserProvider with these three methods, declare it to thes ssystem and tell the system that you want to use it.

15 Kommentare zu “Write Your Own Auth-Driver for Laravel 4”

    Error thrown

    Call to undefined function ereg()