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