Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

Zend Framework: Zend_Auth Component Proposal

Proposed Component Name Zend_Auth
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_Auth
Proposers Darby Felton
Revision 0.3.1 - 28 November 2006: Updated content post-approval (wiki revision: 21)

Table of Contents

1. Overview

Vote Now for an Authentication Adapter!
In order to get an idea of which concrete authentication adapters are most needed, we have a ballot ready to accept your vote on which authentication adapter you would most like to see included with the framework. Vote Now!

Zend_Auth provides an API for generalized authentication and includes concrete authentication implementations.

Zend_Auth is concerned only with authentication and not authorization. Authentication is loosely defined as determining whether an entity actually is what it purports to be, based on some criteria. Authorization, the process of deciding whether to allow an entity access to, or to perform operations upon, other entities is out of scope with respect to this proposal. It is acknowledged that both authentication and authorization are integral parts of an application that are typically used together. We address authentication and authorization with separate framework components, however, with consideration for maximum flexibility for developers to integrate the concepts in accordance with their varied requirements. For more information about authorization with the Zend Framework, please see Zend_Acl.

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • Provide an API to which arbitrary authentication systems can adhere
  • Provide common concrete authentication mechanisms
  • Encapsulate and make available data obtained during authentication with token interface
    • Identity and credentials (e.g., username, e-mail, one-time use keys)
    • Date and time
    • Other results from authentication back-end
  • Facilitate authentication persistence across requests
  • This component is only concerned with performing authentication; other operations, including management of authentication back end data stores, are out of scope.

4. Dependencies on Other Framework Components

  • Zend_Exception
  • Zend_Session (optional, on by default)

5. Theory of Operation

Definitions

For the purposes of this proposal,

  • An identity is a piece of information used to represent the identity of the requester. For example, an authentication scheme might use a username or e-mail address for the purposes of identification, or perhaps the identity is an integer that maps to a database table.
  • A credential is a piece of information used to authenticate the requester against the reported identity. For example, a password is often used as a credential in web applications. Similarly, security questions and answers (e.g., What was your first pet's name?) may be used as authentication credentials. In many cases, credentials include identifying information. For example, a fingerprint might be used alone as an authentication credential that indicates identity inherently.
  • An authentication token refers to, represents, or encapsulates the results of an authentication attempt. For example, if a user logs in with a username and password, an application might use an object to refer to this authentication attempt, and this object would be an authentication token. An authentication token need not necessarily include the authentication credentials presented by the authentication attempt. The token would very likely contain the authenticated identity, and it may contain other information that is important to the application, such as the date and time of login, the number of failed login attempts, and other information from the authentication back-end.

Operation Details

Since authentication methods vary widely depending on the specific needs of an application, Zend_Auth imposes no particular back end authentication mechanism, though it is intended that it provides a set of concrete authentication adapters for the most common use cases.

We have an abstract class, Zend_Auth_Adapter, from which concrete authentication adapter classes inherit.

Zend_Auth_Adapter defines an authenticate() method that should be overridden by child classes, but this is not enforced by PHP because it is not designated as an abstract method. This, in turn, is because abstract method signatures must match their implemented counterparts (in PHP 5.1.4), and we cannot know what the magic number of parameters would be for an authenticate() method, since actual authentication implementations are expected to vary widely. Thus, we resort to throwing an exception if the authenticate() method is called on an object inheriting from the abstract class that does not override authenticate().

The authenticate() method of authentication adapter classes is intended to return an instance of a class that implements the Zend_Authentication_Token_Interface. Again, we're not enforcing the return type, but this intent is documented in the docblock for Zend_Auth_Adapter::authenticate().

For some authentication backends and in certain circumstances (perhaps even in the most common use case), it may make sense to simply call a single static method upon the adapter class, providing authentication credentials, and returning authentication results as a token. The staticAuthenticate() method of the adapter class would be used when the credentials are known and the application is concerned only with the results of the authentication for a particular request.

Zend_Auth_Digest is included as an example concrete authentication adapter class (credit to Gavin).

6. Milestones / Tasks

  1. Finish proposal revisions for review, based on feedback and collaboration - DONE
  2. Arrive on approvable proposal through iterative review and updates - DONE
  3. Implement approved design with inline API documentation - IN PROGRESS
  4. Drive development with unit tests
  5. Write manual documentation

7. Class Index

Common

  • Zend_Auth
  • Zend_Auth_Exception
  • Zend_Auth_Adapter (abstract)
  • Zend_Auth_Adapter_Exception
  • Zend_Auth_Token_Interface (interface)

Digest authentication Adapter

  • Zend_Auth_Digest_Adapter
  • Zend_Auth_Digest_Token
  • Zend_Auth_Digest_Exception

8. Use Cases

Website Login with E-mail Address and Password

Perhaps the most common use case for web applications is password-based authentication. In order to authenticate with a password, the user purports an identity, in this case, using an e-mail address to which the user has access. The user supplies the e-mail address to establish the identity against which the system challenges the user to authenticate using a password that only the person having access to the e-mail address is supposed to know.

Fallback Authentication with Security Question and Answer

It is often the case that users forget their passwords. Some applications may allow sending an e-mail to the identity that the user claims to be, so that the password may be reset to something the user will hopefully remember in the future. If the user is not who she claims to be, then the person that truly is the purported identity would receive the mailing and would simply opt not to change the password.

Other applications, however, may need to provide an alternative means of authentication, such as a security question and answer. To illustrate, suppose we have a user who forgets her password. If she had entered in her profile a security question and answer, then the application can simply ask the question, and the user would provide the correct answer in order to authenticate successfully.

"What was the name of your first pet," "on what street were you born," and "what was your high school mascot" are all examples of security questions that may be asked by an application. Though it may be important for application security not to let authentication hinge upon questions to which answers may be guessable or discoverable by entities other than the identity holder, these decisions are for application developers to make and are outside of the scope of this proposal.

Securing Sensitive Operations

In certain situations it makes sense to require authentication credentials to be presented, regardless of the presence of an authentication token representing a previously successful authentication attempt.

For example, a user might log into a website with an e-mail address and password. The user clicks around various areas in the website, and her actions are performed with the identity which with she logged in, since an authentication token exists in her session and is recognized by the application. For extra security on sensitive operations, however, the website demands that the user enter her password again before being allowed to perform sensitive operations such as editing her profile or changing her password or e-mail address.

Arbitrary Credentials

In many instances, a simple username and password scheme is sufficiently secure for the purposes of the application. Other applications, however, may require more than a trivial set of credentials in order to consider a requester genuinely authenticated.

Consider the case of an application for credit or a loan, where the applicant must provide a series of information that is checked against existing records. There may be complicated sets of rules that must be fulfilled in order to completely authenticate a person.

An applicant would no doubt be required to provide a set of data that must be correct, such as her social security number (SSN), current address and phone number, and current bank information.

There may be other criteria, however, that provide some flexibility when the user cannot provide certain credentials required for authentication. For example, the applicant may have to answer 3 correct questions about her credit history, though some of the information may not be immediately available to the applicant. In this situation, the applicant may choose which of perhaps 10 questions to answer based on the information she does have at hand.

Credentials that Include the Identity

Authentication is often performed by first purporting an identity and then presenting credentials that are checked against the purported identity. But there are cases where separation of the identity from credentials may not be practical. Biometric and security card scanners are a couple of examples in which the credentials may inherently represent the identity of the requester, and the concepts of identity and credentials are not mutually exclusive.

9. Class Skeletons

]]></ac:plain-text-body></ac:macro>

]]></ac:plain-text-body></ac:macro>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Aug 27, 2006

    <p>I have been pondering PHP authentication recently. The problem is that there are many different authentication schemes. However they are all essentially validation. So I was thinking – what if we provided a common Authentication "manager" like both Darby and Gavin are showing, but instead of having the factors built into the Authentication class, we pushed them out into classes for each type of Authentication scheme. You really need a pair of classes for the Authentication class to do its job – first is a Credential that contains the factors, and second is an Authorizor that validates the Credential. </p>

    <p>I should add the note that for common Authentication schemes like username/password could be encapsulated into a single class to provide a simpler interface over these building block classes. </p>

    <p>So for example if you wanted to implement a database username/password scheme you would have a Username/Password Credential (Two Factor) and a Database Username/Password Authorizor. The code might look something like this:</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $credential = new Zend_Auth_Credential_UsernamePassword($request);
    $authorizor = new Zend_Auth_Authorizor_UsernamePassword($db);

    $authenticator = new Zend_Authicator($credential, $authorizor);
    $result = $authenticator->authenticate()
    ]]></ac:plain-text-body></ac:macro>
    <p>But you could also do Three Factor with something like the following where a Capcha class generates the third factor and saves/retrieves it from the session. Notice that the Zend_Authicator does not change and provides a common framework for many(any?) authentication schemes.</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $credential = new Zend_Auth_Credential_UserPassCaptcha($request, $captcha);
    $authorizor = new Zend_Auth_Authorizor_UserPassCaptcha($db, $captcha);

    $authenticator = new Zend_Authicator($credential, $authorizor);
    $result = $authenticator->authenticate()
    ]]></ac:plain-text-body></ac:macro>
    <p>I think it is interesting that Darby called his class "Authentication", Gavie "Authenticate" and I Authenticator". This shows the subtle differences in how the problem of authentication can be thought of – which is part of the problem in discussing the design of these systems. </p>

  2. Sep 13, 2006

    <p>This psuedocode prototype had a couple bugs and combined the ideas of a general-purpose authentication component with a more specialized example that cached intermediate results. I have split the code above into two classes, and added the resulting code to the proposal above under "Alternative Proposal". Note that session validation remains a separate step from user authentication. Session validation is handled by Zend_Session.</p>

    1. Nov 20, 2006

      <p>Just for reference, I've posted the old prototype example that demonstrated an extremely simple integratiion of an abstract authentication mechanism with Zend_Session.</p>

      <p>Specific adapters for authenticating using LDAP, RDBMS, htdigest, etc. would be created by subclassing Zend_Authenticate or Zend_Authenticate_Cache. The use of and support for "domain" is not needed in many situations.</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      // $identity could be anything from a username to an application specific authentication token object.

      abstract class Zend_Authenticate
      {
      protected $_config = array(
      'domain' => null, # domain to authenticate against - optional, arbitrary (e.g. namespace or LDAP/DB/etc.)
      'useCache' => false, # e.g. cache <$username, sha1($password), $domain> using Zend_Cache
      'session' => null # optional, an instance of Zend_Session
      );

      public function __construct($config = array())
      {

      1. we could store values directly into properties, but this is only prototype
        foreach(array_keys($this->_config) as $key)
        Unknown macro: { if (isset($config[$key]))
        Unknown macro: { $this->_config[$key] = $config[$key]; unset($config[$key]); }
        }

        if (is_null($this->_config['session']))

        Unknown macro: { $this->_config['session'] = new Zend_Session('Zend_Authenticate'); }


        if (count($config))

        Unknown macro: { throw new Zend_Exception('Unkown configuration option(s)}


        }

      1. Should be overloaded, with subclass first calling the parent's method below.
      2. Alternatively, this could be simply an abstract method with no implementation:
        public function exists($identity, $authenticationData) {}

      public function domainExists($domain=null) {}

      1. attempts to perform an authentication
        public function authenticate($identity, $authenticationData = null)
        Unknown macro: { if (!isset($authenticationData['domain'] === null)
        Unknown macro: { $domain = $this->_config['domain']; }

        else

        Unknown macro: { $domain = $authenticationData['domain']; }
        1. if $password is null, then other information will be used (e.g. $_SERVER['REMOTE_ADDR']

        if (($this->authenticator($identity, $authenticationData)))

        Unknown macro: { $this->authenticated($identity, $authenticationData); return true; }


        $this->logAuthenticateFailure($identity, $authenticationData);
        return false;
        }

        1. records a successful authentication
          protected function authenticated($identity, $authenticationData)
          Unknown macro: { $this->_config['session']->IP = $_SERVER['REMOTE_ADDR']; $this->_config['session']->identity = $identity; $this->_config['session']->domain = (isset($authenticationData['domain'] ? $authenticationData['domain']}

        1. adapter-specific authentication logic, invoked by public wrapper "authenticate()"
        2. implement in a subclass using DB, LDAP, htdigest, whatever anyone wants
          abstract protected function authenticator($identity, $authenticationData);

        protected function logAuthenticateFailure($identity, $authenticationData) {}

        1. delete session authentication information, thus making the session anonymous
          public function unauthenticate($identity, $domain=null) {
          if ($domain === null)
        $this->_config['session']->expire(null); # expire everything in this "namespace" }

      1. transform $identity to convention/case/format expected by backend (e.g. LDAP, MySQL)
      2. e.g. $username=strtolower(str_replace(' ', '_', trim($identity)));
        public function filter($identity)
        Unknown macro: { return $identity; }


        }

      1. Example designed to support authenticating against a remote system,
      2. with a "high" cost, and infrequent changes to authentication data,
      3. such as a wiki authenticating usernames against an external system.
        abstract class Zend_Authenticate_Cache extends Zend_Authenticate
        {
      4. If using Zend_Cache for a local copy of authenticated <user, password, domain>'s,
      5. then must call this function, if the user changes their password.
      6. Should be overloaded, with subclass first calling the parent's method below:
        public function cache($identity, $authenticationData) {
        if (!isset($authenticationData['domain'] === null)
        Unknown macro: { $domain = $this->_config['domain']; }

        else

        Unknown macro: { $domain = $authenticationData['domain']; }

        if ($this->_config['useCache'])

        Unknown macro: { #TODO}


        }

        1. Should be overloaded, with subclass first calling the parent's method below:
          public function exists($identity, $authenticationData) {
          if (!isset($authenticationData['domain'] === null)

        else

        Unknown macro: { $domain = $authenticationData['domain']; }

        if ($this->_config['useCache'])

        Unknown macro: { #look for user in our cache using Zend_Cache #if found return true }


        return false;
        }

        abstract public function domainExists($domain=null);

        public function authenticate($identity, $authenticationData) {
        if (!isset($authenticationData['domain'] === null)

        Unknown macro: { $domain = $this->_config['domain']; }

        else

      7. if $password is null, then other information will be used (e.g. $_SERVER['REMOTE_ADDR']

      if ($this->useCache && $this->authenticateWithCache($identity, $authenticationData))

      Unknown macro: { $this->authenticated($identity, $authenticationData); return true; }


      else if (($this->authenticator($identity, $authenticationData))) {
      $this->authenticated($identity, $authenticationData);
      if ($this->useCache)

      Unknown macro: { $this->cache($identity, $authenticationData); }


      return true;
      }
      $this->logAuthenticateFailure($identity, $authenticationData);
      return false;
      }

      protected function authenticateWithCache($identity, $authenticationData)

      Unknown macro: { # TODO}

      }
      ]]></ac:plain-text-body></ac:macro>

  3. Sep 14, 2006

    <p>Just to add to the requirements...<br />
    It should be possible to attach a key for password-encryption/-verification.</p>

    <p>Example:<br />
    We store username and password in database. In database the password is encrypted with sha1<br />
    In client/browser we encrypt the entered password once with sha1 and additionally with the session_id.</p>

    <p>Checking if the user has permission would only give true when the users password is equal to the db-pwd sha'ed with the sessionid.</p>

    <p>So it should be possible to attach a encryption key or method to both sides of the verification.</p>

    1. Sep 14, 2006

      <p>The use case provides an example authentication class by the application developer called <code>MyAuth</code>. This class contains a <code>login()</code> method, which arbitrarily is specified as requiring a username and password.</p>

      <p>In order to support your example, one need only modify the <code>login()</code> method implementation to use encryption verification against the session_id and the database backend as you suggest.</p>

      <p>Since Zend_Authentication (I cannot speak for Gavin's alternative approach) supports <a href="http://www.php.net/manual/en/language.pseudo-types.php">PHP callbacks</a> in general, you could implement various authentication approaches with a regular function (e.g., <code>function foo()</code>), a method of an object instance (e.g., <code>$obj->foo()</code>), or a static method of a class (e.g., <code>Class::foo()</code>).</p>

      1. Sep 14, 2006

        <p>Perhaps better examples:</p>

        <ul>
        <li><code>function authenticate($username, $password)</code></li>
        <li><code>$authenticator->login($username, $password)</code></li>
        <li><code>Authentication::login($username, $password)</code></li>
        </ul>

  4. Sep 18, 2006

    <p>I think both of these proposals are a bit too simplistic, in that they maybe require too much of the developer. The whole point of a framework being to support the developer in simplifying tasks. I think an interface structure would be more useful (notice that I helpfully assume a Zend_Ldap class <ac:emoticon ac:name="smile" />):</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    interface Zend_Authentication_Interface
    {
    public function authenticate();
    }

    class Zend_Authentication_Ldap implements Zend_Authentication_Interface
    {
    public function __construct(Zend_Ldap $ldap) {}
    public function setDomain($domain) {}
    // etc...
    public function authenticate($username, $password) {}
    }

    class Zend_Authentication_Db implements Zend_Authentication_Interface
    {
    public function __construct(Zend_Db $db) {}
    // etc...
    public function authenticate($username, $password) {}
    }

    class Zend_Authentication_Apache implements Zend_Authentication_Interface {}
    ]]></ac:plain-text-body></ac:macro>

    <p>You could then load an authentication module like this:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    public function login($username, $password)
    {
    $ldap = Zend::registry('ldap');
    $auth = new Zend_Authentication('ldap', $ldap);
    $auth->setDomain('...');
    // etc... (set various options)
    if ($auth->authenticate($username, $password))

    Unknown macro: { return true; }

    return false;
    }
    ]]></ac:plain-text-body></ac:macro>

    <p>Any unusual/custom authentication methods could then be implemented through the interface.</p>

    1. Oct 13, 2006

      <p>Implemented methods must match the interface method declaration in PHP, and though I understand the semantics of your example, the above code would result in a fatal error.</p>

  5. Sep 21, 2006

    <p>Authentication is any process by which you verify that someone is who they claim they are. This usually involves a username and a password, but can include any other method of demonstrating identity.</p>

    <p>Access control hower, is a much more general way of talking about controlling access to a web resource (very abstract). Access can be granted or denied based on a <strong>very wide</strong> variety of criteria, such as the network address of the client, username, password, the time of day, the phase of the moon, or the browser which the visitor is using.</p>

    <p>These techniques are so closely related in most real applications, it is difficult to talk about them separate from one another. However I think that it is a pretty big design decision that has to be made. Do we make a difference between these two, or do we <strong>wrap</strong> them in one class?</p>

    <p>I would like to ask for brainstorm time, where we can openly consider all ideas to help synthesize a final proposal, before further decisions are made. </p>

    1. Sep 21, 2006

      <p>Thank you for your astute observations, which I believe succinctly describe the problem model quite well.</p>

      <p>Though Zend_Acl, which provides an authorization mechanism or access controls, has been approved for incubator development for good reasons, I also suspect that more research is needed toward defining the framework approach to authentication and authorization. As indicated in this proposal, the initially suggested approach is little more than a wrapper to whatever authentication mechanisms developers wish to employ (or may already be employing).</p>

      <p>I look forward to hearing more about what you and others might suggest for a good solution to this pervasive problem.</p>

      1. Sep 21, 2006

        <p>I agree with Andries that they are closely related, but I feel that they are still separate problems to be solved. IMO you are asking who (Auth) before what/where (Acl) and the scope of both of these will vary dramatically depending on the application. To allow greater freedom I would still encourage that these processes remain as separate components, but be addressed more clearly in documentation, tutorials and examples to complete the picture for the community.</p>

        <p>One of the prime motivators for me to tackle the Acl component in the first place was that I could find no 'simple' implementation of an Acl manager that didn't also require me to install an overly-elaborate SQL storage container and a custom-written ACL admin screen by which to administer it. This didn't suit me - it wasn't plug'n'play and it limited my ability to customise the administration without breaking future support. For these reasons, too, I could see that if Auth and Acl were too closely related they may introduce dependencies that could otherwise be avoided.</p>

        <p>There's a lot of scope for Zend_Acl to be improved and for how it is implemented. You may end up having 5 completely unrelated Acl's - one for access management to a site, one for authoring permissions, one for specific forum permissions, etc... It would not be practical, however, to have 5 authentication instances.</p>

        <p>I can see areas of similarity - I.P. access for one. I don't believe Zend_Acl is well equipped to deal with this at the moment, and I have had a brief discussion with a community member who believes extra callbacks can be added to Zend_Acl to handle 'plugin'<span style="text-decoration: line-through;">like behaviour, providing further checks like:</span></p>

        <ul>
        <li>IP/Hostname access to the whole Acl or only defined areas</li>
        <li>Time of day (as you mention above)</li>
        <li>Status of site services</li>
        <li>Mny others, I'm sure!</li>
        </ul>

        <p>I, too, look forward to hearing how others look to solve their own access requirements using Auth/Acl to see where we can make improvements on the current implementation.</p>

        1. Oct 13, 2006

          <p>Indeed, though authentication and authorization are related, we have opted to strive for as little coupling as possible between authentication and authorization framework components to allow for greatest flexibility.</p>

          <p>As we finalize an authentication component design (Zend_Acl is already usable), we can show sample ways in which the two may be used together.</p>

    2. Sep 22, 2006

      <p>I think we also need to look at Access Control and Authentication from the practical point of view of where the code are used from within the framework. Access Control authorization code is often called on every request (or group of requests), so performance is important. Access Control needs to efficiently check to see if access is allowed. </p>

      <p>If access is denied then there needs to be some scheme to then call some sort of Authentication code. This could be as simple as a redirect to a login page. Or it could be a cookie based Remember Me scheme. Or it could be as complex as defining points of priviledge escalation as requiring re-authentication under various criteria. </p>

      <p>The Authentication system also needs to be able to deal with multiple factors that can be related or not, and that have different data sourcse. An example is login with captcha where the username/password datasource is a database, LDAP, etc., but the capcha is generated on the fly and the datasource is the session. </p>

      <p>Finally there is often an Audit capablity to these system. Again, this can be as simple as timestamping the last login date, to complex tracking of what the user did.</p>

      1. Sep 23, 2006

        <p>I think Zend_Acl and Zend_Authentication should be separate classes. They are related, but they each address different areas. One could use Acl without Authentication (IP-based access control, for example).</p>

        <p>As for efficient access control, I think Zend_Acl should build on Zend_Bitfield when it's completed, and that's why I think Zend_Bitfield should have a non-static interface. That way, one could store groups of Bitfield objects in an array in Acl. That would be a very flexible, fast access control model.</p>

        <p>CAPTCHAs should be handled separately. They could extend Zend_Authentication_Db (referring to my idea above), redefine the constructor, and add a CAPTCHA validation method--a total of two new methods. Alternately, it might be easier to just perform the extra check in the login action, prior to calling authenticate(). I do think there's value in a Zend_Captcha class on its own (dependent on Zend_Session), but it would have to have an extensible interface design and not be limited to strictly visual CAPTCHAs.</p>

        <p>If a table update for last login is necessary, it should also be handled separately. Time spent generalizing code like that would, I feel, be better spent elsewhere, since someone might not just want to update login times but also this other table, that other column, etc., and they could probably do it more quickly with their own specific query than figuring out the class's interface. In a similar vein, I think user tracking should be handled through a custom controller plugin (in the current MVC model). That's what I do in one of my production ZF applications.</p>

        <p>In general, I think it would be best to stay away from adding in all kinds of periphereal functionality to the class--it will result in an unfocused class with a difficult-to-use API that tries to be all things to all people, in contrast to the clarity of Zend's 80/20 guideline for the framework.</p>

        1. Sep 24, 2006

          <p>This discussion probably belongs in the Zend_Acl proposal, but why do you suggest that Zend_Acl is built on top of Zend_Bitfield? Isn't the fact that a Bitfield by definition can only support a limited amount of ACOs?</p>

          1. Sep 25, 2006

            <p>Each instance of Zend_Bitfield (stored in an internal Zend_Acl array) would be a user type, defined using methods: administrator, moderator, etc. Each would have a list of access rights, stored as bits. The bits would be defined in Zend_Acl and then added to each user type so that the bits are universal. You would check access requirement(s) against user type(s) to determine access.</p>

            <p>So, you're right, it would be limited to 64 types of access, but I think that would be more than sufficient to satisfy the 80/20 guideline. It would also be very simple to understand.</p>

        2. Sep 24, 2006

          <p>I think bitfield is just a type of system like ACL is a system. We could also have an ultra-simple Level based system that just checks to see if a prescribed value is greater than or equal to a given access level. That would be helpful for many simple sites and very fast. You can also store delimited strings which work the same as bitfields but don't necessarily have size limits and are self documenting rather than requiring a lookup table for the bit values. There are many options. I think the important thing is that they all sit upon the same base. That's why I proposed a looser Authentication and Access Control managers above. </p>

  6. Oct 11, 2006

    <p>I have also given this problem some thought and in thinking thought it best to further describe the actual problem model here in one post (rehashing other comments), then perhaps some pseudo code in another tomorrow.</p>

    <p>The problem is one of <strong>Application Security</strong>, most specifically <strong>Access Control</strong></p>

    <p>This can further be broken down into 3 logically related yet distinct sub parts: <strong>Authentication</strong>, <strong>Authorization</strong>, and <strong>Audit</strong>.</p>

    <p>1. <strong>Authentication</strong> is simply a process by which when given an Identity (in any number of forms) and a set (0 or more) of Credentials a system can determine that someone is who they claim they are. A typical example of an Authentication Scheme is a username and password stored in a database, but can include any other methods of demonstrating identity (fingerprints, smart card) or other methods of supplying credentials (fingerprint, rolling code, spoken password, etc.)</p>

    <p><span style="text-decoration: underline;">In terms of ZF</span></p>
    <ul>
    <li>No state information is saved across requests, authentication is simply a process that answers a question "Is this person authentic?"</li>
    <li>Hooks should be implemented to allow the developer to use either a stock authentication scheme, or write their own scheme.. These schemes should conform to an interface defined by Zend_Authenticate</li>
    </ul>

    <p>2. <strong>Authorization</strong> is the process of finding out if a person, once identified, is permitted to have a given resource. Authorization, like authentication, can have a wide variety of implementations. Some might choose to deploy an authentication scheme based around an Access Control List, while others might decide to deploy a scheme based around capability (perhaps by principal of least authority).</p>

    <p><span style="text-decoration: underline;">In terms of ZF</span></p>
    <ul>
    <li>Zend_Authorization is stateful.. Which means it has a dependence on Zend_Session so that across page requests, previous authorization information can be retained.</li>
    <li>Currently, Zend_ACL has been approved and should be underway.. there should be some integration with Zend_Authorization and Zend_Session here.</li>
    </ul>

    <p>3. <strong>Audit trail</strong> is a chronological sequence of audit records, each of which contains evidence directly pertaining to and resulting from the execution of a business process or system function. Typically a log would include the identity (someone who was authenticated) attempting executing a specific task which they were (or were'nt) authorized to do.</p>

    <p><span style="text-decoration: underline;">In Terms of ZF</span></p>
    <ul>
    <li>ACL and/or Capability auth schemes should be implemented in such a way that if a developer wants to log (perhaps a dependence on Zend_Log) a specific threshold of ACL/Capability checks, the system can do such.</li>
    <li>More on this later.</li>
    </ul>

    <p>I propose one of the following namespaces to contain this barrage of classes:</p>

    <p>Zend_AccessControl_Authentication<br />
    Zend_AccessControl_Authorization<br />
    Zend_AccessControl_Authorization_ACL<br />
    Zend_AccessControl_Audit or Zend_AccessControl_Authorization_Audit</p>

    <p>or by removing the AccessControl umbrella:</p>

    <p>Zend_Authentication<br />
    Zend_Authorization<br />
    Zend_Authorization_ACL<br />
    Zend_Audit or Zend_Authorization_Audit</p>

    <p>all these ideas float around the net in one place or another, a good staring place is wikipedia, followed by some apache docs. </p>

  7. Oct 11, 2006

    <p>Pseudo Code supporting interface idea</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    – ./controllers/loginController.php

    try

    Unknown macro: { Zend}

    catch (Zend_Authentication_Exception $e)
    {
    echo $e->getMessage();
    }

    – ./lib/RS/Authenticator.php - users custom authentication routine

    class RS_Authenticator extends(implmements) Zend_Authentication_AuthenticatorInterface
    {
    // user defined
    public function processAuthentication()
    {
    $this->_auth_instance->addRequiredCriteria('username');
    $this->_auth_instance->addRequiredCriteria('password');

    try

    Unknown macro: { $user = new User(array('username' => $this->getIdentity())); }

    catch (Model_Exception $e)

    Unknown macro: { // user doesnt exist if the model returned an exception throw new Zend_Authentication_Exception("Username / Password incorrect"); }

    $this->_auth_instance->setValidCredential('username', $username);
    $this->_auth_instance->setValidCredential('password', $user->getPassword());
    }
    }

    – ./Zend/Authentication.php

    class Zend_Authentication
    {
    protected $_authenticator = null;

    public function __construct($auth_class)

    Unknown macro: { $this->_authenticator = new $auth_class; if ($this->_authenticator instanceof Zend_Authentication_AuthenticatorInterface) throw new Zend_Authentication_Exception( ... ); }

    public function setIdentity(mixed $thing)

    Unknown macro: { ... }

    public function getIdentity()

    public function setCredential($name, $value)

    Unknown macro: { ... }

    public function getCredential($name)

    public function setValidCredential($name, $value)

    Unknown macro: { ... }

    public function authenticate()

    Unknown macro: { $this->_authenticator->processAuthentication(); /** * At this point we check to make sure all required credentials * have been passed, and that their value matches what the user * has provided as a "valid" credential. * */ }

    }

    – ./Zend/Authenitcation/AuthenticatorInterface.php

    class Zend_Authentication_AuthenticatorInterface
    {
    public $_authentication_instance = null

    public function __construct($zend_auth_instance)

    Unknown macro: { $this->_auth_instance = $zend_auth_instance; }

    }
    ]]></ac:plain-text-body></ac:macro>

    1. Oct 13, 2006

      <p>In this example we have:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      $auth = new Zend_Authentication('RS_Authenticator');
      ]]></ac:plain-text-body></ac:macro>

      <p>By examination I see that this would create a new instance of Zend_Authentication, passing <code>'RS_Authenticator'</code> to <code>Zend_Authentication::__construct()</code>. Indeed, the constructor accepts the string and creates an instance of RS_Authenticator:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      // in Zend_Authentication::__construct()
      $this->_authenticator = new $auth_class;
      ]]></ac:plain-text-body></ac:macro>

      <p>Note that this instantiation does not pass any parameters to the RS_Authenticator constructor. But because RS_Authenticator extends Zend_Authentication_AuthenticatorInterface, which is not actually an interface but a class, and <code>Zend_Authentication_AuthenticatorInterface::__construct()</code> has a required parameter, we result in fatal error.</p>

      <p>What purposes is Zend_Authentication_AuthenticatorInterface supposed to serve? I thought Zend_Authentication was long enough to type! <ac:emoticon ac:name="wink" /></p>

      <p>I think we would be well served by additional illustration of:</p>

      <ul>
      <li>what <code>setIdentity()</code> would do</li>
      <li>how <code>setCredential()</code> and <code>setValidCredential()</code> would work, what their purposes are</li>
      <li>how identities and credentials are validated against an authentication backend or data store</li>
      </ul>

      <p><code>Zend_Authentication::authenticate()</code> calls <code>$this->_authenticator->processAuthentication()</code>, thereby imposing that classes passed to this constructor implement this method. This is where the idea of the interface makes some sense.</p>

      <p>These things said, I like this approach in that it separates the authentication identity from the authentication credentials. I know you intended this to be pseudocode and probably put it together rather quickly. I would be interested to see another version of this with additional code commenting and a working example use (e.g., against an authentication backend, even if just a PHP array) attached to this proposal (rather than adding much more code to the already long list of comments).</p>

      1. Oct 16, 2006

        <p>Darby,<br />
        Yes, that would produce a fatal error, .. really there would be no constructor in the abstract.. the only coded out method would be registerAuthenitication(), this would give the Authenticator access to the identity and credentials that were passed to Zend_Authenticate.</p>

        <p>I have a working class right now, let me get this to you for review so you can see how this all would play out.</p>

        <p>Notes: </p>

        <p>Zend_Authentication should take either a string of the class name or an instance of the class that extended Zend_Authentication_Authenticator. This allows for maximum flexiblity</p>

        <p>Also, its worth noting that concrete stock authenticators should be included with Zend_Authenticate.. for example authenticate off passwd format, based off htpasswd file, or even based off a php array.</p>

        <p>more later,<br />
        Ralph</p>

  8. Nov 16, 2006

    <p>Some notes:</p>

    <p>Auth ctor could take full adapter object, then we can use code completion when writing 'new My_Auth_Adapter(params)'. If we use factory, code completion won't work.</p>

    <p>I think the should be no distinction between identity and credentials when calling authentication adapter, and I think we can just pass opaque array there, and let the user and the adapter know what exactly means what there. Credentials may even not include identity, but identity would be derived from it - e.g. you can login with multiple credentials (e.g. imagine site where you can login with username, email or internal ID or phone number or SSN) but you would always have same ID - which can be some internal database key, for instance.</p>

    <p>I don't think we need an interface for authenticate() parameter, only array.</p>

    <p>Zend_Auth_Token_Interface - we need to provide example tokens. I propose:</p>

    <ul>
    <li>Simple username token - i.e. something storing just username</li>
    </ul>

    <ul>
    <li>Expiring username token (one can set expiration on ctor - adapter could do it) which maybe also keeps additional info</li>
    </ul>

    <p>For authenticator main class, I see we need following public functions:</p>
    <ul>
    <li>ctor of course</li>
    <li>check if we already have valid auth token</li>
    <li>check auth data and create token (using concrete token & backend classes)</li>
    <li>erase auth state (logout)</li>
    <li>set session parameters (or replace session handler with another class?)</li>
    </ul>

    <p>Static auth method looks like a good idea to me. </p>

  9. Nov 20, 2006

    <ac:macro ac:name="note"><ac:parameter ac:name="title">Zend Comments</ac:parameter><ac:rich-text-body>
    <p>This proposal has been reviewed by the Zend Framework Core Team, and approved for the Incubator.</p>

    <p>We would like to make the following comments and suggestions:</p>

    <ul>
    <li>Provide example classes that implements the token interface
    <ul>
    <li>Simple username token - i.e. something storing just username</li>
    <li>Expiring username token (one can set expiration on ctor - adapter<br />
    could do it) which maybe also keeps additional info</li>
    </ul>
    </li>
    <li><code>Zend_Auth::__construct()</code> accepts concrete adapter object</li>
    <li><code>Zend_Auth</code> provides access to adapter object as needed</li>
    <li><code>Zend_Auth</code> provides session persistence capabilities (on by default)</li>
    <li>Authentication against adapters is in scope, but robust management of<br />
    authentication adapter back-ends is out of scope with respect to<br />
    <code>Zend_Auth</code></li>
    </ul>
    </ac:rich-text-body></ac:macro>

  10. Nov 21, 2006

    <p>Hello,</p>

    <p>I have taken a shot at implementing Zend_Auth starting from the class skeletons in the proposal However, on the way, I have done some things differently.</p>

    <p>I believe that Zend_Auth_Token should act as a container for Zend_Auth_Identity and Zend_Auth_Credentials. The token is valid if the Identity claimed matches the presented credentials.</p>

    <p>This is the flow of the auth process as I see it:</p>

    <p>1. Initialisation<br />
    a. A Zend_Auth object is instantiated for the authentication method required (LDAP, DB Table, Unix .passwd file). A factory is used, with the params for the specified auth method passwd as array</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    require_once 'Zend/Auth.php'; class UsersTable extends Zend_Db_Table { protected function _setup()

    Unknown macro: { $this->_name = 'users'; $this->_primary = 'user_id'; parent}

    } $users_table = new UsersTable(); $dbtable_params = array( 'dbtable' => $users_table, 'username_column' => 'user_email', 'password_column' => 'user_pass', 'hash_function' => 'md5', ); $dbtable_auth = Zend_Auth::factory( 'dbtable', $dbtable_params ); Zend::register('auth', $dbtable_auth);
    ]]></ac:plain-text-body></ac:macro>
    <p>B. Authentication (presenting Identity and/or Credentials)<br />
    a. Retrieve identity and/or credentials from user (html login form, biometric reader API, etc.)<br />
    b. Create a token that contains them<br />
    c. Authenticate the token, using Zend_Auth::authenticate( ). If the token is valid, mark it as such</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    function VerifyLogin( $user_email, $user_pass )

    Unknown macro: { $sess= Zend}

    ]]></ac:plain-text-body></ac:macro>
    <p>C. Page/resource request<br />
    a. A Zend_Token is retrieved from storage (maybe Zend_Session), if one is present<br />
    b. Try to validate the token we have. This may or may not use the underlying validation system (i.e. use caching; we don't want to access LDAP every page refresh, but only once, at login)<br />
    c. If the token is valid, check the enclosed identity against the Zend_Acl to determine whether the user has access to the requested page/resource</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $auth = Zend::registry('auth'); $sess = Zend::registry('sess'); if (!STATIC_AUTH) $sess->token = $auth->authenticate($sess->token); if ($token->isValid())

    Unknown macro: { //do smth }

    ]]></ac:plain-text-body></ac:macro>

  11. Nov 24, 2006

    <p>On the basis an implementation might of some limited use... I needed to add a simple authentication system to an application. Since I was using the ZF, I grabbed the class skeletons from the proposal and implemented one. The namespace differs, but any Exception/Abstract classes or Interfaces are exactly as noted in the proposal body.</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    class Astrum_Auth_Adapter_Simple extends Zend_Auth_Adapter
    {

    public function authenticate(Quantum_Db_Row $credentials, Quantum_Db_Row $user, Zend_Session $session)
    {
    $user->name = $credentials->name;
    $result = $user->getRow();

    if($result === false)

    Unknown macro: { $token = new Astrum_Auth_Token_Simple; $token->setIdentity( array('user'=>$user, 'valid'=>0, 'message'=>'Username does not exist') ); return $token; }

    if($credentials->password !== $user->password)

    Unknown macro: { $token = new Astrum_Auth_Token_Simple; $token->setIdentity( array('user'=>$user, 'valid'=>0, 'message'=>'Password for this Username incorrect') ); return $token; }

    /*

    • User details and password have been verified.
      */
      $session->authenticated = 1;

    /*

    • Return token for validation
      */
      $token = new Astrum_Auth_Token_Simple;
      $token->setIdentity(
      array('user'=>$user, 'valid'=>1, 'message'=>'')
      );
      return $token;

    }
    }

    class Astrum_Auth_Token_Simple implements Zend_Auth_Token_Interface
    {
    protected $identity = array();

    public function isValid()
    {
    if(!isset($this->identity['valid']) || empty($this->identity['valid']))

    Unknown macro: { return false; }

    return true;
    }

    public function getMessage()

    Unknown macro: { return $this->identity['message']; }

    public function getIdentity()

    Unknown macro: { return $this->identity['user']; }

    public function setIdentity($identity)

    Unknown macro: { $this->identity = $identity; }

    public function __toString()

    }
    ]]></ac:plain-text-body></ac:macro>

    <p>Usage was a simple matter:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    // LoginController

    $credentials = new Astrum_User;
    $credentials->name = $this->_post->getRaw('astrum_form_login_user');
    $credentials->password = sha1($this->_post->getRaw('astrum_form_login_pass'));
    $authenticator = new Astrum_Auth_Adapter_Simple;
    $token = $authenticator->authenticate($credentials, new Astrum_User, $this->_session);

    if(!$token->isValid())
    {
    $this->getResponse()->appendBody(
    '<strong>' . $token->getMessage() . '</strong>'
    );
    $this->_forward('index', 'index');
    return;
    }

    $user = $token->getIdentity(); //we have an authenticated user
    ]]></ac:plain-text-body></ac:macro>

    <p>A few of the stickly points is passing in the Session object as an extra parameter - would be nice if this could be set elsewhere, or accessed from within the object. I completely agree with the design implementation-unaware and leaving scope for varying parameters.</p>

    <p>Look forward to the incubator version...</p>

  12. Nov 27, 2006

    <p><em>Updated attachment link for Sorin, original post by Sorin Alin Stoiana at Nov 24, 2006 10:05:</em></p>

    <p>In reply to a mail from Stanislav Malyshev on the Zend_Auth mailing list about the classes I submitted (see attachment <ac:link><ri:attachment ri:filename="Sorin_Alin_Stoiana.zip" /></ac:link>):</p>

    <ul>
    <li>I do not see why we need identity class. We can't possibly know what each app considers to be identity and we don't provide any services with this class.</li>
    <li>Same for credentials class - I don't see which services we can provide, since we don't know anything about what credentials application could use. Thus, I propose to have both as values opaque for base classes and known only to implementations.</li>
    </ul>

    <p>The Identity and Credentials classes are simple containers for data. The token acts as a container for the two. You will see that the way i planned the Dbtable adapter for example, that the Auth class is configurable to read whatever data is necesary from the table, so the app writer doesn't need to modifiy the Dbtable class in any way for a simple "login from db" scenario. On the other hand, if the app needs to load more complex data about the Identity used to login (say, for example, First Name, Last Name, Website, an array of prefferences data, etc), the Identity object is the ideal place to do so. Same for the credentials.</p>

    <p>Another possible scenario is one where part of the user base is located in a db table another part in a ldap directory. Authentication then needs to be done against both Dbtable and Ldap adapters. In such a case, both adapters would be configured to use the 'user_email" detail from the Identity object and the 'user_password' from the Credentials object.</p>

    <p>The Token object could be stored in Zend_Session, and Zend_Token::isValid could be used to quickly check if the token is valid, without going through the auth adapter layer to do so (think about checking hundreds or even thousands of tokens per second against Ldap).</p>

    <ul>
    <li>This implementation proposes to pass token to the concrete auth class.<br />
    I think it is not good since concrete token class might be implementation dependent and creating it by authenticator client would mean that it should know details about currently used authenticator adapter and concrete token it needs to use. My opinion that it would be unnecessary abstraction breach.</li>
    </ul>

    <p>The Token object is, as i've said, a container for the Identity and Credentials, but also states wether the combination is valid (authentic). It's a general class. The only place where configuration takes place in my proposal is when the auth adapter is instantiated.</p>

    <p>Changing the auth adapter for a whole app would then imply simply changing the adapter and it's parameters. Those parameters state what details the adapter uses from the identity and the credentials, and how it interfaces with the back end (db tablename hash functions, passwd file name, ldap directory, etc). This is why I opted for this implementation.</p>

    <p>I am open to suggestions about this.</p>

    1. Nov 28, 2006

      <p><strong><em>The Identity and Credentials classes are simple containers for data. The token acts as a container for the two.</em></strong></p>

      <p>I think this is not right - token does not have to contain credentials, actually it may be even a security risk to keep credentials around for time much longer than we need them for verification of the identity. The token to the client is a sign that the identity X is verified by the system and currently logged in - or alternatively (for invalid token) that login did not work for so and so reason. No credentials are needed for that.</p>

      <p>Also, token does not have to be simple container - it may contain function. For example, expiring tokens. </p>

      <p>Now about the classes. There is a point in creating base classes for that when we can define some structure (interface) or supply some base function (base class) or both (abstract base class). However we know nothing of the nature of the concrete classes, thus base classes would necessarily be empty - so why we need them at all? And - what if I <strong>do not</strong> want my identity to be an object (majority of apps would use DB key as identity which would be either string or a number)?</p>

      <p><strong><em> The only place where configuration takes place in my proposal is when the auth adapter is instantiated.</em></strong></p>

      <p>I must be missing something - your implementation requires authenticate() to receive token. Who is to create the concrete token for passing to the authenticate() in that setup?  </p>

  13. Nov 28, 2006

    <p>I refer to the following section:</p>
    <ul>
    <li>Zend_Auth_Digest_Adapter</li>
    <li>Zend_Auth_Digest_Token</li>
    <li>Zend_Auth_Digest_Exception<br />
    I would say the namespace is incorrect. It should be Zend_Auth_Adapter_Digest, Zend_Auth_Adapter_Digest_Token and Zend_Auth_Adapter_Digest_Exception to be compliant with Zend_Log_Adapter etc. pp.</li>
    </ul>

    1. Nov 28, 2006

      <p>I agree that it may be inconsistent with other components, but I think it's nice to bundle the classes for a given authentication adapter in their own respective directories. What would we gain other than consistency by doing as you suggest? I see it as a disadvantage to have files for a single adapter scattered throughout sibling directories.</p>

    2. Nov 28, 2006

      <p>On second thought, I think you're right - the specific way you suggest is better than I had originally interpreted it to mean. But it does add some typing...</p>

      1. Nov 30, 2006

        <p>It may add a bit of additional typing, but with all those IDEs with autocompletion make the issue irrelevant. On the other hand consistency is just that, consistency - you don't have to think what the class's full name is, you just intuitively know that by remembering the common naming scheme.</p>

        <p>I'm saying hi as I'm new to this community and am actually interested in the ATN and ATZ components for ZF so if there's any additional help needed, I'd love to be able provide it. <ac:emoticon ac:name="smile" /></p>

  14. Jan 23, 2007

    <h3>Proposed Design Improvements</h3>

    <p>Below is a list I have compiled based on my conversations with <ac:link><ri:user ri:username="ralph" /></ac:link> and the proof-of-concept code he has provided. Many details have been omitted for brevity, and this summary highlights some potentially important design decisions that we can consider for Zend_Auth.</p>

    <p>Access SVN versions of Ralph's:</p>

    <ul>
    <li><a href="http://svn.ralphschindler.com/repo/ZendFramework/library/Zend/">Modified Zend_Auth source</a></li>
    <li><a href="http://svn.ralphschindler.com/repo/ZendFramework/docs/usage/ZendAuth/">Usage examples</a></li>
    </ul>

    <h4>Implement Singleton Pattern</h4>

    <p>If we restrict Zend_Auth to providing for authenticating HTTP requests to the application, then we need only one instance of the authentication object per PHP execution space. It is best for the component design that Zend_Auth focus on supporting the generalized use case of authenticating the HTTP request against the web application. All things considered, Zend_Auth is a good candidate for implementing the Singleton pattern, since only one instance is needed per request. Of course, the one instance could bind to any of various authentication adapters included with Zend_Auth or to a custom adapter written by the user.</p>

    <h4>Storage Integration and Zend_Session</h4>

    <p>In the current Zend_Auth design, authentication tokens are stored in the PHP session by default using Zend_Session. If this behavior is not desired (e.g., a user wishes to store the authentication token elsewhere or using a different approach than Zend_Session), then the user must disable session storage of authentication tokens explicitly by providing <code>false</code> as the value for the <code>useSession</code> option.</p>

    <p>Authentication token storage is not otherwise addressed by the current design. If we have a storage interface for the authentication tokens, then various storage mechanisms could be developed by users, and some might even make their way into the framework. Zend_Auth could then be storage agnostic. This is a bit of an implementation detail and would not need to be known by many users, but those who would store authentication tokens other than via Zend_Session would certainly benefit from abstracting the storage mechanism.</p>

    <h4>Identity Revisited</h4>

    <p>In the current implementation of Zend_Auth, the concept of identity is specific to the adapter type. The identity is returned by <code>Zend_Auth_Token_Interface::getIdentity()</code>, and the types of data returned are expected to vary, depending on the authentication adapter used. For example, <code>Zend_Auth_Digest_Token</code> returns an array comprised of both the realm and the username used during authentication.</p>

    <p>Of course, in the context of an application, the identity may be expressed differently. For example, assume that <code>Zend_Auth_Digest_Adapter</code> is used to authenticate the user. Further assume that a database table will be used to hold extended profile information for each user. The table has a foreign key that is matched to the realm and username of an authenticated request, and its primary key is a positive integer incremented for each record in the table. In the context of the authentication adapter, the username and realm comprise the identity. In the context of the application, however, the primary key of the user profile information table might be used for identifying the user, where a value of zero could represent an unauthenticated user. It is currently the responsibility of the application developer to implement identities in the application context that differ from what the authentication adapter already provides as identity information.</p>

    <p>The concept of identity, however, may be represented by a class. The proposed <code>Zend_Auth_Identity</code> class seems to provide little apparent value, though I am likely overlooking some possible reasons for providing this in the framework.</p>

    <h4>Credentials Revisited and Arbitrary Authentication Conditions</h4>

    <p>In general, users may require the ability to authenticate under arbitrary conditions. For example, assume that a user is only allowed to authenticate between the hours of 8:00am and 5:00pm. An authentication attempt must be denied if it were to occur at 1:23am. As another example, it is known that a particular user is only allowed to login from a particular workstation having a static IP address. An authentication attempt must be denied if it occurs from a different IP address. Zend_Auth does not currently support such arbitrary conditions to be plugged into its authentication mechanism. By providing a proper API for credentials, users should be able to supply such common business rules to the authenticator in a simple and concise manner.</p>

    <h4>Change <code>isLoggedIn()</code> to <code>hasIdentity()</code></h4>

    <p>In keeping with the idea that Zend_Auth is used to authenticate, or identify, the requester, the name of the <code>isLoggedIn()</code> method is suggested to change to <code>hasIdentity()</code>, which may be more clear. If a requester has not been identified through authentication, then <code>hasIdentity()</code> returns false. Only if a requester has successfully authenticated would <code>hasIdentity()</code> return true.</p>

    <h4>Authentication Failure Messages</h4>

    <p>Because a single authentication attempt may possibly result in multiple reasons why the attempt failed, the authentication attempt should make available these messages. In the context of the current <code>Zend_Auth_Token_Interface</code>, the <code>getMessage()</code> method could be changed to <code>getMessages()</code>, where the method returns an array of string messages that explain why the authentication failed. For example, an authentication attempt may result in an array of messages such as "<code>user may only log in between 8:00am and 5:00pm</code>" and "<code>user may not log in from 10.0.0.1</code>". This change would support receiving failure messages from validator chains, as from <code>Zend_Validate</code>, currently in the incubator.</p>

    <h4>Integration with Controllers</h4>

    <p><ac:link><ri:user ri:username="gearhead" /></ac:link> is reportedly working on an adapter that would implement <a href="http://www.faqs.org/rfcs/rfc2617.html">RFC 2617 - HTTP Authentication: Basic and Digest Access Authentication</a>. In order to provide for complete support for these authentication protocols, the adapter must be capable of generating and processing the related HTTP headers.</p>

    <p>If an authentication adapter is generating and processing HTTP headers, then we must consider the consequent implications. For example, HTTP headers cannot be generated from PHP if output has already started unless output buffering is enabled.</p>

    <p>Because the use of HTTP headers may be related to framework Request and Response objects, we should continue to think about and discuss how we might achieve integration with the controllers to provide a smooth experience for framework consumers.</p>

    1. Jan 23, 2007

      <p>isAuthenticated() would probably be more clear than hasIdentity().</p>

      <p>About credentials, I'm unclear how restricting to certain time periods or IPs (or whatever) would be handled in the API.</p>

      1. Jan 23, 2007

        <p>Matthew,</p>

        <p>By changing hasIdentity() to isAuthenticated() you are implying that an identity can exist within Zend_Auth that has not been authenticated. Essentially, the only way to have an identity attached to the Zend_Auth object would be to have it put there (hence hasIdentity()) by a Zend_Auth_Adapter...</p>

        <p>I am interested to hear your thoughts if you are willing to go over the proof of concept and usage examples as to the Zend_Auth frame of mind that it presents.</p>

        <p>As for the credentials, I imagine that for something like that usage case, a developer would either start anew with Zend_Auth_Adapter_Abstract or extend an existing adapter. It would then perhaps import rules from somewhere, then set them as the expected value of a credential. After the authenticator prepares itself, it would then check to make sure those credentials have been satisfied (by time of day).</p>

        <p>The overall idea here is that adapters deal with identities and credentials, and concrete adapters implement concepts such as setPassword() or setUsername() as they are adapters specific concepts.</p>

        <p>Zend_Auth will provide basic adapters to guide users in creating the concrete api they so desire, or these adapters will serve as a jumping off point into writing their own adapter.</p>

        <p>-ralph</p>

      2. Jan 24, 2007

        <p>I think " restricting to certain time periods or IP " should be done by Zend_Acl. For some situations, Zend_auth and Zend_Acl should work together.</p>

        <p>I am also wondering why Darby put it into Zend_Auth which Zend_Acl has already implemented.<br />
        ---------------------------------------------------------------------------------------------<br />
        To Ralph,</p>

        <p>I agree with Matthew that isAuthenticated() is better than hasIdentity() because Zend_Auth is supposed to implement identity authentication. Let's compare them as follow:</p>

        <p>isAuthenticated() implies "Is it authenticated ?", that exactly what Zend_Auth needs to answer;</p>

        <p>hasIdentity() sounds like "Does it have identity ?", it could be always ture no matter it is authenticated or not.</p>

        1. Jan 24, 2007

          <p>In Zend_Acl, assertion classes may be used to indicate that an "allow" or "deny" rule only applies if the assertion attached to it returns true. For example, access to the "conference room" may only be allowed between the hours of 8:00am and 5:00pm. This is not necessarily related to authentication, however, since I've said nothing about whether an accessor must be authenticated. Perhaps even unauthenticated users can access the "conference room," but only during the allowed hours.</p>

          <p>In Zend_Auth, it may be useful to enable developers to apply arbitrary conditions (or credentials) to authentication adapters. A credential, as we're using the term here, is some condition on which successful authentication is dependent. For example, the password supplied must match the password on file for the purported identity.</p>

          <p>Assume that Johnny is uses our framework-powered application, and further assume that Johnny only logs in from his workstation each day, which has a static IP address. Finally, if someone attempts to authenticate as Johnny from a different IP address, we should consider this a fraudulent authentication attempt, since we know that Johnny doesn't any other workstation. How else would you nicely support this use case, except by providing the ability through the authentication component API?</p>

          1. Jan 24, 2007

            <p>Thanks Darby,</p>

            <p>I see what you mean.Then I'd like to implement Arbitrary Conditions Authentication(ACA for short) in Zend_Acl and call them from Zend_Auth. Otherwise, there are would be a lot of redundant code because the behavior of ACA is way similar with the rule in Zend_Acl.</p>

            <p>For your last question, there were actually two tasks:</p>

            <p>1 Identify Johnny with usename and password ( or whatever is equivalent to password)<br />
            2 Access control (or ACA as you mentioned)</p>

            <p>If there is no ACA in Zend_Auth, the developer will combine 1 & 2 above into loginAction(); if there are is an ACA with Zend_Auth, the developer will probably only instantiate Zend_Auth in the loginAction().</p>

            <p>Anyway, I can accept either one of them.</p>

      3. Jan 24, 2007

        <p>I agree that from a very high-level perspective, <code>isAuthenticated()</code> makes more sense and would perform the same function as <code>hasIdentity()</code>, currently named <code>isLoggedIn()</code>.</p>

        <p>In my mind, the main reason that <code>hasIdentity()</code> would make sense over other choices is because <code>getIdentity()</code> is already in use at the token level to provide the identity when it exists. In this way, the two method names illustrate their relation to each other.</p>

        <p>Sorry I failed to mention this bit in the comment, and I don't know if this is enough to change your opinion on the matter, but I thought to make this point in any case, since I noticed that I hadn't before.</p>