* @copyright 2005 Janrain, Inc. * @license http://www.gnu.org/copyleft/lesser.html LGPL */ /** * 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"; define('AUTH_OPENID_HTTP_OK', 200); define('AUTH_OPENID_HTTP_REDIRECT', 302); define('AUTH_OPENID_HTTP_ERROR', 400); global $_Auth_OpenID_Request_Modes, $_Auth_OpenID_OpenID_Prefix, $_Auth_OpenID_Encode_Kvform, $_Auth_OpenID_Encode_Url; /** * @access private */ $_Auth_OpenID_Request_Modes = array('checkid_setup', 'checkid_immediate'); /** * @access private */ $_Auth_OpenID_OpenID_Prefix = "openid."; /** * @access private */ $_Auth_OpenID_Encode_Kvform = array('kfvorm'); /** * @access private */ $_Auth_OpenID_Encode_Url = array('URL/redirect'); /** * @access private */ function _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($query = null, $message = null) { $this->message = $message; $this->query = $query; } /** * Returns the return_to URL for the request which caused this * error. */ function hasReturnTo() { global $_Auth_OpenID_OpenID_Prefix; if ($this->query) { return array_key_exists($_Auth_OpenID_OpenID_Prefix . 'return_to', $this->query); } else { return false; } } /** * 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() { global $_Auth_OpenID_OpenID_Prefix; $return_to = Auth_OpenID::arrayGet($this->query, $_Auth_OpenID_OpenID_Prefix . 'return_to'); if (!$return_to) { return new Auth_OpenID_ServerError(null, "no return_to URL"); } return Auth_OpenID::appendArgs($return_to, array('openid.mode' => 'error', 'openid.error' => $this->toString())); } /** * 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())); } /** * 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_Encode_Url, $_Auth_OpenID_Encode_Kvform, $_Auth_OpenID_Request_Modes; if ($this->hasReturnTo()) { return $_Auth_OpenID_Encode_Url; } $mode = Auth_OpenID::arrayGet($this->query, 'openid.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->message) { return $this->message; } else { return get_class($this) . " error"; } } } /** * An error indicating that the return_to URL is malformed. * * @package OpenID */ class Auth_OpenID_MalformedReturnURL extends Auth_OpenID_ServerError { function Auth_OpenID_MalformedReturnURL($query, $return_to) { $this->return_to = $return_to; parent::Auth_OpenID_ServerError($query, "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 toString() { return "Malformed trust root"; } } /** * The base class for all server request classes. * * @access private * @package OpenID */ class Auth_OpenID_Request { var $mode = null; } /** * A request to verify the validity of a previous response. * * @access private * @package OpenID */ class Auth_OpenID_CheckAuthRequest extends Auth_OpenID_Request { var $mode = "check_authentication"; var $invalidate_handle = null; function Auth_OpenID_CheckAuthRequest($assoc_handle, $sig, $signed, $invalidate_handle = null) { $this->assoc_handle = $assoc_handle; $this->sig = $sig; $this->signed = $signed; if ($invalidate_handle !== null) { $this->invalidate_handle = $invalidate_handle; } } function fromQuery($query) { global $_Auth_OpenID_OpenID_Prefix; $required_keys = array('assoc_handle', 'sig', 'signed'); foreach ($required_keys as $k) { if (!array_key_exists($_Auth_OpenID_OpenID_Prefix . $k, $query)) { return new Auth_OpenID_ServerError($query, sprintf("%s request missing required parameter %s from \ query", "check_authentication", $k)); } } $assoc_handle = $query[$_Auth_OpenID_OpenID_Prefix . 'assoc_handle']; $sig = $query[$_Auth_OpenID_OpenID_Prefix . 'sig']; $signed_list = $query[$_Auth_OpenID_OpenID_Prefix . 'signed']; $signed_list = explode(",", $signed_list); $signed_pairs = array(); foreach ($signed_list as $field) { if ($field == 'mode') { // XXX KLUDGE HAX WEB PROTOCoL BR0KENNN // // openid.mode is currently check_authentication // because that's the mode of this request. But the // signature was made on something with a different // openid.mode. $value = "id_res"; } else { if (array_key_exists($_Auth_OpenID_OpenID_Prefix . $field, $query)) { $value = $query[$_Auth_OpenID_OpenID_Prefix . $field]; } else { return new Auth_OpenID_ServerError($query, sprintf("Couldn't find signed field %r in query %s", $field, var_export($query, true))); } } $signed_pairs[] = array($field, $value); } $result = new Auth_OpenID_CheckAuthRequest($assoc_handle, $sig, $signed_pairs); $result->invalidate_handle = Auth_OpenID::arrayGet($query, $_Auth_OpenID_OpenID_Prefix . 'invalidate_handle'); return $result; } function answer(&$signatory) { $is_valid = $signatory->verify($this->assoc_handle, $this->sig, $this->signed); // Now invalidate that assoc_handle so it this checkAuth // message cannot be replayed. $signatory->invalidate($this->assoc_handle, true); $response = new Auth_OpenID_ServerResponse($this); $response->fields['is_valid'] = $is_valid ? "true" : "false"; if ($this->invalidate_handle) { $assoc = $signatory->getAssociation($this->invalidate_handle, false); if (!$assoc) { $response->fields['invalidate_handle'] = $this->invalidate_handle; } } return $response; } } class Auth_OpenID_PlainTextServerSession { /** * An object that knows how to handle association requests with no * session type. */ var $session_type = 'plaintext'; function fromQuery($unused_request) { return new Auth_OpenID_PlainTextServerSession(); } function answer($secret) { return array('mac_key' => base64_encode($secret)); } } class Auth_OpenID_DiffieHellmanServerSession { /** * An object that knows how to handle association requests with * the Diffie-Hellman session type. */ var $session_type = 'DH-SHA1'; function Auth_OpenID_DiffieHellmanServerSession($dh, $consumer_pubkey) { $this->dh = $dh; $this->consumer_pubkey = $consumer_pubkey; } function fromQuery($query) { $dh_modulus = Auth_OpenID::arrayGet($query, 'openid.dh_modulus'); $dh_gen = Auth_OpenID::arrayGet($query, 'openid.dh_gen'); if ((($dh_modulus === null) && ($dh_gen !== null)) || (($dh_gen === null) && ($dh_modulus !== null))) { if ($dh_modulus === null) { $missing = 'modulus'; } else { $missing = 'generator'; } return new Auth_OpenID_ServerError( 'If non-default modulus or generator is '. 'supplied, both must be supplied. Missing '. $missing); } $lib =& Auth_OpenID_getMathLib(); if ($dh_modulus || $dh_gen) { $dh_modulus = $lib->base64ToLong($dh_modulus); $dh_gen = $lib->base64ToLong($dh_gen); if ($lib->cmp($dh_modulus, 0) == 0 || $lib->cmp($dh_gen, 0) == 0) { return new Auth_OpenID_ServerError( $query, "Failed to parse dh_mod or dh_gen"); } $dh = new Auth_OpenID_DiffieHellman($dh_modulus, $dh_gen); } else { $dh = new Auth_OpenID_DiffieHellman(); } $consumer_pubkey = Auth_OpenID::arrayGet($query, 'openid.dh_consumer_public'); if ($consumer_pubkey === null) { return new Auth_OpenID_ServerError( 'Public key for DH-SHA1 session '. 'not found in query'); } $consumer_pubkey = $lib->base64ToLong($consumer_pubkey); if ($consumer_pubkey === false) { return new Auth_OpenID_ServerError($query, "dh_consumer_public is not base64"); } return new Auth_OpenID_DiffieHellmanServerSession($dh, $consumer_pubkey); } function answer($secret) { $lib =& Auth_OpenID_getMathLib(); $mac_key = $this->dh->xorSecret($this->consumer_pubkey, $secret); return array( 'dh_server_public' => $lib->longToBase64($this->dh->public), 'enc_mac_key' => base64_encode($mac_key)); } } /** * A request to associate with the server. * * @access private * @package OpenID */ class Auth_OpenID_AssociateRequest extends Auth_OpenID_Request { var $mode = "associate"; var $assoc_type = 'HMAC-SHA1'; function Auth_OpenID_AssociateRequest(&$session) { $this->session =& $session; } function fromQuery($query) { global $_Auth_OpenID_OpenID_Prefix; $session_classes = array( 'DH-SHA1' => 'Auth_OpenID_DiffieHellmanServerSession', null => 'Auth_OpenID_PlainTextServerSession'); $session_type = null; if (array_key_exists($_Auth_OpenID_OpenID_Prefix . 'session_type', $query)) { $session_type = $query[$_Auth_OpenID_OpenID_Prefix . 'session_type']; } if (!array_key_exists($session_type, $session_classes)) { return new Auth_OpenID_ServerError($query, "Unknown session type $session_type"); } $session_cls = $session_classes[$session_type]; $session = call_user_func_array(array($session_cls, 'fromQuery'), array($query)); if (($session === null) || (_isError($session))) { return new Auth_OpenID_ServerError($query, "Error parsing $session_type session"); } return new Auth_OpenID_AssociateRequest($session); } function answer($assoc) { $ml =& Auth_OpenID_getMathLib(); $response = new Auth_OpenID_ServerResponse($this); $response->fields = array('expires_in' => $assoc->getExpiresIn(), 'assoc_type' => 'HMAC-SHA1', 'assoc_handle' => $assoc->handle); $r = $this->session->answer($assoc->secret); foreach ($r as $k => $v) { $response->fields[$k] = $v; } if ($this->session->session_type != 'plaintext') { $response->fields['session_type'] = $this->session->session_type; } return $response; } } /** * A request to confirm the identity of a user. * * @access private * @package OpenID */ class Auth_OpenID_CheckIDRequest extends Auth_OpenID_Request { var $mode = "checkid_setup"; // or "checkid_immediate" var $immediate = false; var $trust_root = null; function make($query, $identity, $return_to, $trust_root = null, $immediate = false, $assoc_handle = null) { if (!Auth_OpenID_TrustRoot::_parse($return_to)) { return new Auth_OpenID_MalformedReturnURL($query, $return_to); } $r = new Auth_OpenID_CheckIDRequest($identity, $return_to, $trust_root, $immediate, $assoc_handle); if (!$r->trustRootValid()) { return new Auth_OpenID_UntrustedReturnURL($return_to, $trust_root); } else { return $r; } } function Auth_OpenID_CheckIDRequest($identity, $return_to, $trust_root = null, $immediate = false, $assoc_handle = null) { $this->identity = $identity; $this->return_to = $return_to; $this->trust_root = $trust_root; $this->assoc_handle = $assoc_handle; if ($immediate) { $this->immediate = true; $this->mode = "checkid_immediate"; } else { $this->immediate = false; $this->mode = "checkid_setup"; } } function fromQuery($query) { global $_Auth_OpenID_OpenID_Prefix; $mode = $query[$_Auth_OpenID_OpenID_Prefix . 'mode']; $immediate = null; if ($mode == "checkid_immediate") { $immediate = true; $mode = "checkid_immediate"; } else { $immediate = false; $mode = "checkid_setup"; } $required = array('identity', 'return_to'); $optional = array('trust_root', 'assoc_handle'); $values = array(); foreach ($required as $field) { if (array_key_exists($_Auth_OpenID_OpenID_Prefix . $field, $query)) { $value = $query[$_Auth_OpenID_OpenID_Prefix . $field]; } else { return new Auth_OpenID_ServerError($query, sprintf("Missing required field %s from request", $field)); } $values[$field] = $value; } foreach ($optional as $field) { $value = null; if (array_key_exists($_Auth_OpenID_OpenID_Prefix . $field, $query)) { $value = $query[$_Auth_OpenID_OpenID_Prefix. $field]; } if ($value) { $values[$field] = $value; } } if (!Auth_OpenID_TrustRoot::_parse($values['return_to'])) { return new Auth_OpenID_MalformedReturnURL($query, $values['return_to']); } $obj = Auth_OpenID_CheckIDRequest::make($query, $values['identity'], $values['return_to'], Auth_OpenID::arrayGet($values, 'trust_root', null), $immediate); if (is_a($obj, 'Auth_OpenID_ServerError')) { return $obj; } if (Auth_OpenID::arrayGet($values, 'assoc_handle')) { $obj->assoc_handle = $values['assoc_handle']; } return $obj; } function trustRootValid() { if (!$this->trust_root) { return true; } $tr = Auth_OpenID_TrustRoot::_parse($this->trust_root); if ($tr === false) { return new Auth_OpenID_MalformedTrustRoot(null, $this->trust_root); } return Auth_OpenID_TrustRoot::match($this->trust_root, $this->return_to); } function answer($allow, $server_url = null) { if ($allow || $this->immediate) { $mode = 'id_res'; } else { $mode = 'cancel'; } $response = new Auth_OpenID_CheckIDResponse($this, $mode); if ($allow) { $response->fields['identity'] = $this->identity; $response->fields['return_to'] = $this->return_to; if (!$this->trustRootValid()) { return new Auth_OpenID_UntrustedReturnURL($this->return_to, $this->trust_root); } } else { $response->signed = array(); if ($this->immediate) { if (!$server_url) { return new Auth_OpenID_ServerError(null, 'setup_url is required for $allow=false \ in immediate mode.'); } $setup_request = new Auth_OpenID_CheckIDRequest( $this->identity, $this->return_to, $this->trust_root, false, $this->assoc_handle); $setup_url = $setup_request->encodeToURL($server_url); $response->fields['user_setup_url'] = $setup_url; } } return $response; } function encodeToURL($server_url) { global $_Auth_OpenID_OpenID_Prefix; // Imported from the alternate reality where these classes are // used in both the client and server code, so Requests are // Encodable too. That's right, code imported from alternate // realities all for the love of you, id_res/user_setup_url. $q = array('mode' => $this->mode, 'identity' => $this->identity, 'return_to' => $this->return_to); if ($this->trust_root) { $q['trust_root'] = $this->trust_root; } if ($this->assoc_handle) { $q['assoc_handle'] = $this->assoc_handle; } $_q = array(); foreach ($q as $k => $v) { $_q[$_Auth_OpenID_OpenID_Prefix . $k] = $v; } return Auth_OpenID::appendArgs($server_url, $_q); } function getCancelURL() { global $_Auth_OpenID_OpenID_Prefix; if ($this->immediate) { return new Auth_OpenID_ServerError(null, "Cancel is not an appropriate \ response to immediate mode \ requests."); } return Auth_OpenID::appendArgs($this->return_to, array($_Auth_OpenID_OpenID_Prefix . 'mode' => 'cancel')); } } /** * This class encapsulates the response to an OpenID server request. * * @access private * @package OpenID */ class Auth_OpenID_ServerResponse { function Auth_OpenID_ServerResponse($request) { $this->request = $request; $this->fields = array(); } function whichEncoding() { global $_Auth_OpenID_Encode_Kvform, $_Auth_OpenID_Request_Modes, $_Auth_OpenID_Encode_Url; if (in_array($this->request->mode, $_Auth_OpenID_Request_Modes)) { return $_Auth_OpenID_Encode_Url; } else { return $_Auth_OpenID_Encode_Kvform; } } function encodeToURL() { global $_Auth_OpenID_OpenID_Prefix; $fields = array(); foreach ($this->fields as $k => $v) { $fields[$_Auth_OpenID_OpenID_Prefix . $k] = $v; } return Auth_OpenID::appendArgs($this->request->return_to, $fields); } function encodeToKVForm() { return Auth_OpenID_KVForm::fromArray($this->fields); } } /** * A response to a checkid request. * * @access private * @package OpenID */ class Auth_OpenID_CheckIDResponse extends Auth_OpenID_ServerResponse { function Auth_OpenID_CheckIDResponse(&$request, $mode = 'id_res') { parent::Auth_OpenID_ServerResponse($request); $this->fields['mode'] = $mode; $this->signed = array(); if ($mode == 'id_res') { array_push($this->signed, 'mode', 'identity', 'return_to'); } } function addField($namespace, $key, $value, $signed = true) { if ($namespace) { $key = sprintf('%s.%s', $namespace, $key); } $this->fields[$key] = $value; if ($signed && !in_array($key, $this->signed)) { $this->signed[] = $key; } } function addFields($namespace, $fields, $signed = true) { foreach ($fields as $k => $v) { $this->addField($namespace, $k, $v, $signed); } } function update($namespace, $other) { $namespaced_fields = array(); foreach ($other->fields as $k => $v) { $name = sprintf('%s.%s', $namespace, $k); $namespaced_fields[$name] = $v; } $this->fields = array_merge($this->fields, $namespaced_fields); $this->signed = array_merge($this->signed, $other->signed); } } /** * A web-capable response object which you can use to generate a * user-agent response. * * @package OpenID */ class Auth_OpenID_WebResponse { var $code = AUTH_OPENID_HTTP_OK; var $body = ""; function Auth_OpenID_WebResponse($code = null, $headers = null, $body = null) { if ($code) { $this->code = $code; } if ($headers !== null) { $this->headers = $headers; } else { $this->headers = array(); } if ($body !== null) { $this->body = $body; } } } /** * Responsible for the signature of query data and the verification of * OpenID signature values. * * @package OpenID */ class Auth_OpenID_Signatory { // = 14 * 24 * 60 * 60; # 14 days, in seconds var $SECRET_LIFETIME = 1209600; // keys have a bogus server URL in them because the filestore // really does expect that key to be a URL. This seems a little // silly for the server store, since I expect there to be only one // server URL. var $normal_key = 'http://localhost/|normal'; var $dumb_key = 'http://localhost/|dumb'; /** * Create a new signatory using a given store. */ function Auth_OpenID_Signatory(&$store) { // assert store is not None $this->store =& $store; } /** * Verify, using a given association handle, a signature with * signed key-value pairs from an HTTP request. */ function verify($assoc_handle, $sig, $signed_pairs) { $assoc = $this->getAssociation($assoc_handle, true); if (!$assoc) { // oidutil.log("failed to get assoc with handle %r to verify sig %r" // % (assoc_handle, sig)) return false; } $expected_sig = base64_encode($assoc->sign($signed_pairs)); return $sig == $expected_sig; } /** * Given a response, sign the fields in the response's 'signed' * list, and insert the signature into the response. */ function sign($response) { $signed_response = $response; $assoc_handle = $response->request->assoc_handle; if ($assoc_handle) { // normal mode $assoc = $this->getAssociation($assoc_handle, false); if (!$assoc) { // fall back to dumb mode $signed_response->fields['invalidate_handle'] = $assoc_handle; $assoc = $this->createAssociation(true); } } else { // dumb mode. $assoc = $this->createAssociation(true); } $signed_response->fields['assoc_handle'] = $assoc->handle; $assoc->addSignature($signed_response->signed, $signed_response->fields, ''); return $signed_response; } /** * Make a new association. */ function createAssociation($dumb = true, $assoc_type = 'HMAC-SHA1') { $secret = Auth_OpenID_CryptUtil::getBytes(20); $uniq = base64_encode(Auth_OpenID_CryptUtil::getBytes(4)); $handle = sprintf('{%s}{%x}{%s}', $assoc_type, intval(time()), $uniq); $assoc = Auth_OpenID_Association::fromExpiresIn( $this->SECRET_LIFETIME, $handle, $secret, $assoc_type); if ($dumb) { $key = $this->dumb_key; } else { $key = $this->normal_key; } $this->store->storeAssociation($key, $assoc); return $assoc; } /** * Given an association handle, get the association from the * store, or return a ServerError or null if something goes wrong. */ function getAssociation($assoc_handle, $dumb) { if ($assoc_handle === null) { return new Auth_OpenID_ServerError(null, "assoc_handle must not be null"); } if ($dumb) { $key = $this->dumb_key; } else { $key = $this->normal_key; } $assoc = $this->store->getAssociation($key, $assoc_handle); if (($assoc !== null) && ($assoc->getExpiresIn() <= 0)) { $this->store->removeAssociation($key, $assoc_handle); $assoc = null; } return $assoc; } /** * Invalidate a given association handle. */ function invalidate($assoc_handle, $dumb) { if ($dumb) { $key = $this->dumb_key; } else { $key = $this->normal_key; } $this->store->removeAssociation($key, $assoc_handle); } } /** * Encode an Auth_OpenID_Response to an Auth_OpenID_WebResponse. * * @package OpenID */ class Auth_OpenID_Encoder { var $responseFactory = 'Auth_OpenID_WebResponse'; /** * Encode an Auth_OpenID_Response and return an * Auth_OpenID_WebResponse. */ function encode(&$response) { global $_Auth_OpenID_Encode_Kvform, $_Auth_OpenID_Encode_Url; $cls = $this->responseFactory; $encode_as = $response->whichEncoding(); if ($encode_as == $_Auth_OpenID_Encode_Kvform) { $wr = new $cls(null, null, $response->encodeToKVForm()); if (is_a($response, 'Auth_OpenID_ServerError')) { $wr->code = AUTH_OPENID_HTTP_ERROR; } } else if ($encode_as == $_Auth_OpenID_Encode_Url) { $location = $response->encodeToURL(); $wr = new $cls(AUTH_OPENID_HTTP_REDIRECT, array('location' => $location)); } else { return new Auth_OpenID_EncodingError($response); } return $wr; } } /** * Returns true if the given response needs a signature. * * @access private */ function needsSigning($response) { return (in_array($response->request->mode, array('checkid_setup', 'checkid_immediate')) && $response->signed); } /** * An encoder which also takes care of signing fields when required. * * @package OpenID */ class Auth_OpenID_SigningEncoder extends Auth_OpenID_Encoder { function Auth_OpenID_SigningEncoder(&$signatory) { $this->signatory =& $signatory; } /** * Sign an Auth_OpenID_Response and return an * Auth_OpenID_WebResponse. */ function encode(&$response) { // the isinstance is a bit of a kludge... it means there isn't // really an adapter to make the interfaces quite match. if (!is_a($response, 'Auth_OpenID_ServerError') && needsSigning($response)) { if (!$this->signatory) { return new Auth_OpenID_ServerError(null, "Must have a store to sign request"); } if (array_key_exists('sig', $response->fields)) { return new Auth_OpenID_AlreadySigned($response); } $response = $this->signatory->sign($response); } return parent::encode($response); } } /** * Decode an incoming Auth_OpenID_WebResponse into an * Auth_OpenID_Request. * * @package OpenID */ class Auth_OpenID_Decoder { function Auth_OpenID_Decoder() { global $_Auth_OpenID_OpenID_Prefix; $this->prefix = $_Auth_OpenID_OpenID_Prefix; $this->handlers = array( 'checkid_setup' => 'Auth_OpenID_CheckIDRequest', 'checkid_immediate' => 'Auth_OpenID_CheckIDRequest', 'check_authentication' => 'Auth_OpenID_CheckAuthRequest', 'associate' => 'Auth_OpenID_AssociateRequest' ); } /** * Given an HTTP query in an array (key-value pairs), decode it * into an Auth_OpenID_Request object. */ function decode($query) { if (!$query) { return null; } $myquery = array(); foreach ($query as $k => $v) { if (strpos($k, $this->prefix) === 0) { $myquery[$k] = $v; } } if (!$myquery) { return null; } $mode = Auth_OpenID::arrayGet($myquery, $this->prefix . 'mode'); if (!$mode) { return new Auth_OpenID_ServerError($query, sprintf("No %s mode found in query", $this->prefix)); } $handlerCls = Auth_OpenID::arrayGet($this->handlers, $mode, $this->defaultDecoder($query)); if (!is_a($handlerCls, 'Auth_OpenID_ServerError')) { return call_user_func_array(array($handlerCls, 'fromQuery'), array($query)); } else { return $handlerCls; } } function defaultDecoder($query) { $mode = $query[$this->prefix . 'mode']; return new Auth_OpenID_ServerError($query, sprintf("No decoder for mode %s", $mode)); } } /** * An error that indicates an encoding problem occurred. * * @package OpenID */ class Auth_OpenID_EncodingError { function Auth_OpenID_EncodingError(&$response) { $this->response =& $response; } } /** * An error that indicates that a response was already signed. * * @package OpenID */ class Auth_OpenID_AlreadySigned extends Auth_OpenID_EncodingError { // This response is already signed. } /** * An error that indicates that the given return_to is not under the * given trust_root. * * @package OpenID */ class Auth_OpenID_UntrustedReturnURL extends Auth_OpenID_ServerError { function Auth_OpenID_UntrustedReturnURL($return_to, $trust_root) { global $_Auth_OpenID_OpenID_Prefix; $query = array( $_Auth_OpenID_OpenID_Prefix . 'return_to' => $return_to, $_Auth_OpenID_OpenID_Prefix . 'trust_root' => $trust_root); parent::Auth_OpenID_ServerError($query); } function toString() { global $_Auth_OpenID_OpenID_Prefix; $return_to = $this->query[$_Auth_OpenID_OpenID_Prefix . 'return_to']; $trust_root = $this->query[$_Auth_OpenID_OpenID_Prefix . 'trust_root']; return sprintf("return_to %s not under trust_root %s", $return_to, $trust_root); } } /** * An object that implements the OpenID protocol for a single URL. * * Use this object by calling getOpenIDResponse when you get any * request for the server URL. * * @package OpenID */ class Auth_OpenID_Server { function Auth_OpenID_Server(&$store) { $this->store =& $store; $this->signatory = new Auth_OpenID_Signatory($this->store); $this->encoder = new Auth_OpenID_SigningEncoder($this->signatory); $this->decoder = new Auth_OpenID_Decoder(); } /** * Handle a request. Given an Auth_OpenID_Request object, call * the appropriate Auth_OpenID_Server method to process the * request and generate a response. * * @param Auth_OpenID_Request $request An Auth_OpenID_Request * returned by Auth_OpenID_Server::decodeRequest. * * @return Auth_OpenID_Response $response A response object * capable of generating a user-agent reply. */ function handleRequest($request) { if (method_exists($this, "openid_" . $request->mode)) { $handler = array($this, "openid_" . $request->mode); return call_user_func($handler, $request); } return null; } /** * The callback for 'check_authentication' messages. * * @access private */ function openid_check_authentication(&$request) { return $request->answer($this->signatory); } /** * The callback for 'associate' messages. * * @access private */ function openid_associate(&$request) { $assoc = $this->signatory->createAssociation(false); return $request->answer($assoc); } /** * Encodes as response in the appropriate format suitable for * sending to the user agent. */ function encodeResponse(&$response) { return $this->encoder->encode($response); } /** * Decodes a query args array into the appropriate * Auth_OpenID_Request object. */ function decodeRequest(&$query) { return $this->decoder->decode($query); } } ?>