Custom Zend_Auth_Adapter and Zend_Auth_Storage classes

I’m working on a small application these days using Zend Framework‘s MVC components. I use Zend_Auth for authentication and since I did not want to use Zend_Session I decided to implement a custom Zend_Auth_Storage adapter that uses the regular session handling in PHP. Currently the application does authentication towards a database table (users) and since I have made model classes for my tables and rows I wanted the adapter to provide me with an instance of the User class (which implements the Zend_Db_Table_Row_Abstract class and holds some custom logic). First, the storage:

class STM_Auth_Storage implements Zend_Auth_Storage_Interface {
    /**
     * See if the session is empty
     *
     * @return boolean
     */
    public function isEmpty() {
        return empty($_SESSION['stm']['user']);
    }

    /**
     * Read the contents from the session
     *
     * @return null|User
     */
    public function read() {
        $contents = null;

        if (!empty($_SESSION['stm']['user'])) {
            $contents = $_SESSION['stm']['user'];
        }

        return $contents;
    }

    /**
     * Store content in the session
     *
     * @param User $contents
     */
    public function write($contents) {
        if (!($contents instanceof User)) {
            $contents = null;
        }

        $_SESSION['stm']['user'] = $contents;
    }

    /**
     * Clear the data stored in the session
     *
     */
    public function clear() {
        unset($_SESSION['stm']['user']);
    }
}

As you can see we only need to implement a simple interface with 4 methods. The phpdoc comments should be enough to understand whats going on in the methods. Now over to the authentication adapter:

class STM_Auth_Adapter implements Zend_Auth_Adapter_Interface {
    /**
     * Username
     *
     * @var string
     */
    protected $username = null;

    /**
     * Password
     *
     * @var string
     */
    protected $password = null;

    /**
     * Class constructor
     *
     * The constructor sets the username and password
     *
     * @param string $username
     * @param string $password
     */
    public function __construct($username, $password) {
        $this->username = $username;
        $this->password = $password;
    }

    /**
     * Authenticate
     *
     * Authenticate the username and password
     *
     * @return Zend_Auth_Result
     */
    public function authenticate() {
        // Try to fetch the user from the database using the model
        $users = new Users();
        $select = $users->select()
                        ->where('username = ?', $this->username),
                        ->where('password = ?', md5($this->password));
        $user = $users->fetchRow($select);

        // Initialize return values
        $code = Zend_Auth_Result::FAILURE;
        $identity = null;
        $messages = array();

        // Do we have a valid user?
        if ($user instanceof User) {
            $code = Zend_Auth_Result::SUCCESS;
            $identity = $user;
        } else {
            $messages[] = 'Authentication error';
        }

        return new Zend_Auth_Result($code, $identity, $messages);
    }
}

This is also a small class implementing a simple interface. In the authenticate method I do new Users(); to fetch an instance of the gateway to the users table using an implementation of Zend_Db_Table_Abstract. I then make a select statement, that will try to find a valid user, and use that as an argument to the fetchRow() method. If the result is an instance of the User model which is an implementation of the Zend_Db_Table_Row_Abstract class I change two values that is used to create the Zend_Auth_Result return object. If the call to fetchRow() does not return a valid user, we add an error message to the $messages array for the Zend_Auth_Result return object. To log in I have made a form that sends a request to /user/login with some POST data. The login action in the user controller looks something like this:

class UserController extends Zend_Controller_Action {
    /**
     * Authentication object
     *
     * @var Zend_Auth
     */
    protected $auth = null;

    /**
     * Pre dispatch method
     *
     * This code will be executed before the the front controller tries to dispatch the current
     * request.
     */
    public function preDispatch() {
        $this->auth = Zend_Auth::getInstance();
    }

    /**
     * Login action
     *
     * Someone tries to login. Use the auth object to see if the input is valid.
     */
    public function loginAction() {
        $request = $this->getRequest();
        $authAdapter = new STM_Auth_Adapter($request->username, $request->password);
        $result = $this->auth->authenticate($authAdapter);

        if ($result->isValid()) {
            // Yay! User is authenticated and stored in the session (via the storage class)
            $this->_redirect('/user/index');
        } else {
            // Do some error handling...
        }
    }
}

My controller is a bit bigger than this bit I stripped away most of it to keep this example as simple as possible. First I have a preDistpatch() method that populates the protected $auth; attribute that is used in the login action. Before it is used we create the authentication adapter and provide it with the username and password that the user has typed in. If $result->isValid() evaluates to true we have fetched a valid user based on the given credentials. The User instance is also saved in the session under $_SESSION['stm']['user'] (as specified in the write() method in the STM_Auth_Storage class that can be found at the start of this post). And thats about that really. Feel free to play around with this code, and please let me know if there are any problems. If anyone can see some vulnerabilities with this please let me know.

Advertisements
This entry was posted in PHP, Technology and tagged , , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s