Authentication with Zend Framework and Doctrine

At my new job, we’re using some pretty cool cutting edge object-oriented PHP technology: Zend Framework for MVC and Doctrine for ORM. If you don’t know what those acronyms mean, you probably shouldn’t bother reading the rest of this post. 🙂

As an exercise to ramp up on these two technologies, I modified the code from the Zend Framework quick start to authenticate users against a MySQL table using Doctrine.

I found a pretty good tutorial on Zend_Auth authentication adapter. The author of the tutorial says “I’m not going to go into specifics on this, as the documentation covers them, and your needs will vary based on your site.”. Well, the Zend documentation doesn’t cover them all that well, so it took me a while to figure this out. Surprisingly, I was unable to find any examples anywhere. I did find this proposal for an implementation, but it’s not quite complete. I figured I’d save someone else the trouble and post my solution here.

Here’s the adapter:

class MPSAuthAdapter implements Zend_Auth_Adapter_Interface
{
	private $_username;
	private $_password;

    /**
     * Sets username and password for authentication
     *
     * @return void
     */
    public function __construct($username, $password)
    {
        $this->_username = $username;
        $this->_password = $password;
    }

    /**
     * Performs an authentication attempt using Doctrine User class.
     *
     * @throws Zend_Auth_Adapter_Exception If authentication cannot
     *                                     be performed
     * @return Zend_Auth_Result
     */
    public function authenticate()
    {
    	$result = null;

    	try {
			$q = Doctrine_Query::create()
			    ->from('User u')
			    ->where('u.username = ?', $this->_username);

			$user = $q->fetchOne();
			if ($user == NULL) {
				$result = new Zend_Auth_Result(
			            Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND,
			            null,
			            array('sorry, login ' . $this->_username . ' was not found'));
			} else {
				if ($user->getPassword() != $this->_password) {
					$result = new Zend_Auth_Result(
				            Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID,
				            $user,
				            array('sorry, the password you entered was invalid for user ' .
                                                    $this->_username));
				} else {
					$result = new Zend_Auth_Result(
				            Zend_Auth_Result::SUCCESS,
				            $user,
				            array());
				}
			}
			return $result;
    	} catch(Exception $e) {
    		throw new Zend_Auth_Adapter_Exception($e->getMessage());
    	}
    }
}

… and here’s my version of LoginController.php:

// based on Matthew Weier O'Phinney's tutorial
// http://weierophinney.net/matthew/archives/165-Login-and-Authentication-with-Zend-Framework.html
require "MPSAuthAdapter.php";
require "BaseController.php";

class LoginController extends BaseController
{
    public function getForm()
    {
    	// MPS: note I had to change this classname
Erectile dysfunction can be in three forms  female viagra uk - mild, moderate and severe. order generic cialis  It reduces the symptoms of peripheral neuropathies. You can recover from the bad effects of aging in a short span of time with regular use of this herbal pill improves vitality, potency and virility. on line cialis It can become very enabling, cost viagra cialis  and disrupt your quality of life through proper exercise regimen and healthy balanced diet.     	// to work with the Zend autoloader:
        return new Default_Form_Login(array(
            'action' => '/login/process',
            'method' => 'post',
        ));
    }

    public function getAuthAdapter(array $params)
    {
        return new MPSAuthAdapter($params['username'],$params['password']);
    }

    public function indexAction()
    {
        $this->view->form = $this->getForm();
    }   

    public function processAction()
    {
        $request = $this->getRequest();

        // Check if we have a POST request
        if (!$request->isPost()) {
            return $this->_helper->redirector('index');
        }

        // Get our form and validate it
        $form = $this->getForm();
        if (!$form->isValid($request->getPost())) {
            // Invalid entries
            $this->view->form = $form;
            return $this->render('index'); // re-render the login form
        }

        // Get our authentication adapter and check credentials
        $adapter = $this->getAuthAdapter($form->getValues());
        $auth    = Zend_Auth::getInstance();
        $result  = $auth->authenticate($adapter);
        if (!$result->isValid()) {
            // Invalid credentials
            $form->setDescription(array_shift($result->getMessages()) .
                                                           "
please try again.");
            $this->view->form = $form;
            return $this->render('index'); // re-render the login form
        }

        // We're authenticated! Redirect to the home page
        $this->_helper->redirector('index', 'index');
    }

    public function logoutAction()
    {
        Zend_Auth::getInstance()->clearIdentity();
        $this->_helper->redirector('index'); // back to login page
    }
}

To ensure that all of the pages in the app are authenticated, I created a BaseController class that handles authentication, and derived all of my controller classes from it. Here’s that class:

class BaseController extends Zend_Controller_Action
{
    public function preDispatch()
    {
        if (!Zend_Auth::getInstance()->hasIdentity()) {
            if ('/login' != $this->getRequest()->getPathInfo() &&
                'login' != $this->getRequest()->getControllerName()) {
    			$_redirector = $this->_helper->getHelper('Redirector');
                $_redirector->gotoUrl('/login');
            }
        }
    }	

}

I’m still pretty new to all this, so I’m very interested in any comments.

Leave a Reply

Your email address will not be published. Required fields are marked *