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_OpenId Component Proposal

Proposed Component Name Zend_OpenId
Developer Notes
Proposers Dmitry Stogov
Revision 1.1 - 1 June 19: First draft. (wiki revision: 16)

Table of Contents

1. Overview

Zend_OpenId is a set of components that allow anyone to create OpenID authentication servers and consumers

According to an identity is the "collective aspect of the set of characteristics by which a thing is definitively recognizable or known". In many applications an Identity is necessary to authenticate entities, authorize access, maintain privacy and prevent disclosures

In the current Internet world, we have a lot of different unconnected identity systems, and a lot of identities for the individual user. An end user needs to remember their names and passwords for each and every site or service they visit and use.

Identity 2.0 aims to solve this problem. It advocates the use of simple decentralized solutions, so that the user is able to choose where to host their identity, and also control how much information about they should be disclosed to any given site. Sites should be able to integrate such an identity system without any redesign of existing applications in a very short time.

OpenID is one of the leading Identity 2.0 solutions. It provides a protocol framework for secure identity service discovery, authentication and data exchange. These are very simple protocols that require only standard HTTP requests and do not require the use of new schemes, public keys, SSL or browser plug-ins. Some of these (for example, SSL) may be used in addition to the core protocols.

An OpenID identifier is typically a URL. It may be the URL of the user's personal page, blog or other resource that may provide additional information about him. Such URLs are aliases pointing to the OpenID Provider's local identifier for the user. If a user does not wish to support an alias, they may use the OpenID Proider's local identifier directly. End-users should be able to use this identity URL to register and authenticate themselves on any site that supports OpenID. No more need for many passwords and duplicate user names - just one identifier for Internet services.

OpenID is an open, decentralized, and free user centric solution. A user may choose which OpenID provider to use, or even create their own personal identity server. No central authority must approve or register Relying Parties (OpenID Consumers or just Sites) or OpenID Providers (OpenID Servers). The protocols utilised by OpenID scale well.

OpenID standards are community driven. Several implementations are freely available for C++, C#, Java, Perl, Python, Ruby and PHP. However there is no final stable release yet for a PHP library conforming to the OpenID 2.0 specification (currently draft 11). The difficulty of a PHP implementation is increased by the fact that the majority of PHP installations do not have sufficient cryptographic or efficient math functions to provide OpenID applications which perform well. This proposal includes a reference to a possible openssl extension patch which would improve the efficiency of such functions in PHP.

At present, Identity 2.0 and OpenID are very popular and growing technologies and the Zend Framework's OpenID implementation may be a significant advantage in leading and supporting the development of web applications leveraging OpenID in PHP.

This document describes proposals for the Zend Framework's OpenID implementation. It will allow the use of both OpenID Providers (Servers) and Consumers (Relying Parties/Applications) that will fully implement the draft OpenID 2.0 Authentication Specification and remain backwards compatible with the current OpenID 1.1 specification. Also proposed is support for OpenID Extensions (e.g. Simple Registration Extension) which may allow for Relying Parties and Providers exchange data concerning the authenticated user with that user's permission.

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • This component must implement "OpenID Authentication 2.0 - Draft 11" standard.
  • This component must be backward compatible with "OpenID Authentication 1.1" standard.
  • This component must implement Yadis and XRI discovery (probably through Zend_Service_Yadis) in addition to HTML-based discovery
  • This component must implement generic extension mechanism
  • This component must implement "Simple Registration Extentsion 1.0" standard.
  • This component will require patched version of OpenSSL extenion or one of "big-math" extensions (gpm or bcmath) and one of hash extensions (hash or mhash).
  • Zend_OpenId_Consumer must be able to work with both 1.1 and 2.0 protocol versions in associated and dumb modes
  • Zend_OpenId_Provider will not implement any user interface, but will provide functions to use external user interface

4. Dependencies on Other Framework Components

  • Zend_Exception
  • Zend_Http_Client
  • Zend_Session
  • Zend_Controller_Response_Http
  • Zend_Uri ? (I would propose to move OpenId::selfUrl() and especially OpenId::normalizeUrl() into Zend_Uri)
  • Zend_Crypt ? (It is not committed yet)
  • Zend_Services_Yadis ? (It is not committed yet)

5. Theory of Operation

The main purpose of Zend_OpenId components is implementing an OpenID authentication protocol described in the following diagram:

  1. The authentication is initiated by end-user, who passes his OpenID identifier to an OpenID consumer through User-Agent.
  2. OpenID consumer performs normalization of user-supplied identifier, and discovery on it. As result it get claimed identifier, OpenID provider URL and OpenID protocol version.
  3. OpenID client establishes an optional association with server using Diffie-Hellman keys. As result both parties get common shared secret that is used for signing and verification of the following messages.
  4. OpenID consumer redirects User-Agent to OpenID provider URL with OpenID authentication request.
  5. OpenID Provider checks if user-Agent is already authenticated and offers to do so if need.
  6. The end user enters necessary password.
  7. OpenID Provider checks if it allowed to pass user identity to given consumer, and asks user if need
  8. The end user allows or disallows passing his identity
  9. OpenID Provider redirects User-Agent back to OpenID consumer with authentication approved or failed request.
  10. The OpenID consumer verifies the information received from provider by using shared secret got on step 3 or by sending additional direct request to OpenID proider.

The Zend_OpenId components will implement both Zend_OpenId_Consumer and Zend_OpenId_Provider. The consumer class will contain two main public methods login() and verify(). The first one should be called on user login (step 1), it will perform normalisation, discovery and redirection to provider (steps 2-4). The function verify() should be called after redirecting back from provider (step 9), it must perform final validation of received authentication data. The Provider will have just one main method handle() that should be called on any OpenID request.

Both Provider and Consumer need external storage to store some data between requests. These storages will be implemented through abstract interface class, so it will be possible to use different kinds of storages without redesign.

6. Milestones / Tasks

  • Milestone 1: design notes will be published here
  • Milestone 2: Working prototype implementing HTML-based discovery checked into the incubator
  • Milestone 3: Working prototype implementing Yadis and XRI discovery checked into the incubator
  • Milestone 4: Working prototype implementing SREG protocol checked into the incubator
  • Milestone 5: Unit tests exist, work, and are checked into SVN.
  • Milestone 6: Initial documentation exists.

If a milestone is already done, begin the description with "[DONE]", like this:

  • Milestone #: [DONE] Unit tests ...

7. Class Index

  • Zend_OpenId
  • Zend_OpenId_Exception
  • Zend_OpenId_Consumer
  • Zend_OpenId_Consumer_Storage
  • Zend_OpenId_Consumer_Storage_File
  • Zend_OpenId_Provider_Storage
  • Zend_OpenId_Provider_Storage_File
  • Zend_OpenId_Provider_User
  • Zend_OpenId_Provider_User_Session
  • Zend_OpenId_Extension
  • Zend_OpenId_Extension_Sreg

8. Use Cases


Zend_OpenId_Consumer usage


Simplified OpenID server with external user interface

9. Class Skeletons



Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Jun 22, 2007

    <p>I am very excited to see this proposal.</p>

    <p>Wouldn't this be a good thing to tie into Zend_Auth instead of reinventing the wheel?</p>

    1. Jun 25, 2007

      <p>Zend_OpenId component implements both OpenID probvider and consumer.<br />
      And provider is not related to Zend_Auth anyway.</p>

      <p>I was thinking about creating Zend_Auth_Adapter_OpenId in the future.</p>

  2. Jun 25, 2007

    <p>Hey Dmitry,</p>

    <p>My comments on the proposal are below.</p>

    <p>Firstly the comment about there being no existing PHP OpenID 2.0 implementation should be deleted quickly. There is a PHP4 implementation written by JanRain called the PHP OpenID Library which is freely available under an open source license. It's the current defacto 2.0 standard in PHP. It is not a perfect 2.0 implementation but that's to be expected given we're currently at Draft 11 in the specification process for 2.0 (it was Draft 10 not so long ago) so a perfect implementation doesn't exist anywhere.</p>

    <p>The overview could be split into a few sections. e.g. What is OpenID, How the ZF will implement OpenID, and a few long term goals/objectives on why it's useful to be included in the framework. The truth is most people don't know what OpenID is, or why they should care. Giving them information is essential, esp. so the reviewers know what they're reviewing <ac:emoticon ac:name="wink" />.</p>

    <p>The name of the component should probably be "Zend_Openid", small "i" in the name since that seems to follow the Zend Framework naming convention better. Nitpicking me <ac:emoticon ac:name="smile" />.</p>

    <p>Some mention could be made of OpenID compatibility, e.g. Microsoft Vista, AOL implementing an OpenID 1.1 Provider for users, other integrated services. In the overview, this would grab the proposal more interest from other users.</p>

    <p>Should be careful about talking about the implementation of cryptographic algorithms. PHP's core extension BCMath is a living terror, but GMP or big_int are far faster. Not all PHP solutions are horribly inefficient. Also, the dependency on an openssl patch should be optional. I'm not even sure it needs mentioning. As far as I know such a patch (yours or Wez's) will not be implemented in the near future.</p>

    <p>The requirement to implement Simple Registration Extension 1.0 should be dropped. OpenID extensions require no specific internal handling - literally Extension support should be extremely generic so developers may define other Extensions at runtime (e.g. sreg 1.1 Draft 1) which immediately enables their support. This is possible since Extensions just add a "namespace" to the key-value list sent/returned from an OP server. Maybe "This component must implement support for OpenID Extensions" is generic enough for this. 2.0 will see a few new Extensions which could use such a generic interface. (I'll forward my Extension implementation so you can see what I mean).</p>

    <p>On the dependencies:</p>

    <p>Zend_Http_Client should be used - it's easy to implement where needed. Zend_Services_Yadis is effectively 95% complete for OpenID 2.0. It could do with a little API simplification but is otherwise functional. I would suggest come review time, we both press for Yadis to be reviewed since they are potentially dependent on each other.</p>

    <p>Zend_Uri has a static check() method for validating a normalised URI. I would suggest a subclass has more value since normalisation should be local to the OpenID classes. On a similar track, I think selfUrl() could be removed. Setting the Trust Root and Return To uris should be something a user can easily do themselves if they wish. Note that Zend_Uri does not currently have complete support for Internationalised Domain Names using Unicode characters in URIs. This is a requirement of the Yadis Specification (can't remember off hand whether the OpenID 2.0 implementors' draft itself mentions it).</p>

    <p>I'll leave comments on code to another day - In short the existing code needs to be refactored into more focused classes where it's easier to modify behaviour and apply the growing set of OpenID 2.0 rules.</p>

    <p>One of the final points is on design. As I elaborated last week when I was about to post my own OpenID proposal, I was pushing for a split between OpenID and it's components. I still believe there is a lot of merit in extracting common stuff like Diffie-Hellman, HMAC, and Math elements into their own classes. This would allow other components reuse these (e.g. HMAC for Zend_Mail) and cut out any future duplication of the same sort. If you want an example of an implementation, I've already proposed my existing code to PEAR and it's listed as a Proposal for comment. The rest of my code will find it's way to you very soon. I'll also update the referenced repository with the current Zend_Services_Yadis implementation (mainly a few bug fixes I found during testing over the weekend).</p>

    1. Jun 25, 2007

      <p>I saw JanRain library version 1.2.2 at <a class="external-link" href=""></a> and it doesn't have support for OpenID 2.0. It also is not compatible with latest php versions (it supports php from 4.3.0 to 5.1).</p>

      <p>I agree that overview is too small and this proposal is good mainly for people that already know about OpenID.</p>

      <p>The name of the component should be "Zend_OpenId" (we already have Zend_Auth_Storage_NonPersistent).</p>

      <p>The component will be able to work without OpenSSL patch, but with bcmath it will work too slow.</p>

      <p>According to Simple Registration Extension 1.0 may be you are right. I would like to support your way if you show me what kind of API should be used for that.</p>

      <p>I agree to reuse Zend_Http_Client and Zend_Services_Yadis, however I don't see how Yadis may depend on OpenId.</p>

      <p>URI normalization is described in RFC3986 and it is not related to OpenID. It would be nice to have it implemented in Zend_URI. Also I don't see any reason to always require specification of trust root and return uri from user.</p>

      <p>Extracting reusable components is a good idea, but I have no idea how much time it will take.</p>

      <p>I would be glad to look into your OpenID proposal, and OpenID and Yadis code.</p>

  3. Jun 25, 2007

    <p>Hi Dmitry,</p>

    <p>Yes, 1.2.2 implements OpenID 1.0, but a few months back JanRain released 2.0RC1 of the library which implements OpenID 2.0. Also, the service is run by JanRain and they certainly implement 2.0 for their server (I have one of my OpenID identities registered there and using 2.0 for authentication).</p>

    <p><a class="external-link" href=""></a></p>

    <p>I'd prefer (only a personal pref) either Openid or OpenID (if camel casing is allowable then might as well use the official spec title if possible). I used Openid in the proposal example code (my one) - I'm talking too much over a tidly point...</p>

    <p>Yes, but GMP is far faster than BCMath - possibly as fast as an openssl patch unless openssl gets an even better set of algorithms than gmp or PECL's big_int (though big_int is not as good as gmp). BCMath has some horrible algorithm in its source screwing it up. It would be nice to see that improve if possible given the mountain of code likely depending on it.</p>

    <p>Simple Registration is only of a few extensions. Once 2.0 is finalised don't be surprised to see something allowing Phishing protection querys to check if a Phishing-safe method was used - for example Microsoft will bundle a rebranded OpenID 2.0 protocol in Vista CardSpace once the 2.0 drafting is over.</p>

    <p>Here's an extract of my code. I use a "Request" object which lets the end user setup extra Extension parameters to piggy-back on the authentication requests. Part of my refactoring woes (I initially went by Draft 8 or something way back when) is refactoring this object into two separate classes - Authentication, and Extension Requests (Extensions can piggy back without there being defined authentication params). It's simple to add a piggy-backed parameter - just add the namespace for the extension, and append all params to the new authentication redirect's URI. I just start by building separate Argument and ExtensionArgument arrays - the rest is just making a http_build_query call.</p>

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

    • Add an extension argument to this request
    • @param string $namespace
    • @param string $key
    • @param string $value
    • @return void
      public function setExtensionArgument($namespace, $key, $value)
      if (!in_array($namespace, $this->_extensionNamespacesSet))
      Unknown macro: { $namespacesAllowed = array_keys(Zend_Openid}

      if (strrpos($namespace, '.') === false)

      Unknown macro: { $namespace = implode('.', array(Zend_Openid}

      $extensionArgKey = implode('.', array($namespace, $key));
      $this->_extensionArguments[$extensionArgKey] = $value;


    • Getter to return an array of all Extension arguments
    • @return array
      public function getExtensionArguments()
      Unknown macro: { return $this->_extensionArguments; }


    <p>The response just comes back with a param like openid_sreg_email, which is then added to the openid_signed list. The value signature just needs to include it when computing the MAC.</p>

    <p>Yadis does not depend on OpenID - I should proofread my comments!</p>

    <p>OpenID 2.0 specifies some additional requirements - a Zend_Uri subclass might allow these more easily. Might not though. I'm thinking of rules disallowing URI fragments and such.</p>

    <p>Extracting classes isn't too difficult - it's small fry against implementing OpenID itself. The extracted code is quite small - see the PEAR proposals.</p>

    <p>Code coming soon - if I can't finish the refactoring quickly I'll just forward what I have. It's enough at least to make a 2.0 process so long as some edge cases (XRIs since Yadis code needs a small update) aren't met. Probably I'm just nitpicking over folding more code into Request/Response classes so the specification is more easily understandable with it.</p>

    1. Jun 26, 2007

      <p>I didn't see 2.0RC1 before. Than you for pointing me.</p>

      <p>As I understood according to coding standard the name must be OpenId, however I would prefer OpenID too.</p>

      <p>You are right anout GMP. It gives near the same speed as OpenSSL, the bcmath problem in one algorithm, that should be changed to more efficient one.</p>

      <p>I like your solution for generic OpenID extensions support, however this time I don't see if it is enough for SREG.</p>

  4. Jun 27, 2007

    <p>Yeah, the code extract is pretty vague without more of the code to put it in context. The use case is something like:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $userIdentity = $_GET['openid_url'];
    if(!isset($userIdentity)) {
    // handle error of missing identity field
    $store = new Zend_Openid_Store_File('/tmp/openid');
    $openid = new Zend_Openid_Consumer($store);
    $authRedirect = $openid->start($userIdentity);

    // Add SREG extension arguments
    $authredirect->setExtensionArgument('sreg', 'required', 'nickname,email');
    $authRedirect->setExtensionArgument('sreg', 'optional', 'fullname,gender,country');

    $authRedirect->redirect(); // redirect user agent to OP server]]></ac:plain-text-body></ac:macro>

    1. Jul 06, 2007

      <p>I made the same in little bit different way:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      $consumer = new Zend_OpenId_Consumer();
      $props = array(
      'nickname' => true,
      'email' => true,
      'fullname' => false,
      'gender' => false,
      'country' => false
      $sreg = new Zend_OpenId_Extension_Sreg($props);
      $ret = $consumer->login($openid_url, null, null, $sreg);

  5. Jul 06, 2007


    <p>How familiar are you with Zend Framework? For example, in <code>Zend_OpenId_Provider_User_Session</code> you don't leverage <code>Zend_Session</code>. This proposal should also be integrated with <code>Zend_Auth</code>. After all, Zend Framework is a coherent whole, not a collection of unrelated classes.</p>

    1. Jul 09, 2007

      <p>I don't know ZF well, so thank you for suggestion.<br />
      BTW I thought here is a place to discuss proposal and prototype that you saw is just a quick implementation that I plan to finish after proposal approvement.</p>

      <p>I am not sure that this proposal must include Zend_Auth integration. It can be implemented later as an Zend_Auth adapter on top of Zend_OpenId_Consumer.</p>

      1. Jul 13, 2007

        <p>I agree that Zend_Session should be leveraged and that the Zend_Auth adapter should be trivial to implement (provided there is not an impedance mismatch) after we have a viable OpenID component.</p>

        <p>Oh, and I believe the name should be as Dmitry already has it, "Zend_OpenId". This is because "Open" is one word, and "identifier" is another word. We do not capitalize all of the letters of acronyms and abbreviations (e.g., Zend_Config, Zend_Uri).</p>

  6. Jul 07, 2007

    <p>I'm also excited to see this develop.</p>

    <p>Although I also prefer Zend_OpenID, I think the coding standard does indeed require that it must be Zend_OpenId ...</p>

    <p>"If a class name is comprised of more than one word, the first letter of each new word must be capitalized. Successive capitalized letters are not allowed; e.g., a class "Zend_PDF" is not allowed, while "Zend_Pdf" is acceptable."<br />
    --<a class="external-link" href=""></a></p>

    <p>I agree there should be a Zend_Auth_Adapter_OpenId, but also agree that doesn't belong in this proposal. It should be an Auth team proposal, and make use of Zend_OpenId_Consumer.</p>

    <p>I think it would be helpful to use Zend_Session in place of the session_* functions, but I know even less about OpenID provider-side implementation than I do about relying-party-side. <ac:emoticon ac:name="wink" /></p>

  7. Jul 07, 2007

    <p>I think Dmitry would agree that this is a proposal, not a review of complete code. It's something to be examined with the knowledge it will get serious attention over the coming few months.</p>

    <p>There will almost certainly be a Zend_Auth_Adapter_OpenId, however OpenID is itself a necessary standalone component given both its size and complexity. It's impossible to bundle an entire OpenID 2.0 library into an adapter, so library first, adapter second.</p>

    <p>The original code for this was developed outside the Zend Framework initially - hence the lack of component integration at the moment. I'm sure once the code is being refactored the current Zend Framework components will quickly find themselves co opted <ac:emoticon ac:name="wink" />.</p>

  8. Jul 12, 2007

    <p>Dmitry – I know that OpenID needs to redirect to the OpenID provider in order to login and/or retrieve identity details, and I see some methods relating to redirection. Please consider that this will likely be used in the MVC, and think about how it might integrate with Zend_Controller_Response_Http (which aggregates HTTP response headers and sends them); I don't want the two components to conflict in the end.</p>

    <p>Other than that, the API looks reasonable to me, and I think the dialog you and Padraic are having is leading to some good refinement of the code. I look forward to experimenting with this!</p>

    1. Jul 25, 2007

      <p>It is not a problem. This won't effect design but just implementationt that is done only on "working-prototype" level.</p>

  9. Jul 31, 2007

    <ac:macro ac:name="note"><ac:parameter ac:name="title">Zend Comments</ac:parameter><ac:rich-text-body>
    <p>This proposal is approved for incubator development. Before the component would be promoted to core (i.e., the trunk), however, it should take advantage of <a href="">Zend_Service_Yadis</a> and <a href="">Zend_Crypt</a>, when they become available. Also, this component should integrate with Zend_Session (<ac:link><ri:user ri:username="darby" /></ac:link>, <ac:link><ri:user ri:username="ralph" /></ac:link>) and Zend_Controller_Reponse_Http (<ac:link><ri:user ri:username="matthew" /></ac:link>).</p>

    <p>Please also coordinate with <ac:link><ri:user ri:username="padraic" /></ac:link>, assisting with his proposed components (Crypt and Yadis) as possible.</p></ac:rich-text-body></ac:macro>

  10. Aug 07, 2007

    <p>So is this component going to get a spot in the tracker?</p>

  11. Sep 21, 2007

    <p>I'm new to OpenID, but after having looked at Provider, Provider_Storage, Provider_User, Provider_User_Session (incubator) DRY and I were thinking: delegating to the existing Zend_Auth package seems like the way to go. Implementing Zend_Auth_Adapter_Interface (either directly by your Provider_Storage class or by a seperate class Zend_Auth_Adapter_OpenId_Provider) brings in the Zend_Auth class' magic for free. Chances are that you could get rid of the Provider_User hierarchy...</p>

    1. Sep 28, 2007

      <p>I've been working on some real world ZF use cases for Zend_OpenId_Provider, now. Obviously my initial comment wasn't based on any <ac:emoticon ac:name="smile" /><br />
      Here's my feedback on the authentication part, the persistent data part and the server part.</p>

      <p>1. Maybe it's possible and a good idea as well to leverage the Zend_Server package for the server part of Zend_OpenId_Provider.<br />
      2. I want to reuse my existing auth/login/logout models (based on the Zend_Auth package). So I just want to pass a Zend_Auth_Result to the server component.<br />
      3. I'd like to be able to pass seperate Associations and Trusted Site models to the server component. The server would typehint abstract classes or interfaces from the library.</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      // Use Case for a Zend_Open_Id Server subpackage. No response object in this example.
      // Init server component
      // Implements Zend_Server_Interface
      // Extends Zend_Server_Abstract, maybe Zend_Rest_Server?
      $server = new Zend_OpenId_Server();

      // identify class that represents server actions. Static methods only in this example
      $actions = 'Zend_OpenId_Server_Actions';

      // Setup server with actions and make some models available to the actions
      $server->setClass($actions, $authResult, $associations, $sites);

      // Handle request

  12. Dec 27, 2007


    <p>Any update?</p>

    1. Dec 27, 2007

      <p>Zend_OpenId has been moved to core on November. <ac:emoticon ac:name="smile" /><br />
      For further details you can have a look at
      <a class="external-link" href=""></a>
      <a class="external-link" href=""></a></p>

      1. Dec 28, 2007


        <p>Ups, time to checkout a new copy then. Thanks for the links Simone <ac:emoticon ac:name="smile" /> </p>

        <p>Do you know if there's an RSS feed of the changelog? It would be nice if I could add it to Eclipse and get notify every time the log changes.</p>

        1. Dec 28, 2007

          <p>You can subscribe to SVN changelog from here <a class="external-link" href=""></a></p>

          <p>If you want to subscribe a custom changelog simply apply the filter from <a class="external-link" href=""></a> then subscribe the page with your default aggregator. The feed URL will be extracted by your aggregator from the link tag. <ac:emoticon ac:name="smile" /></p>