Implementing a consumer using the PHP OpenID library.

==Introduction==

OpenID is a decentralized identity system, and simply provides a way of
proving that someone owns a URL.  The URL is the user's identity.  An
OpenID consumer will need to confirm the identity of users by talking
to an OpenID server.  The PHP OpenID library does the heavy-lifting of
this interaction, leaving you to spend more time on your application.
It is highly recommended that you have a look at the OpenID homepage
and specification.  http://www.openid.net

The PHP OpenID library has been ported directly from the high-quality 
Python OpenID library.  See http://openid.schtuff.com.  For the most part, 
both the PHP and Python versions share a common API, making it simpler to 
port a server or client from one language to the other.  It is my goal to 
incorporate new fixes and features from the Python library over time and 
to keep the libraries in sync.  Currently, all of the examples are direct 
ports from the Python package as well.  

Further, the bulk of this documentation comes from the Python package, 
although it has been proof-read and PHP specific changes and comments 
added.

Be aware that as of this writing, the PHP port has not been heavily tested 
or used in any production environment.  There are bound to be bugs and 
gotchas.  


==What does PHP OpenID do?==

The PHP OpenID library fully implements the OpenID specification.  The 
library takes care of all consumer to server interaction logic and 
identity verification intricacies.  In a nutshell, the library provides 
building blocks upon which you can base your own OpenID server or 
consumer.  It does NOT make assumptions about your page processing or 
attempt to dispatch incoming requests.  Nor does it display any HTML pages 
or forms to the user.  That is all left to you, although some examples are 
provided.  

Operating in "dumb mode" by default, PHP OpenID can easily use the smart 
association mode, but requires your consumer to store server association 
information.  An example of both a dumb mode and a smart consumer are 
provided, the latter using Diffie-Helman for key exchange.  Anyone 
implementing a real server should use this method to reduce the number of 
requests being made.  


==PHP Compatibility==

PHP OpenID is intended to run on any version of PHP >= 4.1.0, although
to date it has only been tested with PHP5. If you encounter problems
running with other versions, please let me know.  Great care has been taken
to use PHP extensions when available and to provide fallback routines in
case the extension is not present. These fallback routines are often slower
and may not be as secure or "rock-solid". For production use of the library, 
I recommend installation of these extensions: gmp, mhash, curl, and tidy2.
PHP OpenID does not use or require PEAR.



==Getting Started==

Using the library requires a little work on your part, tailoring the
OpenID interaction to your application.  There are three steps
to get your OpenID consumer up and running: 1) Provide your own
OpenIDConsumer subclass. 2) Provide an object that implements the
ActionHandler interface 3) Control flow and dispatching.  Each of these
is covered in a section below.  You will also need to provide a
way for your application to send redirects to the user's browser.


==simple.php example==

The simple.php example script verifies an identity URL and nothing more.  
You may run this script by unpacking the openid package somewhere beneath 
your webserver docroot and loading simple.php in your browser.  You'll be 
presented with a form asking you for your Identity URL.  If you don't have 
an identity url, you may get one from livejournal.com or schtuff.com ( or, 
coming soon: videntity.org ).  The identity server will ask you if you 
trust your consumer server (simple.php).  Click yes and you will return to 
simple.php with a verification message.  If something did not work 
correctly, you will see an error message.  

simple.php is about 200 lines of code including comments and HTML code.  
Jump in!  The code is well-commented and readable.  Also, don't be afraid 
to look at the library source code, as it is also somewhat documented.  
Many questions are answered by looking at the source!  Go ahead and open 
up simple.php, as you find it useful in the following sections.  


==Subclassing OpenIDConsumer==

The core of the library's consumer logic exists in the
consumer.OpenIDConsumer class, and to get our example working we'll
make a subclass extending it's behavior.  OpenIDConsumer handles all
the server interaction, logic, and HTTP calls.  The only thing it
needs from us is an implementation of the verify_return_to method.

Our subclass is called SimpleOpenIDConsumer, and contains only the 
verify_return_to method.  The purpose of this method is to look at the 
elements of the return_to url and make sure they have not been altered in 
any way.  First, we use the standard parse_url() function to parse the 
url, and compare the host information to our consumer's host.  This is 
essential for preventing an attack where one consumer can be used to log 
into other consumers.  If they match, we consider the return_to url 
verified, and return true.  Otherwise we return false.  


==The ActionHandler Interface==

In the course of an OpenID transaction, the consumer object may find
itself in several different states.  The interface.ActionManager class
provides a callback interface for each of these states, and you must
supply an object that implements these methods.

SimpleActionHandler is the ActionHandler implementation for our
example.  For each state it either sets an alert message, or does a
check and transitions to another state which sets the message.

The doCheckAuthRequired callback uses the consumer object's check_auth
call, and returns response state object, which is then used for a
state transition.  This is necessary since our example is operating
in dumb mode.  See http://www.openid.net/specs.bml#mode-check_authentication

Also required in the ActionHandler object are implementations of the 
createReturnTo and getOpenID methods.  The consumer supplies the server 
with a return_to url to which it will redirect once the server interaction 
is complete.  Obviously, the return_to url is application specific, and 
building this url is left up to the user of the library.  The 
ActionHandler contains a createReturnTo method which accepts an base_url, 
identity url, and a dict ( key/val mapped array ) of additional arguments.  
From these parameters your implementation will construct and return a 
return_to url for your application.  One thing that every OpenID consumer 
needs to do is keep track of the initial "identity_url" entered by the 
user.  Since we are a stateless consumer, we choose to pass this 
information through the return_to url.  This could also be done with a 
cookie/session solution.  

The last method required is getOpenID, which returns the identity_url
initially entered by the user.  In simple.php we passed this though the
return_to url, so we can simply extract it from the query arguments.

==Dispatching==

Dispatching is the process of looking for arguments, and then running
code based on their values.  In our case we're looking for the
existence of an "identity_url" argument, or an "openid.mode"
argument. ( PHP's URL parsing converts "." to "_", so we actually look
for "openid_mode". )

In the case of "identity_url" existing, we are at the very beginning
of our OpenID transaction, and need to find out more information from
the user's identity page.  We call consumer::find_identity_info() to
learn more about the identity and the server we are to contact.

Once we have the identity server's url, we build a trust_root for our 
application, a return_to (see above), and let our consumer object do the 
rest by calling consumer::handle_request().  handle_request returns a 
server url which we are to redirect to.  

The server will then respond via redirect to us with an "openid.mode"
argument.  We then create an interface.Request object with the input
parameters, and call the consumer object's handle_response method.  We
are returned a response object, which is executed by calling it's
doAction() method.


==Preventing Replay Attacks==

In the interest of keeping the example short, code to prevent replay 
attacks is not included.  A replay attack involves someone sniffing your 
internet traffic, and stealing your OpenID server responses.  If no 
safeguards are taken, and you are not connecting from the consumer to the 
OpenID server over SSL, it is very easy for someone to execute a replay 
attack, and masquerade as someone else in your consumer application.  

The simplest way to prevent replay attacks is to add a nonce parameter
into the return_to url.  A nonce is a value that is only used once
then discarded.  When building your return_to url, stick a random
number in, and also stick that random number in a consumer-side
database or file.  When verifying the return to url, you should
extract the nonce from the query arguments, and make sure it
exists in the nonce database, also discarding it from the database.
If the nonce does not exist in the database, then someone is attempting
a replay attack, and you should return false from verify_return_to.


==Smart Mode==

Operating in smart mode is a wise choice for any consumer, however it
does require a little more work from you.  Smart mode involves
creating an association with an OpenID server, and then reusing that
association for other users of that server.  This prevents extra trips
to the server and speeds up the validation process tremendously.

Implementing smart mode with PHP OpenID requires you implement an 
ConsumerAssociationManager class which stores association keys in a 
persistent datastore ( database, memcached, flat file, etc ).  An example 
consumer association implementation is available in the examples directory 
in the associations module.  It uses SQLite for association storage, but
is written with to PHP's new PDO database layer, so should be easily
adaptable to any database.  If you do not have PDO in your php installation, 
the example could easily be ported to use mysql_* or pgsql_* calls, etc.

A memcached example should be coming soon.


==httpconsumer.php Example==

To be written


==sampleserver.php Example==

To be written


==Conclusion==

To gain a basic understanding of an OpenID consumer, read through 
simple.php and interact with it and possibly re-read this article.  If you 
are serious about implementing an OpenID consumer or server, then refer to 
the httpconsumer.php and sampleserver.php examples for further details.  
