The Zend_Auth component of the Zend Framework can really help simplify the process of developing a custom authentication system for your next web application. The basic process is already very well-documented, so let’s try something a bit more complex.
For this example, we’re going to allow our users to authenticate in one of multiple ways: e.g., against a database table, against an LDAP server, or by OpenID [1]. Zend_Auth already provides the necessary authentication adapters, so what we’ll be concerned with here is how to implement all three systems without ending up in an FSUC situation.
As I see it, the controller layer ought to have very little awareness of the underlying authentication mechanisms. Here’s what such a controller might look like:
A few things worth noting here. First…usually in this type of workflow the controller would check $this->getRequest()->isPost() prior to firing the authentication method; however, certain authentication strategies may require authentication to fire on other conditions (for instance, the OpenID adapter should fire when the openid_mode parameter is set, regardless of the request method). So, we leave it up to the adapter’s shouldAuthenticate() method to determine if the request warrants authentication.
Second, note that we still don’t have much in the manner of authentication code; that’s because, in order to keep things as flexible as possible, we’ve offloaded the actual authentication work to an arbitrary collection of strategy classes [2], provided dynamically through a Zend_Loader_PluginLoader instance (to make this worth it, we’ll also eventually want to add methods for registering new plugin paths, but I’ve left that out for the sake of brevity). Each strategy class will need to conform to the following interface:
Internally, each authentication strategy will simply configure one of the core Zend_Auth adapters, run its authentication method, and, if successful, ensure that the identity returned in the Zend_Auth_Result instance is the completely-loaded user object. Let’s take a look at the simplest of the three examples, which checks the user’s email address and password against a backend database table:
We’ll use the same principle in designing the remaining two authentication methods (LDAP, which our users will know as their “EUID” or “Enterprise User ID”, and OpenID). I’ve left getForm() and setAdapter() out of these next examples, because the basic technique won’t be much different.
And finally, a strategy class for OpenID. Note that this is a bit more difficult owing to the necessity of client redirects; the OpenID strategy needs to be aware of quite a few more details, most of which are stored in the request object injected from the controller during the shouldAuthenticate() method.
The end result of all this is an extremely flexible (and extremely extensible) user authentication system with very little business logic in the controller.
Footnotes
[Back] The idea is similar, but not identical, to the Zend_Auth_Adapter_Chain proposal from January 2008; the main difference here is that instead of authenticating against a series of several adapters, we’re simply going to have one main adapter that orchestrates the whole procedure via easily-selected strategies. Only one authentication technique will ultimately fire.
[Back] If you look at the code later on, you’ll see that my authentication “strategy” interface is just an extension of Zend_Auth_Adapter_Interface allowing each implementation to provide a few extra standard features (forms, request analysis, etc.). I suppose it would have been reasonable to simply call them adapters, but I decided on using a new (still fairly standard) name to make it clear that they’re doing more than the standard Zend_Auth adapter would do. It was also necessary to namespace these classes separately from any other Zend_Auth adapters that might be included in a given application, so that the plugin loader never accidentally loads a different implementation of Zend_Auth_Adapter_Interface.