<?php |
<?php |
|
|
/** |
/** |
* OpenID server protocol and logic. |
* OpenID server protocol and logic. |
* |
* |
* Overview |
* Overview |
* |
* |
* An OpenID server must perform three tasks: |
* An OpenID server must perform three tasks: |
* |
* |
* 1. Examine the incoming request to determine its nature and validity. |
* 1. Examine the incoming request to determine its nature and validity. |
* 2. Make a decision about how to respond to this request. |
* 2. Make a decision about how to respond to this request. |
* 3. Format the response according to the protocol. |
* 3. Format the response according to the protocol. |
* |
* |
* The first and last of these tasks may performed by the {@link |
* The first and last of these tasks may performed by the {@link |
* Auth_OpenID_Server::decodeRequest()} and {@link |
* Auth_OpenID_Server::decodeRequest()} and {@link |
* Auth_OpenID_Server::encodeResponse} methods. Who gets to do the |
* Auth_OpenID_Server::encodeResponse} methods. Who gets to do the |
* intermediate task -- deciding how to respond to the request -- will |
* intermediate task -- deciding how to respond to the request -- will |
* depend on what type of request it is. |
* depend on what type of request it is. |
* |
* |
* If it's a request to authenticate a user (a 'checkid_setup' or |
* If it's a request to authenticate a user (a 'checkid_setup' or |
* 'checkid_immediate' request), you need to decide if you will assert |
* 'checkid_immediate' request), you need to decide if you will assert |
* that this user may claim the identity in question. Exactly how you |
* that this user may claim the identity in question. Exactly how you |
* do that is a matter of application policy, but it generally |
* do that is a matter of application policy, but it generally |
* involves making sure the user has an account with your system and |
* involves making sure the user has an account with your system and |
* is logged in, checking to see if that identity is hers to claim, |
* is logged in, checking to see if that identity is hers to claim, |
* and verifying with the user that she does consent to releasing that |
* and verifying with the user that she does consent to releasing that |
* information to the party making the request. |
* information to the party making the request. |
* |
* |
* Examine the properties of the {@link Auth_OpenID_CheckIDRequest} |
* Examine the properties of the {@link Auth_OpenID_CheckIDRequest} |
* object, and if and when you've come to a decision, form a response |
* object, and if and when you've come to a decision, form a response |
* by calling {@link Auth_OpenID_CheckIDRequest::answer()}. |
* by calling {@link Auth_OpenID_CheckIDRequest::answer()}. |
* |
* |
* Other types of requests relate to establishing associations between |
* Other types of requests relate to establishing associations between |
* client and server and verifing the authenticity of previous |
* client and server and verifing the authenticity of previous |
* communications. {@link Auth_OpenID_Server} contains all the logic |
* communications. {@link Auth_OpenID_Server} contains all the logic |
* and data necessary to respond to such requests; just pass it to |
* and data necessary to respond to such requests; just pass it to |
* {@link Auth_OpenID_Server::handleRequest()}. |
* {@link Auth_OpenID_Server::handleRequest()}. |
* |
* |
* OpenID Extensions |
* OpenID Extensions |
* |
* |
* Do you want to provide other information for your users in addition |
* Do you want to provide other information for your users in addition |
* to authentication? Version 1.2 of the OpenID protocol allows |
* to authentication? Version 1.2 of the OpenID protocol allows |
* consumers to add extensions to their requests. For example, with |
* consumers to add extensions to their requests. For example, with |
* sites using the Simple Registration |
* sites using the Simple Registration |
* Extension |
* Extension |
* (http://openid.net/specs/openid-simple-registration-extension-1_0.html), |
* (http://openid.net/specs/openid-simple-registration-extension-1_0.html), |
* a user can agree to have their nickname and e-mail address sent to |
* a user can agree to have their nickname and e-mail address sent to |
* a site when they sign up. |
* a site when they sign up. |
* |
* |
* Since extensions do not change the way OpenID authentication works, |
* Since extensions do not change the way OpenID authentication works, |
* code to handle extension requests may be completely separate from |
* code to handle extension requests may be completely separate from |
* the {@link Auth_OpenID_Request} class here. But you'll likely want |
* the {@link Auth_OpenID_Request} class here. But you'll likely want |
* data sent back by your extension to be signed. {@link |
* data sent back by your extension to be signed. {@link |
* Auth_OpenID_ServerResponse} provides methods with which you can add |
* Auth_OpenID_ServerResponse} provides methods with which you can add |
* data to it which can be signed with the other data in the OpenID |
* data to it which can be signed with the other data in the OpenID |
* signature. |
* signature. |
* |
* |
* For example: |
* For example: |
* |
* |
* <pre> // when request is a checkid_* request |
* <pre> // when request is a checkid_* request |
* $response = $request->answer(true); |
* $response = $request->answer(true); |
* // this will a signed 'openid.sreg.timezone' parameter to the response |
* // this will a signed 'openid.sreg.timezone' parameter to the response |
* response.addField('sreg', 'timezone', 'America/Los_Angeles')</pre> |
* response.addField('sreg', 'timezone', 'America/Los_Angeles')</pre> |
* |
* |
* Stores |
* Stores |
* |
* |
* The OpenID server needs to maintain state between requests in order |
* The OpenID server needs to maintain state between requests in order |
* to function. Its mechanism for doing this is called a store. The |
* to function. Its mechanism for doing this is called a store. The |
* store interface is defined in Interface.php. Additionally, several |
* store interface is defined in Interface.php. Additionally, several |
* concrete store implementations are provided, so that most sites |
* concrete store implementations are provided, so that most sites |
* won't need to implement a custom store. For a store backed by flat |
* won't need to implement a custom store. For a store backed by flat |
* files on disk, see {@link Auth_OpenID_FileStore}. For stores based |
* files on disk, see {@link Auth_OpenID_FileStore}. For stores based |
* on MySQL, SQLite, or PostgreSQL, see the {@link |
* on MySQL, SQLite, or PostgreSQL, see the {@link |
* Auth_OpenID_SQLStore} subclasses. |
* Auth_OpenID_SQLStore} subclasses. |
* |
* |
* Upgrading |
* Upgrading |
* |
* |
* The keys by which a server looks up associations in its store have |
* The keys by which a server looks up associations in its store have |
* changed in version 1.2 of this library. If your store has entries |
* changed in version 1.2 of this library. If your store has entries |
* created from version 1.0 code, you should empty it. |
* created from version 1.0 code, you should empty it. |
* |
* |
* 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 |
*/ |
*/ |
|
|
/** |
/** |
* Required imports |
* Required imports |
*/ |
*/ |
require_once "Auth/OpenID.php"; |
require_once "Auth/OpenID.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/BigMath.php"; |
require_once "Auth/OpenID/BigMath.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/TrustRoot.php"; |
require_once "Auth/OpenID/TrustRoot.php"; |
require_once "Auth/OpenID/ServerRequest.php"; |
require_once "Auth/OpenID/ServerRequest.php"; |
require_once "Auth/OpenID/Message.php"; |
require_once "Auth/OpenID/Message.php"; |
require_once "Auth/OpenID/Nonce.php"; |
require_once "Auth/OpenID/Nonce.php"; |
|
|
define('AUTH_OPENID_HTTP_OK', 200); |
define('AUTH_OPENID_HTTP_OK', 200); |
define('AUTH_OPENID_HTTP_REDIRECT', 302); |
define('AUTH_OPENID_HTTP_REDIRECT', 302); |
define('AUTH_OPENID_HTTP_ERROR', 400); |
define('AUTH_OPENID_HTTP_ERROR', 400); |
|
|
/** |
/** |
* @access private |
* @access private |
*/ |
*/ |
global $_Auth_OpenID_Request_Modes; |
global $_Auth_OpenID_Request_Modes; |
$_Auth_OpenID_Request_Modes = array('checkid_setup', |
$_Auth_OpenID_Request_Modes = array('checkid_setup', |
'checkid_immediate'); |
'checkid_immediate'); |
|
|
/** |
/** |
* @access private |
* @access private |
*/ |
*/ |
define('Auth_OpenID_ENCODE_KVFORM', 'kfvorm'); |
define('Auth_OpenID_ENCODE_KVFORM', 'kfvorm'); |
|
|
/** |
/** |
* @access private |
* @access private |
*/ |
*/ |
define('Auth_OpenID_ENCODE_URL', 'URL/redirect'); |
define('Auth_OpenID_ENCODE_URL', 'URL/redirect'); |
|
|
/** |
/** |
* @access private |
* @access private |
*/ |
*/ |
define('Auth_OpenID_ENCODE_HTML_FORM', 'HTML form'); |
define('Auth_OpenID_ENCODE_HTML_FORM', 'HTML form'); |
|
|
/** |
/** |
* @access private |
* @access private |
*/ |
*/ |
function Auth_OpenID_isError($obj, $cls = 'Auth_OpenID_ServerError') |
function Auth_OpenID_isError($obj, $cls = 'Auth_OpenID_ServerError') |
{ |
{ |
return is_a($obj, $cls); |
return is_a($obj, $cls); |
} |
} |
|
|
/** |
/** |
* An error class which gets instantiated and returned whenever an |
* An error class which gets instantiated and returned whenever an |
* OpenID protocol error occurs. Be prepared to use this in place of |
* OpenID protocol error occurs. Be prepared to use this in place of |
* an ordinary server response. |
* an ordinary server response. |
* |
* |
* @package OpenID |
* @package OpenID |
*/ |
*/ |
class Auth_OpenID_ServerError { |
class Auth_OpenID_ServerError { |
/** |
/** |
* @access private |
* @access private |
*/ |
*/ |
function Auth_OpenID_ServerError($message = null, $text = null, |
function Auth_OpenID_ServerError($message = null, $text = null, |
$reference = null, $contact = null) |
$reference = null, $contact = null) |
{ |
{ |
$this->message = $message; |
$this->message = $message; |
$this->text = $text; |
$this->text = $text; |
$this->contact = $contact; |
$this->contact = $contact; |
$this->reference = $reference; |
$this->reference = $reference; |
} |
} |
|
|
function getReturnTo() |
function getReturnTo() |
{ |
{ |
if ($this->message && |
if ($this->message && |
$this->message->hasKey(Auth_OpenID_OPENID_NS, 'return_to')) { |
$this->message->hasKey(Auth_OpenID_OPENID_NS, 'return_to')) { |
return $this->message->getArg(Auth_OpenID_OPENID_NS, |
return $this->message->getArg(Auth_OpenID_OPENID_NS, |
'return_to'); |
'return_to'); |
} else { |
} else { |
return null; |
return null; |
} |
} |
} |
} |
|
|
/** |
/** |
* Returns the return_to URL for the request which caused this |
* Returns the return_to URL for the request which caused this |
* error. |
* error. |
*/ |
*/ |
function hasReturnTo() |
function hasReturnTo() |
{ |
{ |
return $this->getReturnTo() !== null; |
return $this->getReturnTo() !== null; |
} |
} |
|
|
/** |
/** |
* Encodes this error's response as a URL suitable for |
* Encodes this error's response as a URL suitable for |
* redirection. If the response has no return_to, another |
* redirection. If the response has no return_to, another |
* Auth_OpenID_ServerError is returned. |
* Auth_OpenID_ServerError is returned. |
*/ |
*/ |
function encodeToURL() |
function encodeToURL() |
{ |
{ |
if (!$this->message) { |
if (!$this->message) { |
return null; |
return null; |
} |
} |
|
|
$msg = $this->toMessage(); |
$msg = $this->toMessage(); |
return $msg->toURL($this->getReturnTo()); |
return $msg->toURL($this->getReturnTo()); |
} |
} |
|
|
/** |
/** |
* Encodes the response to key-value form. This is a |
* Encodes the response to key-value form. This is a |
* machine-readable format used to respond to messages which came |
* machine-readable format used to respond to messages which came |
* directly from the consumer and not through the user-agent. See |
* directly from the consumer and not through the user-agent. See |
* the OpenID specification. |
* the OpenID specification. |
*/ |
*/ |
function encodeToKVForm() |
function encodeToKVForm() |
{ |
{ |
return Auth_OpenID_KVForm::fromArray( |
return Auth_OpenID_KVForm::fromArray( |
array('mode' => 'error', |
array('mode' => 'error', |
'error' => $this->toString())); |
'error' => $this->toString())); |
} |
} |
|
|
function toFormMarkup($form_tag_attrs=null) |
function toFormMarkup($form_tag_attrs=null) |
{ |
{ |
$msg = $this->toMessage(); |
$msg = $this->toMessage(); |
return $msg->toFormMarkup($this->getReturnTo(), $form_tag_attrs); |
return $msg->toFormMarkup($this->getReturnTo(), $form_tag_attrs); |
} |
} |
|
|
function toHTML($form_tag_attrs=null) |
function toHTML($form_tag_attrs=null) |
{ |
{ |
return Auth_OpenID::autoSubmitHTML( |
return Auth_OpenID::autoSubmitHTML( |
$this->toFormMarkup($form_tag_attrs)); |
$this->toFormMarkup($form_tag_attrs)); |
} |
} |
|
|
function toMessage() |
function toMessage() |
{ |
{ |
// Generate a Message object for sending to the relying party, |
// Generate a Message object for sending to the relying party, |
// after encoding. |
// after encoding. |
$namespace = $this->message->getOpenIDNamespace(); |
$namespace = $this->message->getOpenIDNamespace(); |
$reply = new Auth_OpenID_Message($namespace); |
$reply = new Auth_OpenID_Message($namespace); |
$reply->setArg(Auth_OpenID_OPENID_NS, 'mode', 'error'); |
$reply->setArg(Auth_OpenID_OPENID_NS, 'mode', 'error'); |
$reply->setArg(Auth_OpenID_OPENID_NS, 'error', $this->toString()); |
$reply->setArg(Auth_OpenID_OPENID_NS, 'error', $this->toString()); |
|
|
if ($this->contact !== null) { |
if ($this->contact !== null) { |
$reply->setArg(Auth_OpenID_OPENID_NS, 'contact', $this->contact); |
$reply->setArg(Auth_OpenID_OPENID_NS, 'contact', $this->contact); |
} |
} |
|
|
if ($this->reference !== null) { |
if ($this->reference !== null) { |
$reply->setArg(Auth_OpenID_OPENID_NS, 'reference', |
$reply->setArg(Auth_OpenID_OPENID_NS, 'reference', |
$this->reference); |
$this->reference); |
} |
} |
|
|
return $reply; |
return $reply; |
} |
} |
|
|
/** |
/** |
* Returns one of Auth_OpenID_ENCODE_URL, |
* Returns one of Auth_OpenID_ENCODE_URL, |
* Auth_OpenID_ENCODE_KVFORM, or null, depending on the type of |
* Auth_OpenID_ENCODE_KVFORM, or null, depending on the type of |
* encoding expected for this error's payload. |
* encoding expected for this error's payload. |
*/ |
*/ |
function whichEncoding() |
function whichEncoding() |
{ |
{ |
global $_Auth_OpenID_Request_Modes; |
global $_Auth_OpenID_Request_Modes; |
|
|
if ($this->hasReturnTo()) { |
if ($this->hasReturnTo()) { |
if ($this->message->isOpenID2() && |
if ($this->message->isOpenID2() && |
(strlen($this->encodeToURL()) > |
(strlen($this->encodeToURL()) > |
Auth_OpenID_OPENID1_URL_LIMIT)) { |
Auth_OpenID_OPENID1_URL_LIMIT)) { |
return Auth_OpenID_ENCODE_HTML_FORM; |
return Auth_OpenID_ENCODE_HTML_FORM; |
} else { |
} else { |
return Auth_OpenID_ENCODE_URL; |
return Auth_OpenID_ENCODE_URL; |
} |
} |
} |
} |
|
|
if (!$this->message) { |
if (!$this->message) { |
return null; |
return null; |
} |
} |
|
|
$mode = $this->message->getArg(Auth_OpenID_OPENID_NS, |
$mode = $this->message->getArg(Auth_OpenID_OPENID_NS, |
'mode'); |
'mode'); |
|
|
if ($mode) { |
if ($mode) { |
if (!in_array($mode, $_Auth_OpenID_Request_Modes)) { |
if (!in_array($mode, $_Auth_OpenID_Request_Modes)) { |
return Auth_OpenID_ENCODE_KVFORM; |
return Auth_OpenID_ENCODE_KVFORM; |
} |
} |
} |
} |
return null; |
return null; |
} |
} |
|
|
/** |
/** |
* Returns this error message. |
* Returns this error message. |
*/ |
*/ |
function toString() |
function toString() |
{ |
{ |
if ($this->text) { |
if ($this->text) { |
return $this->text; |
return $this->text; |
} else { |
} else { |
return get_class($this) . " error"; |
return get_class($this) . " error"; |
} |
} |
} |
} |
} |
} |
|
|
/** |
/** |
* Error returned by the server code when a return_to is absent from a |
* Error returned by the server code when a return_to is absent from a |
* request. |
* request. |
* |
* |
* @package OpenID |
* @package OpenID |
*/ |
*/ |
class Auth_OpenID_NoReturnToError extends Auth_OpenID_ServerError { |
class Auth_OpenID_NoReturnToError extends Auth_OpenID_ServerError { |
function Auth_OpenID_NoReturnToError($message = null, |
function Auth_OpenID_NoReturnToError($message = null, |
$text = "No return_to URL available") |
$text = "No return_to URL available") |
{ |
{ |
parent::Auth_OpenID_ServerError($message, $text); |
parent::Auth_OpenID_ServerError($message, $text); |
} |
} |
|
|
function toString() |
function toString() |
{ |
{ |
return "No return_to available"; |
return "No return_to available"; |
} |
} |
} |
} |
|
|
/** |
/** |
* An error indicating that the return_to URL is malformed. |
* An error indicating that the return_to URL is malformed. |
* |
* |
* @package OpenID |
* @package OpenID |
*/ |
*/ |
class Auth_OpenID_MalformedReturnURL extends Auth_OpenID_ServerError { |
class Auth_OpenID_MalformedReturnURL extends Auth_OpenID_ServerError { |
function Auth_OpenID_MalformedReturnURL($message, $return_to) |
function Auth_OpenID_MalformedReturnURL($message, $return_to) |
{ |
{ |
$this->return_to = $return_to; |
$this->return_to = $return_to; |
parent::Auth_OpenID_ServerError($message, "malformed return_to URL"); |
parent::Auth_OpenID_ServerError($message, "malformed return_to URL"); |
} |
} |
} |
} |
|
|
/** |
/** |
* This error is returned when the trust_root value is malformed. |
* This error is returned when the trust_root value is malformed. |
* |
* |
* @package OpenID |
* @package OpenID |
*/ |
*/ |
class Auth_OpenID_MalformedTrustRoot extends Auth_OpenID_ServerError { |
class Auth_OpenID_MalformedTrustRoot extends Auth_OpenID_ServerError { |
function Auth_OpenID_MalformedTrustRoot($message = null, |
function Auth_OpenID_MalformedTrustRoot($message = null, |
$text = "Malformed trust root") |
$text = "Malformed trust root") |
{ |
{ |
parent::Auth_OpenID_ServerError($message, $text); |
parent::Auth_OpenID_ServerError($message, $text); |
} |
} |
|
|
function toString() |
function toString() |
{ |
{ |
return "Malformed trust root"; |
return "Malformed trust root"; |
} |
} |
} |
} |
|
|
/** |
/** |
* The base class for all server request classes. |
* The base class for all server request classes. |
* |
* |
* @package OpenID |
* @package OpenID |
*/ |
*/ |
class Auth_OpenID_Request { |
class Auth_OpenID_Request { |
var $mode = null; |
var $mode = null; |
} |
} |
|
|
/** |
/** |
* A request to verify the validity of a previous response. |
* A request to verify the validity of a previous response. |
* |
* |
* @package OpenID |
* @package OpenID |
*/ |
*/ |
class Auth_OpenID_CheckAuthRequest extends Auth_OpenID_Request { |
class Auth_OpenID_CheckAuthRequest extends Auth_OpenID_Request { |
var $mode = "check_authentication"; |
var $mode = "check_authentication"; |
var $invalidate_handle = null; |
var $invalidate_handle = null; |
|
|
function Auth_OpenID_CheckAuthRequest($assoc_handle, $signed, |
function Auth_OpenID_CheckAuthRequest($assoc_handle, $signed, |
$invalidate_handle = null) |
$invalidate_handle = null) |
{ |
{ |
$this->assoc_handle = $assoc_handle; |
$this->assoc_handle = $assoc_handle; |
$this->signed = $signed; |
$this->signed = $signed; |
if ($invalidate_handle !== null) { |
if ($invalidate_handle !== null) { |
$this->invalidate_handle = $invalidate_handle; |
$this->invalidate_handle = $invalidate_handle; |
} |
} |
$this->namespace = Auth_OpenID_OPENID2_NS; |
$this->namespace = Auth_OpenID_OPENID2_NS; |
$this->message = null; |
$this->message = null; |
} |
} |
|
|
static function fromMessage($message, $server=null) |
static function fromMessage($message, $server=null) |
{ |
{ |
$required_keys = array('assoc_handle', 'sig', 'signed'); |
$required_keys = array('assoc_handle', 'sig', 'signed'); |
|
|
foreach ($required_keys as $k) { |
foreach ($required_keys as $k) { |