ILIAS  Release_5_0_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
Consumer.php
Go to the documentation of this file.
1 <?php
2 
163 require_once "Auth/OpenID.php";
164 require_once "Auth/OpenID/Message.php";
165 require_once "Auth/OpenID/HMAC.php";
166 require_once "Auth/OpenID/Association.php";
167 require_once "Auth/OpenID/CryptUtil.php";
168 require_once "Auth/OpenID/DiffieHellman.php";
169 require_once "Auth/OpenID/KVForm.php";
170 require_once "Auth/OpenID/Nonce.php";
171 require_once "Auth/OpenID/Discover.php";
172 require_once "Auth/OpenID/URINorm.php";
173 require_once "Auth/Yadis/Manager.php";
174 require_once "Auth/Yadis/XRI.php";
175 
180 define('Auth_OpenID_SUCCESS', 'success');
181 
185 define('Auth_OpenID_CANCEL', 'cancel');
186 
191 define('Auth_OpenID_FAILURE', 'failure');
192 
199 define('Auth_OpenID_SETUP_NEEDED', 'setup needed');
200 
206 define('Auth_OpenID_PARSE_ERROR', 'parse error');
207 
216 
220  var $discoverMethod = 'Auth_OpenID_discover';
221 
225  var $session_key_prefix = "_openid_consumer_";
226 
230  var $_token_suffix = "last_token";
231 
261  function Auth_OpenID_Consumer($store, $session = null,
262  $consumer_cls = null)
263  {
264  if ($session === null) {
265  $session = new Auth_Yadis_PHPSession();
266  }
267 
268  $this->session = $session;
269 
270  if ($consumer_cls !== null) {
271  $this->consumer = new $consumer_cls($store);
272  } else {
273  $this->consumer = new Auth_OpenID_GenericConsumer($store);
274  }
275 
276  $this->_token_key = $this->session_key_prefix . $this->_token_suffix;
277  }
278 
284  function getDiscoveryObject($session, $openid_url,
286  {
287  return new Auth_Yadis_Discovery($session, $openid_url,
289  }
290 
313  function begin($user_url, $anonymous=false)
314  {
315  $openid_url = $user_url;
316 
317  $disco = $this->getDiscoveryObject($this->session,
318  $openid_url,
319  $this->session_key_prefix);
320 
321  // Set the 'stale' attribute of the manager. If discovery
322  // fails in a fatal way, the stale flag will cause the manager
323  // to be cleaned up next time discovery is attempted.
324 
325  $m = $disco->getManager();
327 
328  if ($m) {
329  if ($m->stale) {
330  $disco->destroyManager();
331  } else {
332  $m->stale = true;
333  $disco->session->set($disco->session_key,
334  serialize($loader->toSession($m)));
335  }
336  }
337 
338  $endpoint = $disco->getNextService($this->discoverMethod,
339  $this->consumer->fetcher);
340 
341  // Reset the 'stale' attribute of the manager.
342  $m = $disco->getManager();
343  if ($m) {
344  $m->stale = false;
345  $disco->session->set($disco->session_key,
346  serialize($loader->toSession($m)));
347  }
348 
349  if ($endpoint === null) {
350  return null;
351  } else {
352  return $this->beginWithoutDiscovery($endpoint,
353  $anonymous);
354  }
355  }
356 
373  function beginWithoutDiscovery($endpoint, $anonymous=false)
374  {
376  $auth_req = $this->consumer->begin($endpoint);
377  $this->session->set($this->_token_key,
378  $loader->toSession($auth_req->endpoint));
379  if (!$auth_req->setAnonymous($anonymous)) {
380  return new Auth_OpenID_FailureResponse(null,
381  "OpenID 1 requests MUST include the identifier " .
382  "in the request.");
383  }
384  return $auth_req;
385  }
386 
410  function complete($current_url, $query=null)
411  {
412  if ($current_url && !is_string($current_url)) {
413  // This is ugly, but we need to complain loudly when
414  // someone uses the API incorrectly.
415  trigger_error("current_url must be a string; see NEWS file " .
416  "for upgrading notes.",
417  E_USER_ERROR);
418  }
419 
420  if ($query === null) {
422  }
423 
425  $endpoint_data = $this->session->get($this->_token_key);
426  $endpoint =
427  $loader->fromSession($endpoint_data);
428 
430  $response = $this->consumer->complete($message, $endpoint,
431  $current_url);
432  $this->session->del($this->_token_key);
433 
434  if (in_array($response->status, array(Auth_OpenID_SUCCESS,
435  Auth_OpenID_CANCEL))) {
436  if ($response->identity_url !== null) {
437  $disco = $this->getDiscoveryObject($this->session,
438  $response->identity_url,
439  $this->session_key_prefix);
440  $disco->cleanup(true);
441  }
442  }
443 
444  return $response;
445  }
446 }
447 
454  var $session_type = 'DH-SHA1';
455  var $hash_func = 'Auth_OpenID_SHA1';
456  var $secret_size = 20;
457  var $allowed_assoc_types = array('HMAC-SHA1');
458 
460  {
461  if ($dh === null) {
462  $dh = new Auth_OpenID_DiffieHellman();
463  }
464 
465  $this->dh = $dh;
466  }
467 
468  function getRequest()
469  {
470  $math = Auth_OpenID_getMathLib();
471 
472  $cpub = $math->longToBase64($this->dh->public);
473 
474  $args = array('dh_consumer_public' => $cpub);
475 
476  if (!$this->dh->usingDefaultValues()) {
477  $args = array_merge($args, array(
478  'dh_modulus' =>
479  $math->longToBase64($this->dh->mod),
480  'dh_gen' =>
481  $math->longToBase64($this->dh->gen)));
482  }
483 
484  return $args;
485  }
486 
487  function extractSecret($response)
488  {
489  if (!$response->hasKey(Auth_OpenID_OPENID_NS,
490  'dh_server_public')) {
491  return null;
492  }
493 
494  if (!$response->hasKey(Auth_OpenID_OPENID_NS,
495  'enc_mac_key')) {
496  return null;
497  }
498 
499  $math = Auth_OpenID_getMathLib();
500 
501  $spub = $math->base64ToLong($response->getArg(Auth_OpenID_OPENID_NS,
502  'dh_server_public'));
503  $enc_mac_key = base64_decode($response->getArg(Auth_OpenID_OPENID_NS,
504  'enc_mac_key'));
505 
506  return $this->dh->xorSecret($spub, $enc_mac_key, $this->hash_func);
507  }
508 }
509 
517  var $session_type = 'DH-SHA256';
518  var $hash_func = 'Auth_OpenID_SHA256';
519  var $secret_size = 32;
520  var $allowed_assoc_types = array('HMAC-SHA256');
521 }
522 
529  var $session_type = 'no-encryption';
530  var $allowed_assoc_types = array('HMAC-SHA1', 'HMAC-SHA256');
531 
532  function getRequest()
533  {
534  return array();
535  }
536 
537  function extractSecret($response)
538  {
539  if (!$response->hasKey(Auth_OpenID_OPENID_NS, 'mac_key')) {
540  return null;
541  }
542 
543  return base64_decode($response->getArg(Auth_OpenID_OPENID_NS,
544  'mac_key'));
545  }
546 }
547 
552 {
553  $types = array(
554  'no-encryption' => 'Auth_OpenID_PlainTextConsumerSession',
555  'DH-SHA1' => 'Auth_OpenID_DiffieHellmanSHA1ConsumerSession',
556  'DH-SHA256' => 'Auth_OpenID_DiffieHellmanSHA256ConsumerSession');
557 
558  return $types;
559 }
560 
572  var $discoverMethod = 'Auth_OpenID_discover';
573 
577  var $store;
578 
583 
587  var $openid1_nonce_query_arg_name = 'janrain_nonce';
588 
594  var $openid1_return_to_identifier_name = 'openid1_claimed_id';
595 
615  {
616  $this->store = $store;
617  $this->negotiator = Auth_OpenID_getDefaultNegotiator();
618  $this->_use_assocs = (is_null($this->store) ? false : true);
619 
620  $this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
621 
622  $this->session_types = Auth_OpenID_getAvailableSessionTypes();
623  }
624 
631  function begin($service_endpoint)
632  {
633  $assoc = $this->_getAssociation($service_endpoint);
634  $r = new Auth_OpenID_AuthRequest($service_endpoint, $assoc);
635  $r->return_to_args[$this->openid1_nonce_query_arg_name] =
637 
638  if ($r->message->isOpenID1()) {
639  $r->return_to_args[$this->openid1_return_to_identifier_name] =
640  $r->endpoint->claimed_id;
641  }
642 
643  return $r;
644  }
645 
653  function complete($message, $endpoint, $return_to)
654  {
655  $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode',
656  '<no mode set>');
657 
658  $mode_methods = array(
659  'cancel' => '_complete_cancel',
660  'error' => '_complete_error',
661  'setup_needed' => '_complete_setup_needed',
662  'id_res' => '_complete_id_res',
663  );
664 
665  $method = Auth_OpenID::arrayGet($mode_methods, $mode,
666  '_completeInvalid');
667 
668  return call_user_func_array(array($this, $method),
669  array($message, &$endpoint, $return_to));
670  }
671 
675  function _completeInvalid($message, $endpoint, $unused)
676  {
677  $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode',
678  '<No mode set>');
679 
680  return new Auth_OpenID_FailureResponse($endpoint,
681  sprintf("Invalid openid.mode '%s'", $mode));
682  }
683 
687  function _complete_cancel($message, $endpoint, $unused)
688  {
689  return new Auth_OpenID_CancelResponse($endpoint);
690  }
691 
695  function _complete_error($message, $endpoint, $unused)
696  {
697  $error = $message->getArg(Auth_OpenID_OPENID_NS, 'error');
698  $contact = $message->getArg(Auth_OpenID_OPENID_NS, 'contact');
699  $reference = $message->getArg(Auth_OpenID_OPENID_NS, 'reference');
700 
701  return new Auth_OpenID_FailureResponse($endpoint, $error,
702  $contact, $reference);
703  }
704 
708  function _complete_setup_needed($message, $endpoint, $unused)
709  {
710  if (!$message->isOpenID2()) {
711  return $this->_completeInvalid($message, $endpoint);
712  }
713 
714  $user_setup_url = $message->getArg(Auth_OpenID_OPENID2_NS,
715  'user_setup_url');
716  return new Auth_OpenID_SetupNeededResponse($endpoint, $user_setup_url);
717  }
718 
722  function _complete_id_res($message, $endpoint, $return_to)
723  {
724  $user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS,
725  'user_setup_url');
726 
727  if ($this->_checkSetupNeeded($message)) {
729  $endpoint, $user_setup_url);
730  } else {
731  return $this->_doIdRes($message, $endpoint, $return_to);
732  }
733  }
734 
738  function _checkSetupNeeded($message)
739  {
740  // In OpenID 1, we check to see if this is a cancel from
741  // immediate mode by the presence of the user_setup_url
742  // parameter.
743  if ($message->isOpenID1()) {
744  $user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS,
745  'user_setup_url');
746  if ($user_setup_url !== null) {
747  return true;
748  }
749  }
750 
751  return false;
752  }
753 
757  function _doIdRes($message, $endpoint, $return_to)
758  {
759  // Checks for presence of appropriate fields (and checks
760  // signed list fields)
761  $result = $this->_idResCheckForFields($message);
762 
764  return $result;
765  }
766 
767  if (!$this->_checkReturnTo($message, $return_to)) {
768  return new Auth_OpenID_FailureResponse(null,
769  sprintf("return_to does not match return URL. Expected %s, got %s",
770  $return_to,
771  $message->getArg(Auth_OpenID_OPENID_NS, 'return_to')));
772  }
773 
774  // Verify discovery information:
775  $result = $this->_verifyDiscoveryResults($message, $endpoint);
776 
778  return $result;
779  }
780 
781  $endpoint = $result;
782 
783  $result = $this->_idResCheckSignature($message,
784  $endpoint->server_url);
785 
787  return $result;
788  }
789 
790  $result = $this->_idResCheckNonce($message, $endpoint);
791 
793  return $result;
794  }
795 
796  $signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS, 'signed',
798  if (Auth_OpenID::isFailure($signed_list_str)) {
799  return $signed_list_str;
800  }
801  $signed_list = explode(',', $signed_list_str);
802 
803  $signed_fields = Auth_OpenID::addPrefix($signed_list, "openid.");
804 
805  return new Auth_OpenID_SuccessResponse($endpoint, $message,
806  $signed_fields);
807 
808  }
809 
813  function _checkReturnTo($message, $return_to)
814  {
815  // Check an OpenID message and its openid.return_to value
816  // against a return_to URL from an application. Return True
817  // on success, False on failure.
818 
819  // Check the openid.return_to args against args in the
820  // original message.
822  $message->toPostArgs());
824  return false;
825  }
826 
827  // Check the return_to base URL against the one in the
828  // message.
829  $msg_return_to = $message->getArg(Auth_OpenID_OPENID_NS,
830  'return_to');
831  if (Auth_OpenID::isFailure($return_to)) {
832  // XXX log me
833  return false;
834  }
835 
836  $return_to_parts = parse_url(Auth_OpenID_urinorm($return_to));
837  $msg_return_to_parts = parse_url(Auth_OpenID_urinorm($msg_return_to));
838 
839  // If port is absent from both, add it so it's equal in the
840  // check below.
841  if ((!array_key_exists('port', $return_to_parts)) &&
842  (!array_key_exists('port', $msg_return_to_parts))) {
843  $return_to_parts['port'] = null;
844  $msg_return_to_parts['port'] = null;
845  }
846 
847  // If path is absent from both, add it so it's equal in the
848  // check below.
849  if ((!array_key_exists('path', $return_to_parts)) &&
850  (!array_key_exists('path', $msg_return_to_parts))) {
851  $return_to_parts['path'] = null;
852  $msg_return_to_parts['path'] = null;
853  }
854 
855  // The URL scheme, authority, and path MUST be the same
856  // between the two URLs.
857  foreach (array('scheme', 'host', 'port', 'path') as $component) {
858  // If the url component is absent in either URL, fail.
859  // There should always be a scheme, host, port, and path.
860  if (!array_key_exists($component, $return_to_parts)) {
861  return false;
862  }
863 
864  if (!array_key_exists($component, $msg_return_to_parts)) {
865  return false;
866  }
867 
868  if (Auth_OpenID::arrayGet($return_to_parts, $component) !==
869  Auth_OpenID::arrayGet($msg_return_to_parts, $component)) {
870  return false;
871  }
872  }
873 
874  return true;
875  }
876 
881  {
882  // Verify that the arguments in the return_to URL are present in this
883  // response.
884 
886  $return_to = $message->getArg(Auth_OpenID_OPENID_NS, 'return_to');
887 
888  if (Auth_OpenID::isFailure($return_to)) {
889  return $return_to;
890  }
891  // XXX: this should be checked by _idResCheckForFields
892  if (!$return_to) {
893  return new Auth_OpenID_FailureResponse(null,
894  "Response has no return_to");
895  }
896 
897  $parsed_url = parse_url($return_to);
898 
899  $q = array();
900  if (array_key_exists('query', $parsed_url)) {
901  $rt_query = $parsed_url['query'];
902  $q = Auth_OpenID::parse_str($rt_query);
903  }
904 
905  foreach ($q as $rt_key => $rt_value) {
906  if (!array_key_exists($rt_key, $query)) {
907  return new Auth_OpenID_FailureResponse(null,
908  sprintf("return_to parameter %s absent from query", $rt_key));
909  } else {
910  $value = $query[$rt_key];
911  if ($rt_value != $value) {
912  return new Auth_OpenID_FailureResponse(null,
913  sprintf("parameter %s value %s does not match " .
914  "return_to value %s", $rt_key,
915  $value, $rt_value));
916  }
917  }
918  }
919 
920  // Make sure all non-OpenID arguments in the response are also
921  // in the signed return_to.
922  $bare_args = $message->getArgs(Auth_OpenID_BARE_NS);
923  foreach ($bare_args as $key => $value) {
924  if (Auth_OpenID::arrayGet($q, $key) != $value) {
925  return new Auth_OpenID_FailureResponse(null,
926  sprintf("Parameter %s = %s not in return_to URL",
927  $key, $value));
928  }
929  }
930 
931  return true;
932  }
933 
937  function _idResCheckSignature($message, $server_url)
938  {
939  $assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS,
940  'assoc_handle');
941  if (Auth_OpenID::isFailure($assoc_handle)) {
942  return $assoc_handle;
943  }
944 
945  $assoc = $this->store->getAssociation($server_url, $assoc_handle);
946 
947  if ($assoc) {
948  if ($assoc->getExpiresIn() <= 0) {
949  // XXX: It might be a good idea sometimes to re-start
950  // the authentication with a new association. Doing it
951  // automatically opens the possibility for
952  // denial-of-service by a server that just returns
953  // expired associations (or really short-lived
954  // associations)
955  return new Auth_OpenID_FailureResponse(null,
956  'Association with ' . $server_url . ' expired');
957  }
958 
959  if (!$assoc->checkMessageSignature($message)) {
960  return new Auth_OpenID_FailureResponse(null,
961  "Bad signature");
962  }
963  } else {
964  // It's not an association we know about. Stateless mode
965  // is our only possible path for recovery. XXX - async
966  // framework will not want to block on this call to
967  // _checkAuth.
968  if (!$this->_checkAuth($message, $server_url)) {
969  return new Auth_OpenID_FailureResponse(null,
970  "Server denied check_authentication");
971  }
972  }
973 
974  return null;
975  }
976 
980  function _verifyDiscoveryResults($message, $endpoint=null)
981  {
982  if ($message->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS) {
983  return $this->_verifyDiscoveryResultsOpenID2($message,
984  $endpoint);
985  } else {
986  return $this->_verifyDiscoveryResultsOpenID1($message,
987  $endpoint);
988  }
989  }
990 
994  function _verifyDiscoveryResultsOpenID1($message, $endpoint)
995  {
996  $claimed_id = $message->getArg(Auth_OpenID_BARE_NS,
997  $this->openid1_return_to_identifier_name);
998 
999  if (($endpoint === null) && ($claimed_id === null)) {
1000  return new Auth_OpenID_FailureResponse($endpoint,
1001  'When using OpenID 1, the claimed ID must be supplied, ' .
1002  'either by passing it through as a return_to parameter ' .
1003  'or by using a session, and supplied to the GenericConsumer ' .
1004  'as the argument to complete()');
1005  } else if (($endpoint !== null) && ($claimed_id === null)) {
1006  $claimed_id = $endpoint->claimed_id;
1007  }
1008 
1009  $to_match = new Auth_OpenID_ServiceEndpoint();
1010  $to_match->type_uris = array(Auth_OpenID_TYPE_1_1);
1011  $to_match->local_id = $message->getArg(Auth_OpenID_OPENID1_NS,
1012  'identity');
1013 
1014  // Restore delegate information from the initiation phase
1015  $to_match->claimed_id = $claimed_id;
1016 
1017  if ($to_match->local_id === null) {
1018  return new Auth_OpenID_FailureResponse($endpoint,
1019  "Missing required field openid.identity");
1020  }
1021 
1022  $to_match_1_0 = $to_match->copy();
1023  $to_match_1_0->type_uris = array(Auth_OpenID_TYPE_1_0);
1024 
1025  if ($endpoint !== null) {
1026  $result = $this->_verifyDiscoverySingle($endpoint, $to_match);
1027 
1028  if (is_a($result, 'Auth_OpenID_TypeURIMismatch')) {
1029  $result = $this->_verifyDiscoverySingle($endpoint,
1030  $to_match_1_0);
1031  }
1032 
1034  // oidutil.log("Error attempting to use stored
1035  // discovery information: " + str(e))
1036  // oidutil.log("Attempting discovery to
1037  // verify endpoint")
1038  } else {
1039  return $endpoint;
1040  }
1041  }
1042 
1043  // Endpoint is either bad (failed verification) or None
1044  return $this->_discoverAndVerify($to_match->claimed_id,
1045  array($to_match, $to_match_1_0));
1046  }
1047 
1051  function _verifyDiscoverySingle($endpoint, $to_match)
1052  {
1053  // Every type URI that's in the to_match endpoint has to be
1054  // present in the discovered endpoint.
1055  foreach ($to_match->type_uris as $type_uri) {
1056  if (!$endpoint->usesExtension($type_uri)) {
1057  return new Auth_OpenID_TypeURIMismatch($endpoint,
1058  "Required type ".$type_uri." not present");
1059  }
1060  }
1061 
1062  // Fragments do not influence discovery, so we can't compare a
1063  // claimed identifier with a fragment to discovered
1064  // information.
1065  list($defragged_claimed_id, $_) =
1066  Auth_OpenID::urldefrag($to_match->claimed_id);
1067 
1068  if ($defragged_claimed_id != $endpoint->claimed_id) {
1069  return new Auth_OpenID_FailureResponse($endpoint,
1070  sprintf('Claimed ID does not match (different subjects!), ' .
1071  'Expected %s, got %s', $defragged_claimed_id,
1072  $endpoint->claimed_id));
1073  }
1074 
1075  if ($to_match->getLocalID() != $endpoint->getLocalID()) {
1076  return new Auth_OpenID_FailureResponse($endpoint,
1077  sprintf('local_id mismatch. Expected %s, got %s',
1078  $to_match->getLocalID(), $endpoint->getLocalID()));
1079  }
1080 
1081  // If the server URL is None, this must be an OpenID 1
1082  // response, because op_endpoint is a required parameter in
1083  // OpenID 2. In that case, we don't actually care what the
1084  // discovered server_url is, because signature checking or
1085  // check_auth should take care of that check for us.
1086  if ($to_match->server_url === null) {
1087  if ($to_match->preferredNamespace() != Auth_OpenID_OPENID1_NS) {
1088  return new Auth_OpenID_FailureResponse($endpoint,
1089  "Preferred namespace mismatch (bug)");
1090  }
1091  } else if ($to_match->server_url != $endpoint->server_url) {
1092  return new Auth_OpenID_FailureResponse($endpoint,
1093  sprintf('OP Endpoint mismatch. Expected %s, got %s',
1094  $to_match->server_url, $endpoint->server_url));
1095  }
1096 
1097  return null;
1098  }
1099 
1103  function _verifyDiscoveryResultsOpenID2($message, $endpoint)
1104  {
1105  $to_match = new Auth_OpenID_ServiceEndpoint();
1106  $to_match->type_uris = array(Auth_OpenID_TYPE_2_0);
1107  $to_match->claimed_id = $message->getArg(Auth_OpenID_OPENID2_NS,
1108  'claimed_id');
1109 
1110  $to_match->local_id = $message->getArg(Auth_OpenID_OPENID2_NS,
1111  'identity');
1112 
1113  $to_match->server_url = $message->getArg(Auth_OpenID_OPENID2_NS,
1114  'op_endpoint');
1115 
1116  if ($to_match->server_url === null) {
1117  return new Auth_OpenID_FailureResponse($endpoint,
1118  "OP Endpoint URL missing");
1119  }
1120 
1121  // claimed_id and identifier must both be present or both be
1122  // absent
1123  if (($to_match->claimed_id === null) &&
1124  ($to_match->local_id !== null)) {
1125  return new Auth_OpenID_FailureResponse($endpoint,
1126  'openid.identity is present without openid.claimed_id');
1127  }
1128 
1129  if (($to_match->claimed_id !== null) &&
1130  ($to_match->local_id === null)) {
1131  return new Auth_OpenID_FailureResponse($endpoint,
1132  'openid.claimed_id is present without openid.identity');
1133  }
1134 
1135  if ($to_match->claimed_id === null) {
1136  // This is a response without identifiers, so there's
1137  // really no checking that we can do, so return an
1138  // endpoint that's for the specified `openid.op_endpoint'
1140  $to_match->server_url);
1141  }
1142 
1143  if (!$endpoint) {
1144  // The claimed ID doesn't match, so we have to do
1145  // discovery again. This covers not using sessions, OP
1146  // identifier endpoints and responses that didn't match
1147  // the original request.
1148  // oidutil.log('No pre-discovered information supplied.')
1149  return $this->_discoverAndVerify($to_match->claimed_id,
1150  array($to_match));
1151  } else {
1152 
1153  // The claimed ID matches, so we use the endpoint that we
1154  // discovered in initiation. This should be the most
1155  // common case.
1156  $result = $this->_verifyDiscoverySingle($endpoint, $to_match);
1157 
1159  $endpoint = $this->_discoverAndVerify($to_match->claimed_id,
1160  array($to_match));
1161  if (Auth_OpenID::isFailure($endpoint)) {
1162  return $endpoint;
1163  }
1164  }
1165  }
1166 
1167  // The endpoint we return should have the claimed ID from the
1168  // message we just verified, fragment and all.
1169  if ($endpoint->claimed_id != $to_match->claimed_id) {
1170  $endpoint->claimed_id = $to_match->claimed_id;
1171  }
1172 
1173  return $endpoint;
1174  }
1175 
1179  function _discoverAndVerify($claimed_id, $to_match_endpoints)
1180  {
1181  // oidutil.log('Performing discovery on %s' % (claimed_id,))
1182  list($unused, $services) = call_user_func($this->discoverMethod,
1183  $claimed_id,
1184  $this->fetcher); // fixed php 5.4 compatability
1185 
1186  if (!$services) {
1187  return new Auth_OpenID_FailureResponse(null,
1188  sprintf("No OpenID information found at %s",
1189  $claimed_id));
1190  }
1191 
1192  return $this->_verifyDiscoveryServices($claimed_id, $services,
1193  $to_match_endpoints);
1194  }
1195 
1199  function _verifyDiscoveryServices($claimed_id,
1200  $services, $to_match_endpoints)
1201  {
1202  // Search the services resulting from discovery to find one
1203  // that matches the information from the assertion
1204 
1205  foreach ($services as $endpoint) {
1206  foreach ($to_match_endpoints as $to_match_endpoint) {
1207  $result = $this->_verifyDiscoverySingle($endpoint,
1208  $to_match_endpoint);
1209 
1211  // It matches, so discover verification has
1212  // succeeded. Return this endpoint.
1213  return $endpoint;
1214  }
1215  }
1216  }
1217 
1218  return new Auth_OpenID_FailureResponse(null,
1219  sprintf('No matching endpoint found after discovering %s: %s',
1220  $claimed_id, $result->message));
1221  }
1222 
1234  function _idResGetNonceOpenID1($message, $endpoint)
1235  {
1236  return $message->getArg(Auth_OpenID_BARE_NS,
1237  $this->openid1_nonce_query_arg_name);
1238  }
1239 
1243  function _idResCheckNonce($message, $endpoint)
1244  {
1245  if ($message->isOpenID1()) {
1246  // This indicates that the nonce was generated by the consumer
1247  $nonce = $this->_idResGetNonceOpenID1($message, $endpoint);
1248  $server_url = '';
1249  } else {
1250  $nonce = $message->getArg(Auth_OpenID_OPENID2_NS,
1251  'response_nonce');
1252 
1253  $server_url = $endpoint->server_url;
1254  }
1255 
1256  if ($nonce === null) {
1257  return new Auth_OpenID_FailureResponse($endpoint,
1258  "Nonce missing from response");
1259  }
1260 
1261  $parts = Auth_OpenID_splitNonce($nonce);
1262 
1263  if ($parts === null) {
1264  return new Auth_OpenID_FailureResponse($endpoint,
1265  "Malformed nonce in response");
1266  }
1267 
1268  list($timestamp, $salt) = $parts;
1269 
1270  if (!$this->store->useNonce($server_url, $timestamp, $salt)) {
1271  return new Auth_OpenID_FailureResponse($endpoint,
1272  "Nonce already used or out of range");
1273  }
1274 
1275  return null;
1276  }
1277 
1281  function _idResCheckForFields($message)
1282  {
1283  $basic_fields = array('return_to', 'assoc_handle', 'sig', 'signed');
1284  $basic_sig_fields = array('return_to', 'identity');
1285 
1286  $require_fields = array(
1287  Auth_OpenID_OPENID2_NS => array_merge($basic_fields,
1288  array('op_endpoint')),
1289 
1290  Auth_OpenID_OPENID1_NS => array_merge($basic_fields,
1291  array('identity'))
1292  );
1293 
1294  $require_sigs = array(
1295  Auth_OpenID_OPENID2_NS => array_merge($basic_sig_fields,
1296  array('response_nonce',
1297  'claimed_id',
1298  'assoc_handle',
1299  'op_endpoint')),
1300  Auth_OpenID_OPENID1_NS => array_merge($basic_sig_fields,
1301  array('nonce'))
1302  );
1303 
1304  foreach ($require_fields[$message->getOpenIDNamespace()] as $field) {
1305  if (!$message->hasKey(Auth_OpenID_OPENID_NS, $field)) {
1306  return new Auth_OpenID_FailureResponse(null,
1307  "Missing required field '".$field."'");
1308  }
1309  }
1310 
1311  $signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS,
1312  'signed',
1314  if (Auth_OpenID::isFailure($signed_list_str)) {
1315  return $signed_list_str;
1316  }
1317  $signed_list = explode(',', $signed_list_str);
1318 
1319  foreach ($require_sigs[$message->getOpenIDNamespace()] as $field) {
1320  // Field is present and not in signed list
1321  if ($message->hasKey(Auth_OpenID_OPENID_NS, $field) &&
1322  (!in_array($field, $signed_list))) {
1323  return new Auth_OpenID_FailureResponse(null,
1324  "'".$field."' not signed");
1325  }
1326  }
1327 
1328  return null;
1329  }
1330 
1334  function _checkAuth($message, $server_url)
1335  {
1336  $request = $this->_createCheckAuthRequest($message);
1337  if ($request === null) {
1338  return false;
1339  }
1340 
1341  $resp_message = $this->_makeKVPost($request, $server_url);
1342  if (($resp_message === null) ||
1343  (is_a($resp_message, 'Auth_OpenID_ServerErrorContainer'))) {
1344  return false;
1345  }
1346 
1347  return $this->_processCheckAuthResponse($resp_message, $server_url);
1348  }
1349 
1353  function _createCheckAuthRequest($message)
1354  {
1355  $signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed');
1356  if ($signed) {
1357  foreach (explode(',', $signed) as $k) {
1358  $value = $message->getAliasedArg($k);
1359  if ($value === null) {
1360  return null;
1361  }
1362  }
1363  }
1364  $ca_message = $message->copy();
1365  $ca_message->setArg(Auth_OpenID_OPENID_NS, 'mode',
1366  'check_authentication');
1367  return $ca_message;
1368  }
1369 
1373  function _processCheckAuthResponse($response, $server_url)
1374  {
1375  $is_valid = $response->getArg(Auth_OpenID_OPENID_NS, 'is_valid',
1376  'false');
1377 
1378  $invalidate_handle = $response->getArg(Auth_OpenID_OPENID_NS,
1379  'invalidate_handle');
1380 
1381  if ($invalidate_handle !== null) {
1382  $this->store->removeAssociation($server_url,
1383  $invalidate_handle);
1384  }
1385 
1386  if ($is_valid == 'true') {
1387  return true;
1388  }
1389 
1390  return false;
1391  }
1392 
1400  static function _httpResponseToMessage($response, $server_url)
1401  {
1402  // Should this function be named Message.fromHTTPResponse instead?
1403  $response_message = Auth_OpenID_Message::fromKVForm($response->body);
1404 
1405  if ($response->status == 400) {
1407  $response_message);
1408  } else if ($response->status != 200 and $response->status != 206) {
1409  return null;
1410  }
1411 
1412  return $response_message;
1413  }
1414 
1418  function _makeKVPost($message, $server_url)
1419  {
1420  $body = $message->toURLEncoded();
1421  $resp = $this->fetcher->post($server_url, $body);
1422 
1423  if ($resp === null) {
1424  return null;
1425  }
1426 
1427  return $this->_httpResponseToMessage($resp, $server_url);
1428  }
1429 
1433  function _getAssociation($endpoint)
1434  {
1435  if (!$this->_use_assocs) {
1436  return null;
1437  }
1438 
1439  $assoc = $this->store->getAssociation($endpoint->server_url);
1440 
1441  if (($assoc === null) ||
1442  ($assoc->getExpiresIn() <= 0)) {
1443 
1444  $assoc = $this->_negotiateAssociation($endpoint);
1445 
1446  if ($assoc !== null) {
1447  $this->store->storeAssociation($endpoint->server_url,
1448  $assoc);
1449  }
1450  }
1451 
1452  return $assoc;
1453  }
1454 
1464  function _extractSupportedAssociationType($server_error, $endpoint,
1465  $assoc_type)
1466  {
1467  // Any error message whose code is not 'unsupported-type'
1468  // should be considered a total failure.
1469  if (($server_error->error_code != 'unsupported-type') ||
1470  ($server_error->message->isOpenID1())) {
1471  return null;
1472  }
1473 
1474  // The server didn't like the association/session type that we
1475  // sent, and it sent us back a message that might tell us how
1476  // to handle it.
1477 
1478  // Extract the session_type and assoc_type from the error
1479  // message
1480  $assoc_type = $server_error->message->getArg(Auth_OpenID_OPENID_NS,
1481  'assoc_type');
1482 
1483  $session_type = $server_error->message->getArg(Auth_OpenID_OPENID_NS,
1484  'session_type');
1485 
1486  if (($assoc_type === null) || ($session_type === null)) {
1487  return null;
1488  } else if (!$this->negotiator->isAllowed($assoc_type,
1489  $session_type)) {
1490  return null;
1491  } else {
1492  return array($assoc_type, $session_type);
1493  }
1494  }
1495 
1499  function _negotiateAssociation($endpoint)
1500  {
1501  // Get our preferred session/association type from the negotiatior.
1502  list($assoc_type, $session_type) = $this->negotiator->getAllowedType();
1503 
1504  $assoc = $this->_requestAssociation(
1505  $endpoint, $assoc_type, $session_type);
1506 
1507  if (Auth_OpenID::isFailure($assoc)) {
1508  return null;
1509  }
1510 
1511  if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) {
1512  $why = $assoc;
1513 
1514  $supportedTypes = $this->_extractSupportedAssociationType(
1515  $why, $endpoint, $assoc_type);
1516 
1517  if ($supportedTypes !== null) {
1518  list($assoc_type, $session_type) = $supportedTypes;
1519 
1520  // Attempt to create an association from the assoc_type
1521  // and session_type that the server told us it
1522  // supported.
1523  $assoc = $this->_requestAssociation(
1524  $endpoint, $assoc_type, $session_type);
1525 
1526  if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) {
1527  // Do not keep trying, since it rejected the
1528  // association type that it told us to use.
1529  // oidutil.log('Server %s refused its suggested association
1530  // 'type: session_type=%s, assoc_type=%s'
1531  // % (endpoint.server_url, session_type,
1532  // assoc_type))
1533  return null;
1534  } else {
1535  return $assoc;
1536  }
1537  } else {
1538  return null;
1539  }
1540  } else {
1541  return $assoc;
1542  }
1543  }
1544 
1548  function _requestAssociation($endpoint, $assoc_type, $session_type)
1549  {
1550  list($assoc_session, $args) = $this->_createAssociateRequest(
1551  $endpoint, $assoc_type, $session_type);
1552 
1553  $response_message = $this->_makeKVPost($args, $endpoint->server_url);
1554 
1555  if ($response_message === null) {
1556  // oidutil.log('openid.associate request failed: %s' % (why[0],))
1557  return null;
1558  } else if (is_a($response_message,
1559  'Auth_OpenID_ServerErrorContainer')) {
1560  return $response_message;
1561  }
1562 
1563  return $this->_extractAssociation($response_message, $assoc_session);
1564  }
1565 
1569  function _extractAssociation($assoc_response, $assoc_session)
1570  {
1571  // Extract the common fields from the response, raising an
1572  // exception if they are not found
1573  $assoc_type = $assoc_response->getArg(
1574  Auth_OpenID_OPENID_NS, 'assoc_type',
1576 
1577  if (Auth_OpenID::isFailure($assoc_type)) {
1578  return $assoc_type;
1579  }
1580 
1581  $assoc_handle = $assoc_response->getArg(
1582  Auth_OpenID_OPENID_NS, 'assoc_handle',
1584 
1585  if (Auth_OpenID::isFailure($assoc_handle)) {
1586  return $assoc_handle;
1587  }
1588 
1589  // expires_in is a base-10 string. The Python parsing will
1590  // accept literals that have whitespace around them and will
1591  // accept negative values. Neither of these are really in-spec,
1592  // but we think it's OK to accept them.
1593  $expires_in_str = $assoc_response->getArg(
1594  Auth_OpenID_OPENID_NS, 'expires_in',
1596 
1597  if (Auth_OpenID::isFailure($expires_in_str)) {
1598  return $expires_in_str;
1599  }
1600 
1601  $expires_in = Auth_OpenID::intval($expires_in_str);
1602  if ($expires_in === false) {
1603 
1604  $err = sprintf("Could not parse expires_in from association ".
1605  "response %s", print_r($assoc_response, true));
1606  return new Auth_OpenID_FailureResponse(null, $err);
1607  }
1608 
1609  // OpenID 1 has funny association session behaviour.
1610  if ($assoc_response->isOpenID1()) {
1611  $session_type = $this->_getOpenID1SessionType($assoc_response);
1612  } else {
1613  $session_type = $assoc_response->getArg(
1614  Auth_OpenID_OPENID2_NS, 'session_type',
1616 
1617  if (Auth_OpenID::isFailure($session_type)) {
1618  return $session_type;
1619  }
1620  }
1621 
1622  // Session type mismatch
1623  if ($assoc_session->session_type != $session_type) {
1624  if ($assoc_response->isOpenID1() &&
1625  ($session_type == 'no-encryption')) {
1626  // In OpenID 1, any association request can result in
1627  // a 'no-encryption' association response. Setting
1628  // assoc_session to a new no-encryption session should
1629  // make the rest of this function work properly for
1630  // that case.
1631  $assoc_session = new Auth_OpenID_PlainTextConsumerSession();
1632  } else {
1633  // Any other mismatch, regardless of protocol version
1634  // results in the failure of the association session
1635  // altogether.
1636  return null;
1637  }
1638  }
1639 
1640  // Make sure assoc_type is valid for session_type
1641  if (!in_array($assoc_type, $assoc_session->allowed_assoc_types)) {
1642  return null;
1643  }
1644 
1645  // Delegate to the association session to extract the secret
1646  // from the response, however is appropriate for that session
1647  // type.
1648  $secret = $assoc_session->extractSecret($assoc_response);
1649 
1650  if ($secret === null) {
1651  return null;
1652  }
1653 
1655  $expires_in, $assoc_handle, $secret, $assoc_type);
1656  }
1657 
1661  function _createAssociateRequest($endpoint, $assoc_type, $session_type)
1662  {
1663  if (array_key_exists($session_type, $this->session_types)) {
1664  $session_type_class = $this->session_types[$session_type];
1665 
1666  if (is_callable($session_type_class)) {
1667  $assoc_session = $session_type_class();
1668  } else {
1669  $assoc_session = new $session_type_class();
1670  }
1671  } else {
1672  return null;
1673  }
1674 
1675  $args = array(
1676  'mode' => 'associate',
1677  'assoc_type' => $assoc_type);
1678 
1679  if (!$endpoint->compatibilityMode()) {
1680  $args['ns'] = Auth_OpenID_OPENID2_NS;
1681  }
1682 
1683  // Leave out the session type if we're in compatibility mode
1684  // *and* it's no-encryption.
1685  if ((!$endpoint->compatibilityMode()) ||
1686  ($assoc_session->session_type != 'no-encryption')) {
1687  $args['session_type'] = $assoc_session->session_type;
1688  }
1689 
1690  $args = array_merge($args, $assoc_session->getRequest());
1691  $message = Auth_OpenID_Message::fromOpenIDArgs($args);
1692  return array($assoc_session, $message);
1693  }
1694 
1708  function _getOpenID1SessionType($assoc_response)
1709  {
1710  // If it's an OpenID 1 message, allow session_type to default
1711  // to None (which signifies "no-encryption")
1712  $session_type = $assoc_response->getArg(Auth_OpenID_OPENID1_NS,
1713  'session_type');
1714 
1715  // Handle the differences between no-encryption association
1716  // respones in OpenID 1 and 2:
1717 
1718  // no-encryption is not really a valid session type for OpenID
1719  // 1, but we'll accept it anyway, while issuing a warning.
1720  if ($session_type == 'no-encryption') {
1721  // oidutil.log('WARNING: OpenID server sent "no-encryption"'
1722  // 'for OpenID 1.X')
1723  } else if (($session_type == '') || ($session_type === null)) {
1724  // Missing or empty session type is the way to flag a
1725  // 'no-encryption' response. Change the session type to
1726  // 'no-encryption' so that it can be handled in the same
1727  // way as OpenID 2 'no-encryption' respones.
1728  $session_type = 'no-encryption';
1729  }
1730 
1731  return $session_type;
1732  }
1733 }
1734 
1742 
1751  function Auth_OpenID_AuthRequest($endpoint, $assoc)
1752  {
1753  $this->assoc = $assoc;
1754  $this->endpoint = $endpoint;
1755  $this->return_to_args = array();
1756  $this->message = new Auth_OpenID_Message(
1757  $endpoint->preferredNamespace());
1758  $this->_anonymous = false;
1759  }
1760 
1767  function addExtension($extension_request)
1768  {
1769  $extension_request->toMessage($this->message);
1770  }
1771 
1791  function addExtensionArg($namespace, $key, $value)
1792  {
1793  return $this->message->setArg($namespace, $key, $value);
1794  }
1795 
1805  function setAnonymous($is_anonymous)
1806  {
1807  if ($is_anonymous && $this->message->isOpenID1()) {
1808  return false;
1809  } else {
1810  $this->_anonymous = $is_anonymous;
1811  return true;
1812  }
1813  }
1814 
1835  function getMessage($realm, $return_to=null, $immediate=false)
1836  {
1837  if ($return_to) {
1838  $return_to = Auth_OpenID::appendArgs($return_to,
1839  $this->return_to_args);
1840  } else if ($immediate) {
1841  // raise ValueError(
1842  // '"return_to" is mandatory when
1843  //using "checkid_immediate"')
1844  return new Auth_OpenID_FailureResponse(null,
1845  "'return_to' is mandatory when using checkid_immediate");
1846  } else if ($this->message->isOpenID1()) {
1847  // raise ValueError('"return_to" is
1848  // mandatory for OpenID 1 requests')
1849  return new Auth_OpenID_FailureResponse(null,
1850  "'return_to' is mandatory for OpenID 1 requests");
1851  } else if ($this->return_to_args) {
1852  // raise ValueError('extra "return_to" arguments
1853  // were specified, but no return_to was specified')
1854  return new Auth_OpenID_FailureResponse(null,
1855  "extra 'return_to' arguments where specified, " .
1856  "but no return_to was specified");
1857  }
1858 
1859  if ($immediate) {
1860  $mode = 'checkid_immediate';
1861  } else {
1862  $mode = 'checkid_setup';
1863  }
1864 
1865  $message = $this->message->copy();
1866  if ($message->isOpenID1()) {
1867  $realm_key = 'trust_root';
1868  } else {
1869  $realm_key = 'realm';
1870  }
1871 
1872  $message->updateArgs(Auth_OpenID_OPENID_NS,
1873  array(
1874  $realm_key => $realm,
1875  'mode' => $mode,
1876  'return_to' => $return_to));
1877 
1878  if (!$this->_anonymous) {
1879  if ($this->endpoint->isOPIdentifier()) {
1880  // This will never happen when we're in compatibility
1881  // mode, as long as isOPIdentifier() returns False
1882  // whenever preferredNamespace() returns OPENID1_NS.
1883  $claimed_id = $request_identity =
1885  } else {
1886  $request_identity = $this->endpoint->getLocalID();
1887  $claimed_id = $this->endpoint->claimed_id;
1888  }
1889 
1890  // This is true for both OpenID 1 and 2
1891  $message->setArg(Auth_OpenID_OPENID_NS, 'identity',
1892  $request_identity);
1893 
1894  if ($message->isOpenID2()) {
1895  $message->setArg(Auth_OpenID_OPENID2_NS, 'claimed_id',
1896  $claimed_id);
1897  }
1898  }
1899 
1900  if ($this->assoc) {
1901  $message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle',
1902  $this->assoc->handle);
1903  }
1904 
1905  return $message;
1906  }
1907 
1908  function redirectURL($realm, $return_to = null,
1909  $immediate = false)
1910  {
1911  $message = $this->getMessage($realm, $return_to, $immediate);
1912 
1913  if (Auth_OpenID::isFailure($message)) {
1914  return $message;
1915  }
1916 
1917  return $message->toURL($this->endpoint->server_url);
1918  }
1919 
1928  function formMarkup($realm, $return_to=null, $immediate=false,
1929  $form_tag_attrs=null)
1930  {
1931  $message = $this->getMessage($realm, $return_to, $immediate);
1932 
1933  if (Auth_OpenID::isFailure($message)) {
1934  return $message;
1935  }
1936 
1937  return $message->toFormMarkup($this->endpoint->server_url,
1938  $form_tag_attrs);
1939  }
1940 
1947  function htmlMarkup($realm, $return_to=null, $immediate=false,
1948  $form_tag_attrs=null)
1949  {
1950  $form = $this->formMarkup($realm, $return_to, $immediate,
1951  $form_tag_attrs);
1952 
1953  if (Auth_OpenID::isFailure($form)) {
1954  return $form;
1955  }
1956  return Auth_OpenID::autoSubmitHTML($form);
1957  }
1958 
1960  {
1961  return $this->endpoint->compatibilityMode();
1962  }
1963 }
1964 
1971  var $status = null;
1972 
1973  function setEndpoint($endpoint)
1974  {
1975  $this->endpoint = $endpoint;
1976  if ($endpoint === null) {
1977  $this->identity_url = null;
1978  } else {
1979  $this->identity_url = $endpoint->claimed_id;
1980  }
1981  }
1982 
2001  {
2002  if ($this->endpoint !== null) {
2003  return $this->endpoint->getDisplayIdentifier();
2004  }
2005  return null;
2006  }
2007 }
2008 
2026 
2030  function Auth_OpenID_SuccessResponse($endpoint, $message, $signed_args=null)
2031  {
2032  $this->endpoint = $endpoint;
2033  $this->identity_url = $endpoint->claimed_id;
2034  $this->signed_args = $signed_args;
2035  $this->message = $message;
2036 
2037  if ($this->signed_args === null) {
2038  $this->signed_args = array();
2039  }
2040  }
2041 
2048  function extensionResponse($namespace_uri, $require_signed)
2049  {
2050  if ($require_signed) {
2051  return $this->getSignedNS($namespace_uri);
2052  } else {
2053  return $this->message->getArgs($namespace_uri);
2054  }
2055  }
2056 
2057  function isOpenID1()
2058  {
2059  return $this->message->isOpenID1();
2060  }
2061 
2062  function isSigned($ns_uri, $ns_key)
2063  {
2064  // Return whether a particular key is signed, regardless of
2065  // its namespace alias
2066  return in_array($this->message->getKey($ns_uri, $ns_key),
2067  $this->signed_args);
2068  }
2069 
2070  function getSigned($ns_uri, $ns_key, $default = null)
2071  {
2072  // Return the specified signed field if available, otherwise
2073  // return default
2074  if ($this->isSigned($ns_uri, $ns_key)) {
2075  return $this->message->getArg($ns_uri, $ns_key, $default);
2076  } else {
2077  return $default;
2078  }
2079  }
2080 
2081  function getSignedNS($ns_uri)
2082  {
2083  $args = array();
2084 
2085  $msg_args = $this->message->getArgs($ns_uri);
2086  if (Auth_OpenID::isFailure($msg_args)) {
2087  return null;
2088  }
2089 
2090  foreach ($msg_args as $key => $value) {
2091  if (!$this->isSigned($ns_uri, $key)) {
2092  unset($msg_args[$key]);
2093  }
2094  }
2095 
2096  return $msg_args;
2097  }
2098 
2109  function getReturnTo()
2110  {
2111  return $this->getSigned(Auth_OpenID_OPENID_NS, 'return_to');
2112  }
2113 }
2114 
2132 
2133  function Auth_OpenID_FailureResponse($endpoint, $message = null,
2134  $contact = null, $reference = null)
2135  {
2136  $this->setEndpoint($endpoint);
2137  $this->message = $message;
2138  $this->contact = $contact;
2139  $this->reference = $reference;
2140  }
2141 }
2142 
2149 }
2150 
2158  function Auth_OpenID_ServerErrorContainer($error_text,
2159  $error_code,
2160  $message)
2161  {
2162  $this->error_text = $error_text;
2163  $this->error_code = $error_code;
2164  $this->message = $message;
2165  }
2166 
2170  static function fromMessage($message)
2171  {
2172  $error_text = $message->getArg(
2173  Auth_OpenID_OPENID_NS, 'error', '<no error message supplied>');
2174  $error_code = $message->getArg(Auth_OpenID_OPENID_NS, 'error_code');
2175  return new Auth_OpenID_ServerErrorContainer($error_text,
2176  $error_code,
2177  $message);
2178  }
2179 }
2180 
2195 
2196  function Auth_OpenID_CancelResponse($endpoint)
2197  {
2198  $this->setEndpoint($endpoint);
2199  }
2200 }
2201 
2221 
2223  $setup_url = null)
2224  {
2225  $this->setEndpoint($endpoint);
2226  $this->setup_url = $setup_url;
2227  }
2228 }
2229 
2230