Authentication with Zend_Auth in Zend Framework 1.8

Exclusive offer: get 50% off this eBook here
Zend Framework 1.8 Web Application Development

Zend Framework 1.8 Web Application Development — Save 50%

Design, develop, and deploy feature-rich PHP web applications with this MVC framework

€18.99    €9.50
by Keith Pope | October 2009 | MySQL Open Source PHP Web Development

In this article by Keith Pope, we will focus on how we can control access to parts of the application—Storefront and how users can log in to use the services provided by the it.

We will learn how to implement authentication in Zend Framework 1.8.

We will cover the following topics in this article:

  • Authentication versus Authorization
  • Using Zend_Auth

Let's get started.

Authentication versus Authorization

Before we go any further, we need to first look at what exactly authentication and authorization is, as they are often misunderstood.

Authorization is the process of allowing someone or something to actually do something. For example, if I go into a data centre, then the security guards control my authorization to the data centre and would, for instance, not allow me access to the server room if I was just a visitor but would if I worked there as a system admin.

Authentication is the process of confirming someone or something's identity. For example, when I go to into the data centre the security guards will ask me for my identity, which most probably would be a card with my name and photo on. They use this to authenticate my identity.

These concepts are very important so make sure you understand the difference. This is how I remember them:

Authorization: Can they do this?
Authentication: Are they who they say they are?

Authentication with Zend_Auth

To provide our authentication layer, we are going to use Zend_Auth. It provides an easy way to authenticate a request, obtain a result, and then store the identity of that authentication request.

Zend_Auth

Zend_Auth has three main areas—authentication adapters, authentication results, and identity persistence.

Authentication adapters

Authentication adapters work in a similar way to database adapters. We configure the adapter and then pass it to the Zend_Auth instance, which then uses it to authenticate the request.

The following concrete adapters are provided by default:

  • HTTP Digest authentication
  • HTTP Basic authentication
  • Database Table authentication
  • LDAP authentication
  • OpenID authentication
  • InfoCard authentication

All of these adapters implement the Zend_Auth_Adapter_Interface, meaning we can create our own adapters by implementing this interface.

Authentication results

All authentication adapters return a Zend_Auth_Result instance, which stores the result of the authentication request. The stored data includes whether the authentication request was successful, an identity if the request was successful, and any failure messages, if unsuccessful.

Identity persistence

The default persistence used is the PHP session. It uses Zend_Session_Namespace to store the identity information in the Zend_Auth namespace. There is one other type of storage available named NonPersistent, which is used for HTTP authentication. We can also create our own storage by implementing the Zend_Auth_Storage_Interface.

Authentication Service

We are going to create an Authentication Service that will handle authentication requests. We are using a service to keep the authentication logic away from our User Model. Let's create this class now:

application/modules/storefront/services/Authentication.php
class Storefront_Service_Authentication
{
protected $_authAdapter;
protected $_userModel;
protected $_auth;
public function __construct(Storefront_Model_User
$userModel = null)
{
$this->_userModel = null === $userModel ?
new Storefront_Model_User() : $userModel;
}
public function authenticate($credentials)
{
$adapter = $this->getAuthAdapter($credentials);
$auth = $this->getAuth();
$result = $auth->authenticate($adapter);
if (!$result->isValid()) {
return false;
}
$user = $this->_userModel
->getUserByEmail($credentials['email']);
$auth->getStorage()->write($user);
return true;
}
public function getAuth()
{
if (null === $this->_auth) {
$this->_auth = Zend_Auth::getInstance();
}
return $this->_auth;
}
public function getIdentity()
{
$auth = $this->getAuth();
if ($auth->hasIdentity()) {
return $auth->getIdentity();
}
return false;
}
public function clear()
{
$this->getAuth()->clearIdentity();
}
public function setAuthAdapter(Zend_Auth_Adapter_Interface
$adapter)
{
$this->_authAdapter = $adapter;
}
public function getAuthAdapter($values)
{
if (null === $this->_authAdapter) {
$authAdapter = new Zend_Auth_Adapter_DbTable(
Zend_Db_Table_Abstract::getDefaultAdapter(),
'user',
'email',
'passwd'
);
$this->setAuthAdapter($authAdapter);
$this->_authAdapter
->setIdentity($values['email']);
$this->_authAdapter
->setCredential($values['passwd']);
$this->_authAdapter
->setCredentialTreatment(
'SHA1(CONCAT(?,salt))'
);
}
return $this->_authAdapter;
}
}

The Authentication Service contains the following methods:

  • __constuct: Creates or sets the User Model instance
  • authenticate: Processes the authentication request
  • getAuth: Returns the Zend_Auth instance
  • getIdentity: Returns the stored identity
  • clear: Clears the identity (log out)
  • setAuthAdapter: Sets the authentication adapter to use
  • getAuthAdapter: Returns the authentication adapter

The Service is really separated into three areas. They are getting the Zend_Auth instance, configuring the adapter, and authenticating the request using Zend_Auth and the Adapter.

To get the Zend_Auth instance, we have the getAuth() method. This method retrieves the singleton Zend_Auth instance and sets it on the $_auth property. It is important to remember that Zend_Auth is a singleton class, meaning that there can only ever be one instance of it.

To configure the adapter, we have the getAuthAdapter() method. By default, we are going to use the Zend_Auth_Adapter_DbTable adapter to authenticate the request. However, we can also override this by setting another adapter using the setAuthAdapter() method. This is useful for adding authenticate strategies and testing. The configuration of the DbTable adapter is important here, so let's have a look at that code:

$authAdapter = new Zend_Auth_Adapter_DbTable(
Zend_Db_Table_Abstract::getDefaultAdapter(),
'user',
'email',
'passwd',
'SHA1(CONCAT(?,salt))'
);
$this->setAuthAdapter($authAdapter);
$this->_authAdapter->setIdentity($values['email']);
$this->_authAdapter->setCredential($values['passwd']);

The Zend_Auth_Adapter_DbTable constructor accepts five parameters. They are database adapter, database table, table name, identity column, and credential treatment. For our adapter, we supply the default database adapter for our table classes using the getDefaultAdapter() method, the user table, the email column, the passwd column, and the encryption and salting SQL for the password. Once we have our configured adapter, we set the identity and credential properties. These will then be used during authentication.

To authenticate the request, we use the authenticate method.

$adapter = $this->getAuthAdapter($credentials);
$auth = $this->getAuth();
$result = $auth->authenticate($adapter);
if (!$result->isValid()) {
return false;
}
$user = $this->_userModel
->getUserByEmail($credentials['email']);
$auth->getStorage()->write($user);
return true;

Here we first get the configured adapter, get the Zend_Auth instance, and then fetch the result using Zend_Auth's authenticate method, while passing in the configured adapter. We then check that the authentication request was successful using the isValid() method. At this point, we can also choose to handle different kinds of failures using the getCode() method. This will return one of the following constants:

Zend_Auth_Result::SUCCESS
Zend_Auth_Result::FAILURE
Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND
Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS
Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID
Zend_Auth_Result::FAILURE_UNCATEGORIZED

By using these, we could switch and handle each error in a different way. However, for our purposes, this is not necessary.

If the authentication request was successful, we then retrieve a Storefront_Resource_User_Item instance from the User Model and then write this object to Zend_Auth's persistence layer by getting the storage instance using  getStorage() and writing to it using write(). This will then store the user in the session so that we can retrieve the user information throughout the session.

Our Authentication Service is now complete, and we can start using it to create a login system for the Storefront.


Zend Framework 1.8 Web Application Development Design, develop, and deploy feature-rich PHP web applications with this MVC framework
Published: September 2009
eBook Price: €18.99
Book Price: €30.99
See more
Select your format and quantity:

Customer Controller

To use our new Authentication Service, we need to add the following to the Customer Controller:

application/modules/storefront/controllers/CustomerController.php
public function init()
{
// get the default model
$this->_model = new Storefront_Model_User();
$this->_authService =
new Storefront_Service_Authentication();
// add forms
$this->view->registerForm = $this->getRegistrationForm();
$this->view->loginForm = $this->getLoginForm();
$this->view->userForm = $this->getUserForm();
}
public function loginAction()
{}
public function authenticateAction()
{
$request = $this->getRequest();
if (!$request->isPost()) {
return $this->_helper->redirector('login');
}
// Validate
$form = $this->_forms['login'];
if (!$form->isValid($request->getPost())) {
return $this->render('login');
}
if (false === $this->_authService->authenticate
($form->getValues())) {
$form->setDescription('Login failed, please try again.');
return $this->render('login');
}
return $this->_helper->redirector('index');
}
public function logoutAction()
{
$this->_authService->clear();
return $this->_helper->redirector('index');
}
public function getLoginForm()
{
$urlHelper = $this->_helper->getHelper('url');
$this->_forms['login'] = $this->_model->getForm('userLogin');
$this->_forms['login']->setAction($urlHelper->url(array(
'controller' => 'customer',
'action' => 'authenticate',
),
'default'
));
$this->_forms['login']->setMethod('post');
return $this->_forms['login'];
}

We have added three new methods to the Customer Controller—loginAction, authenticateAction, and getLoginForm. We have also updated the init() method to instantiate the Authentication Service and add the login form to the view.

The loginAction is used to display the login form to the user and simply renders the login.phtml view.

The authenticateAction validates the login form, authenticates the request, and then redirects the user to the indexAction if the request is successfully authenticated. If the authentication request fails, we set the description of the form to reflect this and then re-render the login form.

The getLoginForm method simply gets the login form from the User Model. We should already be familiar with the way this works.

Authentication View Helper

There are many times when our Views need to use information about the user to decide whether to display something or not. To help with this, we will create the AuthInfo View Helper.

application/modules/storefront/views/helpers/AuthInfo.php
class Zend_View_Helper_AuthInfo extends Zend_View_Helper_Abstract
{
protected $_authService;
public function authInfo ($info = null)
{
if (null === $this->_authService) {
$this->_authService = new
Storefront_Service_Authentication();
}
if (null === $info) {
return $this;
}
if (false === $this->isLoggedIn()) {
return null;
}
return $this->_authService->getIdentity()->$info;
}
public function isLoggedIn()
{
return $this->_authService->getAuth()->hasIdentity();
}
}

The AuthInfo Helper is pretty simple and wraps the functionality of the Authentication Service for us. Remember though just like Models, our View Helpers should only have read-only access to the Service.

We can use the AuthInfo Helper in two ways:

  • To get some user information, we use:
    $this->authInfo('role');
    $this->authInfo('firstname');

    This would get the role and first name of the currently authenticated user or null if they are not logged in.

  • To check if the user is logged in or not, we use:
    $this->authInfo()->isLoggedIn();

    This will return true if they are logged in or false if they are not.

Other Authentication Service elements

With the main Authentication components created, all that is left to do is create the forms and update the Views. As we have covered this a few times already, we will not include the code listing here. Instead, copy the following updated or new files from the example files:

  • application/layouts/scripts/_topnav.phtml: updates the menu to exclude certain items to non-logged in users.
  • application/modules/storefront/forms/User/Login.php: new login form.
  • application/modules/storefront/views/scripts/customer/login.phtml: new view to display the login form.

Of course, this would be a good chance for you to try and create these elements yourself. Once these are created, we should be able to log in by registering and then using the newly created user to authenticate with.

Summary

In this article, we looked at authentication (are they who they say they are?) and used Zend_Auth to create the Authentication Service. This enabled our users to be authenticated by the Storefront and their identity stored for later use with the authorization systems.

 

If you have read this article you may be interested to view :

 

Zend Framework 1.8 Web Application Development Design, develop, and deploy feature-rich PHP web applications with this MVC framework
Published: September 2009
eBook Price: €18.99
Book Price: €30.99
See more
Select your format and quantity:

About the Author :


Keith Pope has over ten years of experience in web-related industries and has had a keen interest in programming from an early age. Keith currently works in the airline industry as a technical project manager, providing entertainment systems for aircraft.

He has been working with the Zend Framework since its first preview release, using it in many of his work and personal projects.

Books From Packt

Joomla! 1.5 SEO
Joomla! 1.5 SEO

jQuery 1.3 with PHP
jQuery 1.3 with PHP

Symfony 1.3 Web Application Development
Symfony 1.3 Web Application Development

Joomla! 1.5x Customization: Make Your Site Adapt to Your Needs
Joomla! 1.5x Customization: Make Your Site Adapt to Your Needs

PHP Team Development
PHP Team Development

Matplotlib for Python Developers
Matplotlib for Python Developers

Spring Web Flow 2 Web Development
Spring Web Flow 2 Web Development

Grok 1.0 Web Development
Grok 1.0 Web Development

 

Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software