<?php |
<?php |
|
|
/** |
/** |
* This module documents the main interface with the OpenID consumer |
* This module documents the main interface with the OpenID consumer |
* library. The only part of the library which has to be used and |
* library. The only part of the library which has to be used and |
* isn't documented in full here is the store required to create an |
* isn't documented in full here is the store required to create an |
* Auth_OpenID_Consumer instance. More on the abstract store type and |
* Auth_OpenID_Consumer instance. More on the abstract store type and |
* concrete implementations of it that are provided in the |
* concrete implementations of it that are provided in the |
* documentation for the Auth_OpenID_Consumer constructor. |
* documentation for the Auth_OpenID_Consumer constructor. |
* |
* |
* OVERVIEW |
* OVERVIEW |
* |
* |
* The OpenID identity verification process most commonly uses the |
* The OpenID identity verification process most commonly uses the |
* following steps, as visible to the user of this library: |
* following steps, as visible to the user of this library: |
* |
* |
* 1. The user enters their OpenID into a field on the consumer's |
* 1. The user enters their OpenID into a field on the consumer's |
* site, and hits a login button. |
* site, and hits a login button. |
* 2. The consumer site discovers the user's OpenID server using the |
* 2. The consumer site discovers the user's OpenID server using the |
* YADIS protocol. |
* YADIS protocol. |
* 3. The consumer site sends the browser a redirect to the identity |
* 3. The consumer site sends the browser a redirect to the identity |
* server. This is the authentication request as described in |
* server. This is the authentication request as described in |
* the OpenID specification. |
* the OpenID specification. |
* 4. The identity server's site sends the browser a redirect back |
* 4. The identity server's site sends the browser a redirect back |
* to the consumer site. This redirect contains the server's |
* to the consumer site. This redirect contains the server's |
* response to the authentication request. |
* response to the authentication request. |
* |
* |
* The most important part of the flow to note is the consumer's site |
* The most important part of the flow to note is the consumer's site |
* must handle two separate HTTP requests in order to perform the full |
* must handle two separate HTTP requests in order to perform the full |
* identity check. |
* identity check. |
* |
* |
* LIBRARY DESIGN |
* LIBRARY DESIGN |
* |
* |
* This consumer library is designed with that flow in mind. The goal |
* This consumer library is designed with that flow in mind. The goal |
* is to make it as easy as possible to perform the above steps |
* is to make it as easy as possible to perform the above steps |
* securely. |
* securely. |
* |
* |
* At a high level, there are two important parts in the consumer |
* At a high level, there are two important parts in the consumer |
* library. The first important part is this module, which contains |
* library. The first important part is this module, which contains |
* the interface to actually use this library. The second is the |
* the interface to actually use this library. The second is the |
* Auth_OpenID_Interface class, which describes the interface to use |
* Auth_OpenID_Interface class, which describes the interface to use |
* if you need to create a custom method for storing the state this |
* if you need to create a custom method for storing the state this |
* library needs to maintain between requests. |
* library needs to maintain between requests. |
* |
* |
* In general, the second part is less important for users of the |
* In general, the second part is less important for users of the |
* library to know about, as several implementations are provided |
* library to know about, as several implementations are provided |
* which cover a wide variety of situations in which consumers may use |
* which cover a wide variety of situations in which consumers may use |
* the library. |
* the library. |
* |
* |
* This module contains a class, Auth_OpenID_Consumer, with methods |
* This module contains a class, Auth_OpenID_Consumer, with methods |
* corresponding to the actions necessary in each of steps 2, 3, and 4 |
* corresponding to the actions necessary in each of steps 2, 3, and 4 |
* described in the overview. Use of this library should be as easy |
* described in the overview. Use of this library should be as easy |
* as creating an Auth_OpenID_Consumer instance and calling the |
* as creating an Auth_OpenID_Consumer instance and calling the |
* methods appropriate for the action the site wants to take. |
* methods appropriate for the action the site wants to take. |
* |
* |
* STORES AND DUMB MODE |
* STORES AND DUMB MODE |
* |
* |
* OpenID is a protocol that works best when the consumer site is able |
* OpenID is a protocol that works best when the consumer site is able |
* to store some state. This is the normal mode of operation for the |
* to store some state. This is the normal mode of operation for the |
* protocol, and is sometimes referred to as smart mode. There is |
* protocol, and is sometimes referred to as smart mode. There is |
* also a fallback mode, known as dumb mode, which is available when |
* also a fallback mode, known as dumb mode, which is available when |
* the consumer site is not able to store state. This mode should be |
* the consumer site is not able to store state. This mode should be |
* avoided when possible, as it leaves the implementation more |
* avoided when possible, as it leaves the implementation more |
* vulnerable to replay attacks. |
* vulnerable to replay attacks. |
* |
* |
* The mode the library works in for normal operation is determined by |
* The mode the library works in for normal operation is determined by |
* the store that it is given. The store is an abstraction that |
* the store that it is given. The store is an abstraction that |
* handles the data that the consumer needs to manage between http |
* handles the data that the consumer needs to manage between http |
* requests in order to operate efficiently and securely. |
* requests in order to operate efficiently and securely. |
* |
* |
* Several store implementation are provided, and the interface is |
* Several store implementation are provided, and the interface is |
* fully documented so that custom stores can be used as well. See |
* fully documented so that custom stores can be used as well. See |
* the documentation for the Auth_OpenID_Consumer class for more |
* the documentation for the Auth_OpenID_Consumer class for more |
* information on the interface for stores. The implementations that |
* information on the interface for stores. The implementations that |
* are provided allow the consumer site to store the necessary data in |
* are provided allow the consumer site to store the necessary data in |
* several different ways, including several SQL databases and normal |
* several different ways, including several SQL databases and normal |
* files on disk. |
* files on disk. |
* |
* |
* There is an additional concrete store provided that puts the system |
* There is an additional concrete store provided that puts the system |
* in dumb mode. This is not recommended, as it removes the library's |
* in dumb mode. This is not recommended, as it removes the library's |
* ability to stop replay attacks reliably. It still uses time-based |
* ability to stop replay attacks reliably. It still uses time-based |
* checking to make replay attacks only possible within a small |
* checking to make replay attacks only possible within a small |
* window, but they remain possible within that window. This store |
* window, but they remain possible within that window. This store |
* should only be used if the consumer site has no way to retain data |
* should only be used if the consumer site has no way to retain data |
* between requests at all. |
* between requests at all. |
* |
* |
* IMMEDIATE MODE |
* IMMEDIATE MODE |
* |
* |
* In the flow described above, the user may need to confirm to the |
* In the flow described above, the user may need to confirm to the |
* lidentity server that it's ok to authorize his or her identity. |
* lidentity server that it's ok to authorize his or her identity. |
* The server may draw pages asking for information from the user |
* The server may draw pages asking for information from the user |
* before it redirects the browser back to the consumer's site. This |
* before it redirects the browser back to the consumer's site. This |
* is generally transparent to the consumer site, so it is typically |
* is generally transparent to the consumer site, so it is typically |
* ignored as an implementation detail. |
* ignored as an implementation detail. |
* |
* |
* There can be times, however, where the consumer site wants to get a |
* There can be times, however, where the consumer site wants to get a |
* response immediately. When this is the case, the consumer can put |
* response immediately. When this is the case, the consumer can put |
* the library in immediate mode. In immediate mode, there is an |
* the library in immediate mode. In immediate mode, there is an |
* extra response possible from the server, which is essentially the |
* extra response possible from the server, which is essentially the |
* server reporting that it doesn't have enough information to answer |
* server reporting that it doesn't have enough information to answer |
* the question yet. |
* the question yet. |
* |
* |
* USING THIS LIBRARY |
* USING THIS LIBRARY |
* |
* |
* Integrating this library into an application is usually a |
* Integrating this library into an application is usually a |
* relatively straightforward process. The process should basically |
* relatively straightforward process. The process should basically |
* follow this plan: |
* follow this plan: |
* |
* |
* Add an OpenID login field somewhere on your site. When an OpenID |
* Add an OpenID login field somewhere on your site. When an OpenID |
* is entered in that field and the form is submitted, it should make |
* is entered in that field and the form is submitted, it should make |
* a request to the your site which includes that OpenID URL. |
* a request to the your site which includes that OpenID URL. |
* |
* |
* First, the application should instantiate the Auth_OpenID_Consumer |
* First, the application should instantiate the Auth_OpenID_Consumer |
* class using the store of choice (Auth_OpenID_FileStore or one of |
* class using the store of choice (Auth_OpenID_FileStore or one of |
* the SQL-based stores). If the application has a custom |
* the SQL-based stores). If the application has a custom |
* session-management implementation, an object implementing the |
* session-management implementation, an object implementing the |
* {@link Auth_Yadis_PHPSession} interface should be passed as the |
* {@link Auth_Yadis_PHPSession} interface should be passed as the |
* second parameter. Otherwise, the default uses $_SESSION. |
* second parameter. Otherwise, the default uses $_SESSION. |
* |
* |
* Next, the application should call the Auth_OpenID_Consumer object's |
* Next, the application should call the Auth_OpenID_Consumer object's |
* 'begin' method. This method takes the OpenID URL. The 'begin' |
* 'begin' method. This method takes the OpenID URL. The 'begin' |
* method returns an Auth_OpenID_AuthRequest object. |
* method returns an Auth_OpenID_AuthRequest object. |
* |
* |
* Next, the application should call the 'redirectURL' method of the |
* Next, the application should call the 'redirectURL' method of the |
* Auth_OpenID_AuthRequest object. The 'return_to' URL parameter is |
* Auth_OpenID_AuthRequest object. The 'return_to' URL parameter is |
* the URL that the OpenID server will send the user back to after |
* the URL that the OpenID server will send the user back to after |
* attempting to verify his or her identity. The 'trust_root' is the |
* attempting to verify his or her identity. The 'trust_root' is the |
* URL (or URL pattern) that identifies your web site to the user when |
* URL (or URL pattern) that identifies your web site to the user when |
* he or she is authorizing it. Send a redirect to the resulting URL |
* he or she is authorizing it. Send a redirect to the resulting URL |
* to the user's browser. |
* to the user's browser. |
* |
* |
* That's the first half of the authentication process. The second |
* That's the first half of the authentication process. The second |
* half of the process is done after the user's ID server sends the |
* half of the process is done after the user's ID server sends the |
* user's browser a redirect back to your site to complete their |
* user's browser a redirect back to your site to complete their |
* login. |
* login. |
* |
* |
* When that happens, the user will contact your site at the URL given |
* When that happens, the user will contact your site at the URL given |
* as the 'return_to' URL to the Auth_OpenID_AuthRequest::redirectURL |
* as the 'return_to' URL to the Auth_OpenID_AuthRequest::redirectURL |
* call made above. The request will have several query parameters |
* call made above. The request will have several query parameters |
* added to the URL by the identity server as the information |
* added to the URL by the identity server as the information |
* necessary to finish the request. |
* necessary to finish the request. |
* |
* |
* Lastly, instantiate an Auth_OpenID_Consumer instance as above and |
* Lastly, instantiate an Auth_OpenID_Consumer instance as above and |
* call its 'complete' method, passing in all the received query |
* call its 'complete' method, passing in all the received query |
* arguments. |
* arguments. |
* |
* |
* There are multiple possible return types possible from that |
* There are multiple possible return types possible from that |
* method. These indicate the whether or not the login was successful, |
* method. These indicate the whether or not the login was successful, |
* and include any additional information appropriate for their type. |
* and include any additional information appropriate for their type. |
* |
* |
* PHP versions 4 and 5 |
* PHP versions 4 and 5 |
* |
* |
* LICENSE: See the COPYING file included in this distribution. |
* LICENSE: See the COPYING file included in this distribution. |
* |
* |
* @package OpenID |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005-2008 Janrain, Inc. |
* @copyright 2005-2008 Janrain, Inc. |
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache |
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache |
*/ |
*/ |
|
|
/** |
/** |
* Require utility classes and functions for the consumer. |
* Require utility classes and functions for the consumer. |
*/ |
*/ |
require_once "Auth/OpenID.php"; |
require_once "Auth/OpenID.php"; |
require_once "Auth/OpenID/Message.php"; |
require_once "Auth/OpenID/Message.php"; |
require_once "Auth/OpenID/HMAC.php"; |
require_once "Auth/OpenID/HMAC.php"; |
require_once "Auth/OpenID/Association.php"; |
require_once "Auth/OpenID/Association.php"; |
require_once "Auth/OpenID/CryptUtil.php"; |
require_once "Auth/OpenID/CryptUtil.php"; |
require_once "Auth/OpenID/DiffieHellman.php"; |
require_once "Auth/OpenID/DiffieHellman.php"; |
require_once "Auth/OpenID/KVForm.php"; |
require_once "Auth/OpenID/KVForm.php"; |
require_once "Auth/OpenID/Nonce.php"; |
require_once "Auth/OpenID/Nonce.php"; |
require_once "Auth/OpenID/Discover.php"; |
require_once "Auth/OpenID/Discover.php"; |
require_once "Auth/OpenID/URINorm.php"; |
require_once "Auth/OpenID/URINorm.php"; |
require_once "Auth/Yadis/Manager.php"; |
require_once "Auth/Yadis/Manager.php"; |
require_once "Auth/Yadis/XRI.php"; |
require_once "Auth/Yadis/XRI.php"; |
|
|
/** |
/** |
* This is the status code returned when the complete method returns |
* This is the status code returned when the complete method returns |
* successfully. |
* successfully. |
*/ |
*/ |
define('Auth_OpenID_SUCCESS', 'success'); |
define('Auth_OpenID_SUCCESS', 'success'); |
|
|
/** |
/** |
* Status to indicate cancellation of OpenID authentication. |
* Status to indicate cancellation of OpenID authentication. |
*/ |
*/ |
define('Auth_OpenID_CANCEL', 'cancel'); |
define('Auth_OpenID_CANCEL', 'cancel'); |
|
|
/** |
/** |
* This is the status code completeAuth returns when the value it |
* This is the status code completeAuth returns when the value it |
* received indicated an invalid login. |
* received indicated an invalid login. |
*/ |
*/ |
define('Auth_OpenID_FAILURE', 'failure'); |
define('Auth_OpenID_FAILURE', 'failure'); |
|
|
/** |
/** |
* This is the status code completeAuth returns when the |
* This is the status code completeAuth returns when the |
* {@link Auth_OpenID_Consumer} instance is in immediate mode, and the |
* {@link Auth_OpenID_Consumer} instance is in immediate mode, and the |
* identity server sends back a URL to send the user to to complete his |
* identity server sends back a URL to send the user to to complete his |
* or her login. |
* or her login. |
*/ |
*/ |
define('Auth_OpenID_SETUP_NEEDED', 'setup needed'); |
define('Auth_OpenID_SETUP_NEEDED', 'setup needed'); |
|
|
/** |
/** |
* This is the status code beginAuth returns when the page fetched |
* This is the status code beginAuth returns when the page fetched |
* from the entered OpenID URL doesn't contain the necessary link tags |
* from the entered OpenID URL doesn't contain the necessary link tags |
* to function as an identity page. |
* to function as an identity page. |
*/ |
*/ |
define('Auth_OpenID_PARSE_ERROR', 'parse error'); |
define('Auth_OpenID_PARSE_ERROR', 'parse error'); |
|
|
/** |
/** |
* An OpenID consumer implementation that performs discovery and does |
* An OpenID consumer implementation that performs discovery and does |
* session management. See the Consumer.php file documentation for |
* session management. See the Consumer.php file documentation for |
* more information. |
* more information. |
* |
* |
* @package OpenID |
* @package OpenID |
*/ |
*/ |
class Auth_OpenID_Consumer { |
class Auth_OpenID_Consumer { |
|
|
/** |
/** |
* @access private |
* @access private |
*/ |
*/ |
var $discoverMethod = 'Auth_OpenID_discover'; |
var $discoverMethod = 'Auth_OpenID_discover'; |
|
|
/** |
/** |
* @access private |
* @access private |
*/ |
*/ |
var $session_key_prefix = "_openid_consumer_"; |
var $session_key_prefix = "_openid_consumer_"; |
|
|
/** |
/** |
* @access private |
* @access private |
*/ |
*/ |
var $_token_suffix = "last_token"; |
var $_token_suffix = "last_token"; |
|
|
/** |
/** |
* Initialize a Consumer instance. |
* Initialize a Consumer instance. |
* |
* |
* You should create a new instance of the Consumer object with |
* You should create a new instance of the Consumer object with |
* every HTTP request that handles OpenID transactions. |
* every HTTP request that handles OpenID transactions. |
* |
* |
* @param Auth_OpenID_OpenIDStore $store This must be an object |
* @param Auth_OpenID_OpenIDStore $store This must be an object |
* that implements the interface in {@link |
* that implements the interface in {@link |
* Auth_OpenID_OpenIDStore}. Several concrete implementations are |
* Auth_OpenID_OpenIDStore}. Several concrete implementations are |
* provided, to cover most common use cases. For stores backed by |
* provided, to cover most common use cases. For stores backed by |
* MySQL, PostgreSQL, or SQLite, see the {@link |
* MySQL, PostgreSQL, or SQLite, see the {@link |
* Auth_OpenID_SQLStore} class and its sublcasses. For a |
* Auth_OpenID_SQLStore} class and its sublcasses. For a |
* filesystem-backed store, see the {@link Auth_OpenID_FileStore} |
* filesystem-backed store, see the {@link Auth_OpenID_FileStore} |
* module. As a last resort, if it isn't possible for the server |
* module. As a last resort, if it isn't possible for the server |
* to store state at all, an instance of {@link |
* to store state at all, an instance of {@link |
* Auth_OpenID_DumbStore} can be used. |
* Auth_OpenID_DumbStore} can be used. |
* |
* |
* @param mixed $session An object which implements the interface |
* @param mixed $session An object which implements the interface |
* of the {@link Auth_Yadis_PHPSession} class. Particularly, this |
* of the {@link Auth_Yadis_PHPSession} class. Particularly, this |
* object is expected to have these methods: get($key), set($key), |
* object is expected to have these methods: get($key), set($key), |
* $value), and del($key). This defaults to a session object |
* $value), and del($key). This defaults to a session object |
* which wraps PHP's native session machinery. You should only |
* which wraps PHP's native session machinery. You should only |
* need to pass something here if you have your own sessioning |
* need to pass something here if you have your own sessioning |
* implementation. |
* implementation. |
* |
* |
* @param str $consumer_cls The name of the class to instantiate |
* @param str $consumer_cls The name of the class to instantiate |
* when creating the internal consumer object. This is used for |
* when creating the internal consumer object. This is used for |
* testing. |
* testing. |
*/ |
*/ |
function Auth_OpenID_Consumer($store, $session = null, |
function Auth_OpenID_Consumer($store, $session = null, |
$consumer_cls = null) |
$consumer_cls = null) |
{ |
{ |
if ($session === null) { |
if ($session === null) { |
$session = new Auth_Yadis_PHPSession(); |
$session = new Auth_Yadis_PHPSession(); |
} |
} |
|
|
$this->session = $session; |
$this->session = $session; |
|
|
if ($consumer_cls !== null) { |
if ($consumer_cls !== null) { |
$this->consumer = new $consumer_cls($store); |
$this->consumer = new $consumer_cls($store); |
} else { |
} else { |
$this->consumer = new Auth_OpenID_GenericConsumer($store); |
$this->consumer = new Auth_OpenID_GenericConsumer($store); |
} |
} |
|
|
$this->_token_key = $this->session_key_prefix . $this->_token_suffix; |
$this->_token_key = $this->session_key_prefix . $this->_token_suffix; |
} |
} |
|
|
/** |
/** |
* Used in testing to define the discovery mechanism. |
* Used in testing to define the discovery mechanism. |
* |
* |
* @access private |
* @access private |
*/ |
*/ |
function getDiscoveryObject($session, $openid_url, |
function getDiscoveryObject($session, $openid_url, |
$session_key_prefix) |
$session_key_prefix) |
{ |
{ |
return new Auth_Yadis_Discovery($session, $openid_url, |
return new Auth_Yadis_Discovery($session, $openid_url, |
$session_key_prefix); |
$session_key_prefix); |
} |
} |
|
|
/** |
/** |
* Start the OpenID authentication process. See steps 1-2 in the |
* Start the OpenID authentication process. See steps 1-2 in the |
* overview at the top of this file. |
* overview at the top of this file. |
* |
* |
* @param string $user_url Identity URL given by the user. This |
* @param string $user_url Identity URL given by the user. This |
* method performs a textual transformation of the URL to try and |
* method performs a textual transformation of the URL to try and |
* make sure it is normalized. For example, a user_url of |
* make sure it is normalized. For example, a user_url of |
* example.com will be normalized to http://example.com/ |
* example.com will be normalized to http://example.com/ |
* normalizing and resolving any redirects the server might issue. |
* normalizing and resolving any redirects the server might issue. |
* |
* |
* @param bool $anonymous True if the OpenID request is to be sent |
* @param bool $anonymous True if the OpenID request is to be sent |
* to the server without any identifier information. Use this |
* to the server without any identifier information. Use this |
* when you want to transport data but don't want to do OpenID |
* when you want to transport data but don't want to do OpenID |
* authentication with identifiers. |
* authentication with identifiers. |
* |
* |
* @return Auth_OpenID_AuthRequest $auth_request An object |
* @return Auth_OpenID_AuthRequest $auth_request An object |
* containing the discovered information will be returned, with a |
* containing the discovered information will be returned, with a |
* method for building a redirect URL to the server, as described |
* method for building a redirect URL to the server, as described |
* in step 3 of the overview. This object may also be used to add |
* in step 3 of the overview. This object may also be used to add |
* extension arguments to the request, using its 'addExtensionArg' |
* extension arguments to the request, using its 'addExtensionArg' |
* method. |
* method. |
*/ |
*/ |
function begin($user_url, $anonymous=false) |
function begin($user_url, $anonymous=false) |
{ |
{ |
$openid_url = $user_url; |
$openid_url = $user_url; |
|
|
$disco = $this->getDiscoveryObject($this->session, |
$disco = $this->getDiscoveryObject($this->session, |
$openid_url, |
$openid_url, |
$this->session_key_prefix); |
$this->session_key_prefix); |
|
|
// Set the 'stale' attribute of the manager. If discovery |
// Set the 'stale' attribute of the manager. If discovery |
// fails in a fatal way, the stale flag will cause the manager |
// fails in a fatal way, the stale flag will cause the manager |
// to be cleaned up next time discovery is attempted. |
// to be cleaned up next time discovery is attempted. |
|
|
$m = $disco->getManager(); |
$m = $disco->getManager(); |
$loader = new Auth_Yadis_ManagerLoader(); |
$loader = new Auth_Yadis_ManagerLoader(); |
|
|
if ($m) { |
if ($m) { |
if ($m->stale) { |
if ($m->stale) { |
$disco->destroyManager(); |
$disco->destroyManager(); |
} else { |
} else { |
$m->stale = true; |
$m->stale = true; |
<