Added by Darby Felton, last edited by Darby Felton on Nov 28, 2006  (view change) show comment

Labels

 
(None)

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: 20)

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

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.

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.

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:

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.

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.

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.

Posted by Gavin at Sep 13, 2006 19:49

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

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.

Posted by Gavin at Nov 20, 2006 13:52

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

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

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.

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

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

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

Since Zend_Authentication (I cannot speak for Gavin's alternative approach) supports PHP callbacks in general, you could implement various authentication approaches with a regular function (e.g., function foo()), a method of an object instance (e.g., $obj->foo()), or a static method of a class (e.g., Class::foo()).

Perhaps better examples:

  • function authenticate($username, $password)
  • $authenticator->login($username, $password)
  • Authentication::login($username, $password)

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 ):

You could then load an authentication module like this:

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

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.

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.

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 very wide 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.

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 wrap them in one class?

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.

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

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).

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

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.

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.

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.

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'like behaviour, providing further checks like:

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

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.

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.

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.

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.

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.

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.

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.

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).

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.

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.

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.

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.

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?

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.

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.

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.

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.

The problem is one of Application Security, most specifically Access Control

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

1. Authentication 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.)

In terms of ZF

  • No state information is saved across requests, authentication is simply a process that answers a question "Is this person authentic?"
  • 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

2. Authorization 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).

In terms of ZF

  • Zend_Authorization is stateful.. Which means it has a dependence on Zend_Session so that across page requests, previous authorization information can be retained.
  • Currently, Zend_ACL has been approved and should be underway.. there should be some integration with Zend_Authorization and Zend_Session here.

3. Audit trail 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.

In Terms of ZF

  • 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.
  • More on this later.

I propose one of the following namespaces to contain this barrage of classes:

Zend_AccessControl_Authentication
Zend_AccessControl_Authorization
Zend_AccessControl_Authorization_ACL
Zend_AccessControl_Audit or Zend_AccessControl_Authorization_Audit

or by removing the AccessControl umbrella:

Zend_Authentication
Zend_Authorization
Zend_Authorization_ACL
Zend_Audit or Zend_Authorization_Audit

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

Pseudo Code supporting interface idea

In this example we have:

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

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 Zend_Authentication_AuthenticatorInterface::__construct() has a required parameter, we result in fatal error.

What purposes is Zend_Authentication_AuthenticatorInterface supposed to serve? I thought Zend_Authentication was long enough to type!

I think we would be well served by additional illustration of:

  • what setIdentity() would do
  • how setCredential() and setValidCredential() would work, what their purposes are
  • how identities and credentials are validated against an authentication backend or data store

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

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).

Darby,
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.

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.

Notes:

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

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.

more later,
Ralph

Some notes:

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.

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.

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

Zend_Auth_Token_Interface - we need to provide example tokens. I propose:

  • Simple username token - i.e. something storing just username
  • Expiring username token (one can set expiration on ctor - adapter could do it) which maybe also keeps additional info

For authenticator main class, I see we need following public functions:

  • ctor of course
  • check if we already have valid auth token
  • check auth data and create token (using concrete token & backend classes)
  • erase auth state (logout)
  • set session parameters (or replace session handler with another class?)

Static auth method looks like a good idea to me.