Wednesday, October 28, 2009

Php login with Zend_Auth

Zend_Auth is the component of the Zend Framework which provides a standard authentication mechanism for web applications users. It has few dependencies (on Zend_Loader and on Zend_Session for default persistence of the authentication) and, as other framework components, will let you concentrate on the user experience instead of worrying about boilerplate code.

Zend_Auth is the facade class for this component and it is implemented as a Singleton. If you want to access it in your business classes wou may want to inject it in the constructor, relieving your code from coupling and make it simpler to unit test.
The shortest way to access Zend_Auth is by requesting its Singleton instance (I hope you will write this specific code only in a factory if you have a well-designed object-oriented application):
<?php
$auth = Zend_Auth::getInstance();
The $auth object has some methods which encapsulate the functionalities you have been busy reinventing for every project you are participating in. For instance to authenticate a user, just set up an adapter (more information on this later in the post) and write the following code:
$result = $auth->authenticate($adapter);
$result is a Zend_Auth_Result object, and has a method getCode() which you can call to access the result code created by the adapter during the request for authentication. 
switch ($result->getCode()) { 
            case Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND:
            case Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID:
                // bad...
                break;
    
            case Zend_Auth_Result::SUCCESS:
                // good...
}
Maybe you want to check if your user is already authenticated before redirecting him to a login form:
if (!$auth->hasIdentity()) {
    // redirect wherever you want... 
}
or you want to know the username that was passed to the adapter (again, more on setting up the adapter of your choice and passing it username and password later):
$name = $auth->getIdentity();
or certainly you want in some place to logout the user, as he chose by pressing the Logout button:
$auth->clearIdentity();

As a side note, remember that username and password assume the generic name of identity and credential troughout all classes contained in the Zend_Auth component. Moreover, the default storage for Zend_Auth successful authentication attempts is Zend_Session, which means a session cookie will be set on the client and the username will be saved as a session variable. Typically the session lifecycle will last till the browser closure and you have to provide alternate storage if you want a permanent authentication a la facebook.

An adapter is an object that bridges Zend_Auth with different authentication servers: it links together the infrastructure code of Zend_Auth with your business and domain layer. For instance, you can login via Ldap or via a relational table, by specifying the identity and credential column names:
$authAdapter = new Zend_Auth_Adapter_DbTable(
    $dbAdapter,
    'oss_users',
    'nick',
    'pwd'
);
where $dbAdapter is an instance of Zend_Db.
Don't want to tie your authentication with another zf component? No problem, it is indeed very simple even to create your adapter which uses PDO or whatever you want, even ini files. I just work recently on a server where PDO was not available and I could only call mysql_query() to access the database. Pragmatically, I wrote this adapter in about five minutes:
<?php

require_once 'Zend/Auth/Adapter/Interface.php';
require_once 'Zend/Auth/Adapter/Exception.php';
require_once 'Zend/Auth/Result.php';

class MyAuthAdapter implements Zend_Auth_Adapter_Interface
{
    private $_table = 'oss_users';
    private $_username;
    private $_password;

    public function __construct($username, $password)
    {
        $this->_username = $username;
        $this->_password = $password;
    }

    /**
     * @throws Zend_Auth_Adapter_Exception
     * @return Zend_Auth_Result
     */
    public function authenticate()
    {
        $q = mysql_query("SELECT * FROM $this->_table WHERE nick = '$this->_username'");
        if (!mysql_num_rows($q)) {
            return $this->_getResult(Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND);
        }
        if (mysql_num_rows($q) > 1) {
            throw new Zend_Auth_Adapter_Exception('Too many results.');
        }

        $row = mysql_fetch_array($q);
        if ($row['pwd'] != $this->_password) {
            return $this->_getResult(Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID);
        }

        return $this->_getResult(Zend_Auth_Result::SUCCESS);
    }

    protected function _getResult($code)
    {
        return new Zend_Auth_Result($code, $this->_username);
    }
}
Of course, $username and $password should be quoted in some way before passing them to the constructor, since PDO is not used here. After having created this object, I only had to pass it to Zend_Auth:: authenticate() to complete the process as I explained earlier in this post.

I hope you feel the power of Zend_Auth and the time it can save for you in many different php projects. If you already have experience with Zend Framework, it is the right time to start using a standard solution.

1 comment:

Flynn Barr said...

Zend_Auth is the part of the Zend framework which provides a conventional verification procedure for web programs customers. It has few dependencies and, as other framework elements.

ShareThis