| [ Indice ] |
Riferimento incrociato di Joomla! 1.5.14 - VM 1.1.4Servizio fornito da VMItalia |
[Vedi sommario] [Stampa] [Vedi testo]
1 <?php 2 3 /** 4 * This module documents the main interface with the OpenID consumer 5 * library. The only part of the library which has to be used and 6 * isn't documented in full here is the store required to create an 7 * Auth_OpenID_Consumer instance. More on the abstract store type and 8 * concrete implementations of it that are provided in the 9 * documentation for the Auth_OpenID_Consumer constructor. 10 * 11 * OVERVIEW 12 * 13 * The OpenID identity verification process most commonly uses the 14 * following steps, as visible to the user of this library: 15 * 16 * 1. The user enters their OpenID into a field on the consumer's 17 * site, and hits a login button. 18 * 2. The consumer site discovers the user's OpenID server using the 19 * YADIS protocol. 20 * 3. The consumer site sends the browser a redirect to the identity 21 * server. This is the authentication request as described in 22 * the OpenID specification. 23 * 4. The identity server's site sends the browser a redirect back 24 * to the consumer site. This redirect contains the server's 25 * response to the authentication request. 26 * 27 * The most important part of the flow to note is the consumer's site 28 * must handle two separate HTTP requests in order to perform the full 29 * identity check. 30 * 31 * LIBRARY DESIGN 32 * 33 * This consumer library is designed with that flow in mind. The goal 34 * is to make it as easy as possible to perform the above steps 35 * securely. 36 * 37 * At a high level, there are two important parts in the consumer 38 * library. The first important part is this module, which contains 39 * the interface to actually use this library. The second is the 40 * Auth_OpenID_Interface class, which describes the interface to use 41 * if you need to create a custom method for storing the state this 42 * library needs to maintain between requests. 43 * 44 * In general, the second part is less important for users of the 45 * library to know about, as several implementations are provided 46 * which cover a wide variety of situations in which consumers may use 47 * the library. 48 * 49 * This module contains a class, Auth_OpenID_Consumer, with methods 50 * corresponding to the actions necessary in each of steps 2, 3, and 4 51 * described in the overview. Use of this library should be as easy 52 * as creating an Auth_OpenID_Consumer instance and calling the 53 * methods appropriate for the action the site wants to take. 54 * 55 * STORES AND DUMB MODE 56 * 57 * OpenID is a protocol that works best when the consumer site is able 58 * to store some state. This is the normal mode of operation for the 59 * protocol, and is sometimes referred to as smart mode. There is 60 * also a fallback mode, known as dumb mode, which is available when 61 * the consumer site is not able to store state. This mode should be 62 * avoided when possible, as it leaves the implementation more 63 * vulnerable to replay attacks. 64 * 65 * The mode the library works in for normal operation is determined by 66 * the store that it is given. The store is an abstraction that 67 * handles the data that the consumer needs to manage between http 68 * requests in order to operate efficiently and securely. 69 * 70 * Several store implementation are provided, and the interface is 71 * fully documented so that custom stores can be used as well. See 72 * the documentation for the Auth_OpenID_Consumer class for more 73 * information on the interface for stores. The implementations that 74 * are provided allow the consumer site to store the necessary data in 75 * several different ways, including several SQL databases and normal 76 * files on disk. 77 * 78 * There is an additional concrete store provided that puts the system 79 * in dumb mode. This is not recommended, as it removes the library's 80 * ability to stop replay attacks reliably. It still uses time-based 81 * checking to make replay attacks only possible within a small 82 * window, but they remain possible within that window. This store 83 * should only be used if the consumer site has no way to retain data 84 * between requests at all. 85 * 86 * IMMEDIATE MODE 87 * 88 * In the flow described above, the user may need to confirm to the 89 * lidentity server that it's ok to authorize his or her identity. 90 * The server may draw pages asking for information from the user 91 * before it redirects the browser back to the consumer's site. This 92 * is generally transparent to the consumer site, so it is typically 93 * ignored as an implementation detail. 94 * 95 * There can be times, however, where the consumer site wants to get a 96 * response immediately. When this is the case, the consumer can put 97 * the library in immediate mode. In immediate mode, there is an 98 * extra response possible from the server, which is essentially the 99 * server reporting that it doesn't have enough information to answer 100 * the question yet. 101 * 102 * USING THIS LIBRARY 103 * 104 * Integrating this library into an application is usually a 105 * relatively straightforward process. The process should basically 106 * follow this plan: 107 * 108 * Add an OpenID login field somewhere on your site. When an OpenID 109 * is entered in that field and the form is submitted, it should make 110 * a request to the your site which includes that OpenID URL. 111 * 112 * First, the application should instantiate the Auth_OpenID_Consumer 113 * class using the store of choice (Auth_OpenID_FileStore or one of 114 * the SQL-based stores). If the application has a custom 115 * session-management implementation, an object implementing the 116 * {@link Auth_Yadis_PHPSession} interface should be passed as the 117 * second parameter. Otherwise, the default uses $_SESSION. 118 * 119 * Next, the application should call the Auth_OpenID_Consumer object's 120 * 'begin' method. This method takes the OpenID URL. The 'begin' 121 * method returns an Auth_OpenID_AuthRequest object. 122 * 123 * Next, the application should call the 'redirectURL' method of the 124 * Auth_OpenID_AuthRequest object. The 'return_to' URL parameter is 125 * the URL that the OpenID server will send the user back to after 126 * attempting to verify his or her identity. The 'trust_root' is the 127 * URL (or URL pattern) that identifies your web site to the user when 128 * he or she is authorizing it. Send a redirect to the resulting URL 129 * to the user's browser. 130 * 131 * That's the first half of the authentication process. The second 132 * half of the process is done after the user's ID server sends the 133 * user's browser a redirect back to your site to complete their 134 * login. 135 * 136 * When that happens, the user will contact your site at the URL given 137 * as the 'return_to' URL to the Auth_OpenID_AuthRequest::redirectURL 138 * call made above. The request will have several query parameters 139 * added to the URL by the identity server as the information 140 * necessary to finish the request. 141 * 142 * Lastly, instantiate an Auth_OpenID_Consumer instance as above and 143 * call its 'complete' method, passing in all the received query 144 * arguments. 145 * 146 * There are multiple possible return types possible from that 147 * method. These indicate the whether or not the login was successful, 148 * and include any additional information appropriate for their type. 149 * 150 * PHP versions 4 and 5 151 * 152 * LICENSE: See the COPYING file included in this distribution. 153 * 154 * @package OpenID 155 * @author JanRain, Inc. <openid@janrain.com> 156 * @copyright 2005-2008 Janrain, Inc. 157 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache 158 */ 159 160 // Do not allow direct access 161 defined( '_JEXEC' ) or die( 'Restricted access' ); 162 163 /** 164 * Require utility classes and functions for the consumer. 165 */ 166 require_once "Auth/OpenID.php"; 167 require_once "Auth/OpenID/Message.php"; 168 require_once "Auth/OpenID/HMAC.php"; 169 require_once "Auth/OpenID/Association.php"; 170 require_once "Auth/OpenID/CryptUtil.php"; 171 require_once "Auth/OpenID/DiffieHellman.php"; 172 require_once "Auth/OpenID/KVForm.php"; 173 require_once "Auth/OpenID/Nonce.php"; 174 require_once "Auth/OpenID/Discover.php"; 175 require_once "Auth/OpenID/URINorm.php"; 176 require_once "Auth/Yadis/Manager.php"; 177 require_once "Auth/Yadis/XRI.php"; 178 179 /** 180 * This is the status code returned when the complete method returns 181 * successfully. 182 */ 183 define('Auth_OpenID_SUCCESS', 'success'); 184 185 /** 186 * Status to indicate cancellation of OpenID authentication. 187 */ 188 define('Auth_OpenID_CANCEL', 'cancel'); 189 190 /** 191 * This is the status code completeAuth returns when the value it 192 * received indicated an invalid login. 193 */ 194 define('Auth_OpenID_FAILURE', 'failure'); 195 196 /** 197 * This is the status code completeAuth returns when the 198 * {@link Auth_OpenID_Consumer} instance is in immediate mode, and the 199 * identity server sends back a URL to send the user to to complete his 200 * or her login. 201 */ 202 define('Auth_OpenID_SETUP_NEEDED', 'setup needed'); 203 204 /** 205 * This is the status code beginAuth returns when the page fetched 206 * from the entered OpenID URL doesn't contain the necessary link tags 207 * to function as an identity page. 208 */ 209 define('Auth_OpenID_PARSE_ERROR', 'parse error'); 210 211 /** 212 * An OpenID consumer implementation that performs discovery and does 213 * session management. See the Consumer.php file documentation for 214 * more information. 215 * 216 * @package OpenID 217 */ 218 class Auth_OpenID_Consumer { 219 220 /** 221 * @access private 222 */ 223 var $discoverMethod = 'Auth_OpenID_discover'; 224 225 /** 226 * @access private 227 */ 228 var $session_key_prefix = "_openid_consumer_"; 229 230 /** 231 * @access private 232 */ 233 var $_token_suffix = "last_token"; 234 235 /** 236 * Initialize a Consumer instance. 237 * 238 * You should create a new instance of the Consumer object with 239 * every HTTP request that handles OpenID transactions. 240 * 241 * @param Auth_OpenID_OpenIDStore $store This must be an object 242 * that implements the interface in {@link 243 * Auth_OpenID_OpenIDStore}. Several concrete implementations are 244 * provided, to cover most common use cases. For stores backed by 245 * MySQL, PostgreSQL, or SQLite, see the {@link 246 * Auth_OpenID_SQLStore} class and its sublcasses. For a 247 * filesystem-backed store, see the {@link Auth_OpenID_FileStore} 248 * module. As a last resort, if it isn't possible for the server 249 * to store state at all, an instance of {@link 250 * Auth_OpenID_DumbStore} can be used. 251 * 252 * @param mixed $session An object which implements the interface 253 * of the {@link Auth_Yadis_PHPSession} class. Particularly, this 254 * object is expected to have these methods: get($key), set($key), 255 * $value), and del($key). This defaults to a session object 256 * which wraps PHP's native session machinery. You should only 257 * need to pass something here if you have your own sessioning 258 * implementation. 259 * 260 * @param str $consumer_cls The name of the class to instantiate 261 * when creating the internal consumer object. This is used for 262 * testing. 263 */ 264 function Auth_OpenID_Consumer(&$store, $session = null, 265 $consumer_cls = null) 266 { 267 if ($session === null) { 268 $session = new Auth_Yadis_PHPSession(); 269 } 270 271 $this->session =& $session; 272 273 if ($consumer_cls !== null) { 274 $this->consumer =& new $consumer_cls($store); 275 } else { 276 $this->consumer =& new Auth_OpenID_GenericConsumer($store); 277 } 278 279 $this->_token_key = $this->session_key_prefix . $this->_token_suffix; 280 } 281 282 /** 283 * Used in testing to define the discovery mechanism. 284 * 285 * @access private 286 */ 287 function getDiscoveryObject(&$session, $openid_url, 288 $session_key_prefix) 289 { 290 return new Auth_Yadis_Discovery($session, $openid_url, 291 $session_key_prefix); 292 } 293 294 /** 295 * Start the OpenID authentication process. See steps 1-2 in the 296 * overview at the top of this file. 297 * 298 * @param string $user_url Identity URL given by the user. This 299 * method performs a textual transformation of the URL to try and 300 * make sure it is normalized. For example, a user_url of 301 * example.com will be normalized to http://example.com/ 302 * normalizing and resolving any redirects the server might issue. 303 * 304 * @param bool $anonymous True if the OpenID request is to be sent 305 * to the server without any identifier information. Use this 306 * when you want to transport data but don't want to do OpenID 307 * authentication with identifiers. 308 * 309 * @return Auth_OpenID_AuthRequest $auth_request An object 310 * containing the discovered information will be returned, with a 311 * method for building a redirect URL to the server, as described 312 * in step 3 of the overview. This object may also be used to add 313 * extension arguments to the request, using its 'addExtensionArg' 314 * method. 315 */ 316 function begin($user_url, $anonymous=false) 317 { 318 $openid_url = $user_url; 319 320 $disco = $this->getDiscoveryObject($this->session, 321 $openid_url, 322 $this->session_key_prefix); 323 324 // Set the 'stale' attribute of the manager. If discovery 325 // fails in a fatal way, the stale flag will cause the manager 326 // to be cleaned up next time discovery is attempted. 327 328 $m = $disco->getManager(); 329 $loader = new Auth_Yadis_ManagerLoader(); 330 331 if ($m) { 332 if ($m->stale) { 333 $disco->destroyManager(); 334 } else { 335 $m->stale = true; 336 $disco->session->set($disco->session_key, 337 serialize($loader->toSession($m))); 338 } 339 } 340 341 $endpoint = $disco->getNextService($this->discoverMethod, 342 $this->consumer->fetcher); 343 344 // Reset the 'stale' attribute of the manager. 345 $m =& $disco->getManager(); 346 if ($m) { 347 $m->stale = false; 348 $disco->session->set($disco->session_key, 349 serialize($loader->toSession($m))); 350 } 351 352 if ($endpoint === null) { 353 return null; 354 } else { 355 return $this->beginWithoutDiscovery($endpoint, 356 $anonymous); 357 } 358 } 359 360 /** 361 * Start OpenID verification without doing OpenID server 362 * discovery. This method is used internally by Consumer.begin 363 * after discovery is performed, and exists to provide an 364 * interface for library users needing to perform their own 365 * discovery. 366 * 367 * @param Auth_OpenID_ServiceEndpoint $endpoint an OpenID service 368 * endpoint descriptor. 369 * 370 * @param bool anonymous Set to true if you want to perform OpenID 371 * without identifiers. 372 * 373 * @return Auth_OpenID_AuthRequest $auth_request An OpenID 374 * authentication request object. 375 */ 376 function &beginWithoutDiscovery($endpoint, $anonymous=false) 377 { 378 $loader = new Auth_OpenID_ServiceEndpointLoader(); 379 $auth_req = $this->consumer->begin($endpoint); 380 $this->session->set($this->_token_key, 381 $loader->toSession($auth_req->endpoint)); 382 if (!$auth_req->setAnonymous($anonymous)) { 383 return new Auth_OpenID_FailureResponse(null, 384 "OpenID 1 requests MUST include the identifier " . 385 "in the request."); 386 } 387 return $auth_req; 388 } 389 390 /** 391 * Called to interpret the server's response to an OpenID 392 * request. It is called in step 4 of the flow described in the 393 * consumer overview. 394 * 395 * @param string $current_url The URL used to invoke the application. 396 * Extract the URL from your application's web 397 * request framework and specify it here to have it checked 398 * against the openid.current_url value in the response. If 399 * the current_url URL check fails, the status of the 400 * completion will be FAILURE. 401 * 402 * @param array $query An array of the query parameters (key => 403 * value pairs) for this HTTP request. Defaults to null. If 404 * null, the GET or POST data are automatically gotten from the 405 * PHP environment. It is only useful to override $query for 406 * testing. 407 * 408 * @return Auth_OpenID_ConsumerResponse $response A instance of an 409 * Auth_OpenID_ConsumerResponse subclass. The type of response is 410 * indicated by the status attribute, which will be one of 411 * SUCCESS, CANCEL, FAILURE, or SETUP_NEEDED. 412 */ 413 function complete($current_url, $query=null) 414 { 415 if ($current_url && !is_string($current_url)) { 416 // This is ugly, but we need to complain loudly when 417 // someone uses the API incorrectly. 418 trigger_error("current_url must be a string; see NEWS file " . 419 "for upgrading notes.", 420 E_USER_ERROR); 421 } 422 423 if ($query === null) { 424 $query = Auth_OpenID::getQuery(); 425 } 426 427 $loader = new Auth_OpenID_ServiceEndpointLoader(); 428 $endpoint_data = $this->session->get($this->_token_key); 429 $endpoint = 430 $loader->fromSession($endpoint_data); 431 432 $message = Auth_OpenID_Message::fromPostArgs($query); 433 $response = $this->consumer->complete($message, $endpoint, 434 $current_url); 435 $this->session->del($this->_token_key); 436 437 if (in_array($response->status, array(Auth_OpenID_SUCCESS, 438 Auth_OpenID_CANCEL))) { 439 if ($response->identity_url !== null) { 440 $disco = $this->getDiscoveryObject($this->session, 441 $response->identity_url, 442 $this->session_key_prefix); 443 $disco->cleanup(true); 444 } 445 } 446 447 return $response; 448 } 449 } 450 451 /** 452 * A class implementing HMAC/DH-SHA1 consumer sessions. 453 * 454 * @package OpenID 455 */ 456 class Auth_OpenID_DiffieHellmanSHA1ConsumerSession { 457 var $session_type = 'DH-SHA1'; 458 var $hash_func = 'Auth_OpenID_SHA1'; 459 var $secret_size = 20; 460 var $allowed_assoc_types = array('HMAC-SHA1'); 461 462 function Auth_OpenID_DiffieHellmanSHA1ConsumerSession($dh = null) 463 { 464 if ($dh === null) { 465 $dh = new Auth_OpenID_DiffieHellman(); 466 } 467 468 $this->dh = $dh; 469 } 470 471 function getRequest() 472 { 473 $math =& Auth_OpenID_getMathLib(); 474 475 $cpub = $math->longToBase64($this->dh->public); 476 477 $args = array('dh_consumer_public' => $cpub); 478 479 if (!$this->dh->usingDefaultValues()) { 480 $args = array_merge($args, array( 481 'dh_modulus' => 482 $math->longToBase64($this->dh->mod), 483 'dh_gen' => 484 $math->longToBase64($this->dh->gen))); 485 } 486 487 return $args; 488 } 489 490 function extractSecret($response) 491 { 492 if (!$response->hasKey(Auth_OpenID_OPENID_NS, 493 'dh_server_public')) { 494 return null; 495 } 496 497 if (!$response->hasKey(Auth_OpenID_OPENID_NS, 498 'enc_mac_key')) { 499 return null; 500 } 501 502 $math =& Auth_OpenID_getMathLib(); 503 504 $spub = $math->base64ToLong($response->getArg(Auth_OpenID_OPENID_NS, 505 'dh_server_public')); 506 $enc_mac_key = base64_decode($response->getArg(Auth_OpenID_OPENID_NS, 507 'enc_mac_key')); 508 509 return $this->dh->xorSecret($spub, $enc_mac_key, $this->hash_func); 510 } 511 } 512 513 /** 514 * A class implementing HMAC/DH-SHA256 consumer sessions. 515 * 516 * @package OpenID 517 */ 518 class Auth_OpenID_DiffieHellmanSHA256ConsumerSession extends 519 Auth_OpenID_DiffieHellmanSHA1ConsumerSession { 520 var $session_type = 'DH-SHA256'; 521 var $hash_func = 'Auth_OpenID_SHA256'; 522 var $secret_size = 32; 523 var $allowed_assoc_types = array('HMAC-SHA256'); 524 } 525 526 /** 527 * A class implementing plaintext consumer sessions. 528 * 529 * @package OpenID 530 */ 531 class Auth_OpenID_PlainTextConsumerSession { 532 var $session_type = 'no-encryption'; 533 var $allowed_assoc_types = array('HMAC-SHA1', 'HMAC-SHA256'); 534 535 function getRequest() 536 { 537 return array(); 538 } 539 540 function extractSecret($response) 541 { 542 if (!$response->hasKey(Auth_OpenID_OPENID_NS, 'mac_key')) { 543 return null; 544 } 545 546 return base64_decode($response->getArg(Auth_OpenID_OPENID_NS, 547 'mac_key')); 548 } 549 } 550 551 /** 552 * Returns available session types. 553 */ 554 function Auth_OpenID_getAvailableSessionTypes() 555 { 556 $types = array( 557 'no-encryption' => 'Auth_OpenID_PlainTextConsumerSession', 558 'DH-SHA1' => 'Auth_OpenID_DiffieHellmanSHA1ConsumerSession', 559 'DH-SHA256' => 'Auth_OpenID_DiffieHellmanSHA256ConsumerSession'); 560 561 return $types; 562 } 563 564 /** 565 * This class is the interface to the OpenID consumer logic. 566 * Instances of it maintain no per-request state, so they can be 567 * reused (or even used by multiple threads concurrently) as needed. 568 * 569 * @package OpenID 570 */ 571 class Auth_OpenID_GenericConsumer { 572 /** 573 * @access private 574 */ 575 var $discoverMethod = 'Auth_OpenID_discover'; 576 577 /** 578 * This consumer's store object. 579 */ 580 var $store; 581 582 /** 583 * @access private 584 */ 585 var $_use_assocs; 586 587 /** 588 * @access private 589 */ 590 var $openid1_nonce_query_arg_name = 'janrain_nonce'; 591 592 /** 593 * Another query parameter that gets added to the return_to for 594 * OpenID 1; if the user's session state is lost, use this claimed 595 * identifier to do discovery when verifying the response. 596 */ 597 var $openid1_return_to_identifier_name = 'openid1_claimed_id'; 598 599 /** 600 * This method initializes a new {@link Auth_OpenID_Consumer} 601 * instance to access the library. 602 * 603 * @param Auth_OpenID_OpenIDStore $store This must be an object 604 * that implements the interface in {@link Auth_OpenID_OpenIDStore}. 605 * Several concrete implementations are provided, to cover most common use 606 * cases. For stores backed by MySQL, PostgreSQL, or SQLite, see 607 * the {@link Auth_OpenID_SQLStore} class and its sublcasses. For a 608 * filesystem-backed store, see the {@link Auth_OpenID_FileStore} module. 609 * As a last resort, if it isn't possible for the server to store 610 * state at all, an instance of {@link Auth_OpenID_DumbStore} can be used. 611 * 612 * @param bool $immediate This is an optional boolean value. It 613 * controls whether the library uses immediate mode, as explained 614 * in the module description. The default value is False, which 615 * disables immediate mode. 616 */ 617 function Auth_OpenID_GenericConsumer(&$store) 618 { 619 $this->store =& $store; 620 $this->negotiator =& Auth_OpenID_getDefaultNegotiator(); 621 $this->_use_assocs = ($this->store ? true : false); 622 623 $this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); 624 625 $this->session_types = Auth_OpenID_getAvailableSessionTypes(); 626 } 627 628 /** 629 * Called to begin OpenID authentication using the specified 630 * {@link Auth_OpenID_ServiceEndpoint}. 631 * 632 * @access private 633 */ 634 function begin($service_endpoint) 635 { 636 $assoc = $this->_getAssociation($service_endpoint); 637 $r = new Auth_OpenID_AuthRequest($service_endpoint, $assoc); 638 $r->return_to_args[$this->openid1_nonce_query_arg_name] = 639 Auth_OpenID_mkNonce(); 640 641 if ($r->message->isOpenID1()) { 642 $r->return_to_args[$this->openid1_return_to_identifier_name] = 643 $r->endpoint->claimed_id; 644 } 645 646 return $r; 647 } 648 649 /** 650 * Given an {@link Auth_OpenID_Message}, {@link 651 * Auth_OpenID_ServiceEndpoint} and optional return_to URL, 652 * complete OpenID authentication. 653 * 654 * @access private 655 */ 656 function complete($message, $endpoint, $return_to) 657 { 658 $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode', 659 '<no mode set>'); 660 661 $mode_methods = array( 662 'cancel' => '_complete_cancel', 663 'error' => '_complete_error', 664 'setup_needed' => '_complete_setup_needed', 665 'id_res' => '_complete_id_res', 666 ); 667 668 $method = Auth_OpenID::arrayGet($mode_methods, $mode, 669 '_completeInvalid'); 670 671 return call_user_func_array(array(&$this, $method), 672 array($message, $endpoint, $return_to)); 673 } 674 675 /** 676 * @access private 677 */ 678 function _completeInvalid($message, &$endpoint, $unused) 679 { 680 $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode', 681 '<No mode set>'); 682 683 return new Auth_OpenID_FailureResponse($endpoint, 684 sprintf("Invalid openid.mode '%s'", $mode)); 685 } 686 687 /** 688 * @access private 689 */ 690 function _complete_cancel($message, &$endpoint, $unused) 691 { 692 return new Auth_OpenID_CancelResponse($endpoint); 693 } 694 695 /** 696 * @access private 697 */ 698 function _complete_error($message, &$endpoint, $unused) 699 { 700 $error = $message->getArg(Auth_OpenID_OPENID_NS, 'error'); 701 $contact = $message->getArg(Auth_OpenID_OPENID_NS, 'contact'); 702 $reference = $message->getArg(Auth_OpenID_OPENID_NS, 'reference'); 703 704 return new Auth_OpenID_FailureResponse($endpoint, $error, 705 $contact, $reference); 706 } 707 708 /** 709 * @access private 710 */ 711 function _complete_setup_needed($message, &$endpoint, $unused) 712 { 713 if (!$message->isOpenID2()) { 714 return $this->_completeInvalid($message, $endpoint); 715 } 716 717 $user_setup_url = $message->getArg(Auth_OpenID_OPENID2_NS, 718 'user_setup_url'); 719 return new Auth_OpenID_SetupNeededResponse($endpoint, $user_setup_url); 720 } 721 722 /** 723 * @access private 724 */ 725 function _complete_id_res($message, &$endpoint, $return_to) 726 { 727 $user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS, 728 'user_setup_url'); 729 730 if ($this->_checkSetupNeeded($message)) { 731 return new Auth_OpenID_SetupNeededResponse( 732 $endpoint, $user_setup_url); 733 } else { 734 return $this->_doIdRes($message, $endpoint, $return_to); 735 } 736 } 737 738 /** 739 * @access private 740 */ 741 function _checkSetupNeeded($message) 742 { 743 // In OpenID 1, we check to see if this is a cancel from 744 // immediate mode by the presence of the user_setup_url 745 // parameter. 746 if ($message->isOpenID1()) { 747 $user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS, 748 'user_setup_url'); 749 if ($user_setup_url !== null) { 750 return true; 751 } 752 } 753 754 return false; 755 } 756 757 /** 758 * @access private 759 */ 760 function _doIdRes($message, $endpoint, $return_to) 761 { 762 // Checks for presence of appropriate fields (and checks 763 // signed list fields) 764 $result = $this->_idResCheckForFields($message); 765 766 if (Auth_OpenID::isFailure($result)) { 767 return $result; 768 } 769 770 if (!$this->_checkReturnTo($message, $return_to)) { 771 return new Auth_OpenID_FailureResponse(null, 772 sprintf("return_to does not match return URL. Expected %s, got %s", 773 $return_to, 774 $message->getArg(Auth_OpenID_OPENID_NS, 'return_to'))); 775 } 776 777 // Verify discovery information: 778 $result = $this->_verifyDiscoveryResults($message, $endpoint); 779 780 if (Auth_OpenID::isFailure($result)) { 781 return $result; 782 } 783 784 $endpoint = $result; 785 786 $result = $this->_idResCheckSignature($message, 787 $endpoint->server_url); 788 789 if (Auth_OpenID::isFailure($result)) { 790 return $result; 791 } 792 793 $result = $this->_idResCheckNonce($message, $endpoint); 794 795 if (Auth_OpenID::isFailure($result)) { 796 return $result; 797 } 798 799 $signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS, 'signed', 800 Auth_OpenID_NO_DEFAULT); 801 if (Auth_OpenID::isFailure($signed_list_str)) { 802 return $signed_list_str; 803 } 804 $signed_list = explode(',', $signed_list_str); 805 806 $signed_fields = Auth_OpenID::addPrefix($signed_list, "openid."); 807 808 return new Auth_OpenID_SuccessResponse($endpoint, $message, 809 $signed_fields); 810 811 } 812 813 /** 814 * @access private 815 */ 816 function _checkReturnTo($message, $return_to) 817 { 818 // Check an OpenID message and its openid.return_to value 819 // against a return_to URL from an application. Return True 820 // on success, False on failure. 821 822 // Check the openid.return_to args against args in the 823 // original message. 824 $result = Auth_OpenID_GenericConsumer::_verifyReturnToArgs( 825 $message->toPostArgs()); 826 if (Auth_OpenID::isFailure($result)) { 827 return false; 828 } 829 830 // Check the return_to base URL against the one in the 831 // message. 832 $msg_return_to = $message->getArg(Auth_OpenID_OPENID_NS, 833 'return_to'); 834 if (Auth_OpenID::isFailure($return_to)) { 835 // XXX log me 836 return false; 837 } 838 839 $return_to_parts = parse_url(Auth_OpenID_urinorm($return_to)); 840 $msg_return_to_parts = parse_url(Auth_OpenID_urinorm($msg_return_to)); 841 842 // If port is absent from both, add it so it's equal in the 843 // check below. 844 if ((!array_key_exists('port', $return_to_parts)) && 845 (!array_key_exists('port', $msg_return_to_parts))) { 846 $return_to_parts['port'] = null; 847 $msg_return_to_parts['port'] = null; 848 } 849 850 // If path is absent from both, add it so it's equal in the 851 // check below. 852 if ((!array_key_exists('path', $return_to_parts)) && 853 (!array_key_exists('path', $msg_return_to_parts))) { 854 $return_to_parts['path'] = null; 855 $msg_return_to_parts['path'] = null; 856 } 857 858 // The URL scheme, authority, and path MUST be the same 859 // between the two URLs. 860 foreach (array('scheme', 'host', 'port', 'path') as $component) { 861 // If the url component is absent in either URL, fail. 862 // There should always be a scheme, host, port, and path. 863 if (!array_key_exists($component, $return_to_parts)) { 864 return false; 865 } 866 867 if (!array_key_exists($component, $msg_return_to_parts)) { 868 return false; 869 } 870 871 if (Auth_OpenID::arrayGet($return_to_parts, $component) !== 872 Auth_OpenID::arrayGet($msg_return_to_parts, $component)) { 873 return false; 874 } 875 } 876 877 return true; 878 } 879 880 /** 881 * @access private 882 */ 883 function _verifyReturnToArgs($query) 884 { 885 // Verify that the arguments in the return_to URL are present in this 886 // response. 887 888 $message = Auth_OpenID_Message::fromPostArgs($query); 889 $return_to = $message->getArg(Auth_OpenID_OPENID_NS, 'return_to'); 890 891 if (Auth_OpenID::isFailure($return_to)) { 892 return $return_to; 893 } 894 // XXX: this should be checked by _idResCheckForFields 895 if (!$return_to) { 896 return new Auth_OpenID_FailureResponse(null, 897 "Response has no return_to"); 898 } 899 900 $parsed_url = parse_url($return_to); 901 902 $q = array(); 903 if (array_key_exists('query', $parsed_url)) { 904 $rt_query = $parsed_url['query']; 905 $q = Auth_OpenID::parse_str($rt_query); 906 } 907 908 foreach ($q as $rt_key => $rt_value) { 909 if (!array_key_exists($rt_key, $query)) { 910 return new Auth_OpenID_FailureResponse(null, 911 sprintf("return_to parameter %s absent from query", $rt_key)); 912 } else { 913 $value = $query[$rt_key]; 914 if ($rt_value != $value) { 915 return new Auth_OpenID_FailureResponse(null, 916 sprintf("parameter %s value %s does not match " . 917 "return_to value %s", $rt_key, 918 $value, $rt_value)); 919 } 920 } 921 } 922 923 // Make sure all non-OpenID arguments in the response are also 924 // in the signed return_to. 925 $bare_args = $message->getArgs(Auth_OpenID_BARE_NS); 926 foreach ($bare_args as $key => $value) { 927 if (Auth_OpenID::arrayGet($q, $key) != $value) { 928 return new Auth_OpenID_FailureResponse(null, 929 sprintf("Parameter %s = %s not in return_to URL", 930 $key, $value)); 931 } 932 } 933 934 return true; 935 } 936 937 /** 938 * @access private 939 */ 940 function _idResCheckSignature($message, $server_url) 941 { 942 $assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS, 943 'assoc_handle'); 944 if (Auth_OpenID::isFailure($assoc_handle)) { 945 return $assoc_handle; 946 } 947 948 $assoc = $this->store->getAssociation($server_url, $assoc_handle); 949 950 if ($assoc) { 951 if ($assoc->getExpiresIn() <= 0) { 952 // XXX: It might be a good idea sometimes to re-start 953 // the authentication with a new association. Doing it 954 // automatically opens the possibility for 955 // denial-of-service by a server that just returns 956 // expired associations (or really short-lived 957 // associations) 958 return new Auth_OpenID_FailureResponse(null, 959 'Association with ' . $server_url . ' expired'); 960 } 961 962 if (!$assoc->checkMessageSignature($message)) { 963 return new Auth_OpenID_FailureResponse(null, 964 "Bad signature"); 965 } 966 } else { 967 // It's not an association we know about. Stateless mode 968 // is our only possible path for recovery. XXX - async 969 // framework will not want to block on this call to 970 // _checkAuth. 971 if (!$this->_checkAuth($message, $server_url)) { 972 return new Auth_OpenID_FailureResponse(null, 973 "Server denied check_authentication"); 974 } 975 } 976 977 return null; 978 } 979 980 /** 981 * @access private 982 */ 983 function _verifyDiscoveryResults($message, $endpoint=null) 984 { 985 if ($message->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS) { 986 return $this->_verifyDiscoveryResultsOpenID2($message, 987 $endpoint); 988 } else { 989 return $this->_verifyDiscoveryResultsOpenID1($message, 990 $endpoint); 991 } 992 } 993 994 /** 995 * @access private 996 */ 997 function _verifyDiscoveryResultsOpenID1($message, $endpoint) 998 { 999 $claimed_id = $message->getArg(Auth_OpenID_BARE_NS, 1000 $this->openid1_return_to_identifier_name); 1001 1002 if (($endpoint === null) && ($claimed_id === null)) { 1003 return new Auth_OpenID_FailureResponse($endpoint, 1004 'When using OpenID 1, the claimed ID must be supplied, ' . 1005 'either by passing it through as a return_to parameter ' . 1006 'or by using a session, and supplied to the GenericConsumer ' . 1007 'as the argument to complete()'); 1008 } else if (($endpoint !== null) && ($claimed_id === null)) { 1009 $claimed_id = $endpoint->claimed_id; 1010 } 1011 1012 $to_match = new Auth_OpenID_ServiceEndpoint(); 1013 $to_match->type_uris = array(Auth_OpenID_TYPE_1_1); 1014 $to_match->local_id = $message->getArg(Auth_OpenID_OPENID1_NS, 1015 'identity'); 1016 1017 // Restore delegate information from the initiation phase 1018 $to_match->claimed_id = $claimed_id; 1019 1020 if ($to_match->local_id === null) { 1021 return new Auth_OpenID_FailureResponse($endpoint, 1022 "Missing required field openid.identity"); 1023 } 1024 1025 $to_match_1_0 = $to_match->copy(); 1026 $to_match_1_0->type_uris = array(Auth_OpenID_TYPE_1_0); 1027 1028 if ($endpoint !== null) { 1029 $result = $this->_verifyDiscoverySingle($endpoint, $to_match); 1030 1031 if (is_a($result, 'Auth_OpenID_TypeURIMismatch')) { 1032 $result = $this->_verifyDiscoverySingle($endpoint, 1033 $to_match_1_0); 1034 } 1035 1036 if (Auth_OpenID::isFailure($result)) { 1037 // oidutil.log("Error attempting to use stored 1038 // discovery information: " + str(e)) 1039 // oidutil.log("Attempting discovery to 1040 // verify endpoint") 1041 } else { 1042 return $endpoint; 1043 } 1044 } 1045 1046 // Endpoint is either bad (failed verification) or None 1047 return $this->_discoverAndVerify($to_match->claimed_id, 1048 array($to_match, $to_match_1_0)); 1049 } 1050 1051 /** 1052 * @access private 1053 */ 1054 function _verifyDiscoverySingle($endpoint, $to_match) 1055 { 1056 // Every type URI that's in the to_match endpoint has to be 1057 // present in the discovered endpoint. 1058 foreach ($to_match->type_uris as $type_uri) { 1059 if (!$endpoint->usesExtension($type_uri)) { 1060 return new Auth_OpenID_TypeURIMismatch($endpoint, 1061 "Required type ".$type_uri." not present"); 1062 } 1063 } 1064 1065 // Fragments do not influence discovery, so we can't compare a 1066 // claimed identifier with a fragment to discovered 1067 // information. 1068 list($defragged_claimed_id, $_) = 1069 Auth_OpenID::urldefrag($to_match->claimed_id); 1070 1071 if ($defragged_claimed_id != $endpoint->claimed_id) { 1072 return new Auth_OpenID_FailureResponse($endpoint, 1073 sprintf('Claimed ID does not match (different subjects!), ' . 1074 'Expected %s, got %s', $defragged_claimed_id, 1075 $endpoint->claimed_id)); 1076 } 1077 1078 if ($to_match->getLocalID() != $endpoint->getLocalID()) { 1079 return new Auth_OpenID_FailureResponse($endpoint, 1080 sprintf('local_id mismatch. Expected %s, got %s', 1081 $to_match->getLocalID(), $endpoint->getLocalID())); 1082 } 1083 1084 // If the server URL is None, this must be an OpenID 1 1085 // response, because op_endpoint is a required parameter in 1086 // OpenID 2. In that case, we don't actually care what the 1087 // discovered server_url is, because signature checking or 1088 // check_auth should take care of that check for us. 1089 if ($to_match->server_url === null) { 1090 if ($to_match->preferredNamespace() != Auth_OpenID_OPENID1_NS) { 1091 return new Auth_OpenID_FailureResponse($endpoint, 1092 "Preferred namespace mismatch (bug)"); 1093 } 1094 } else if ($to_match->server_url != $endpoint->server_url) { 1095 return new Auth_OpenID_FailureResponse($endpoint, 1096 sprintf('OP Endpoint mismatch. Expected %s, got %s', 1097 $to_match->server_url, $endpoint->server_url)); 1098 } 1099 1100 return null; 1101 } 1102 1103 /** 1104 * @access private 1105 */ 1106 function _verifyDiscoveryResultsOpenID2($message, $endpoint) 1107 { 1108 $to_match = new Auth_OpenID_ServiceEndpoint(); 1109 $to_match->type_uris = array(Auth_OpenID_TYPE_2_0); 1110 $to_match->claimed_id = $message->getArg(Auth_OpenID_OPENID2_NS, 1111 'claimed_id'); 1112 1113 $to_match->local_id = $message->getArg(Auth_OpenID_OPENID2_NS, 1114 'identity'); 1115 1116 $to_match->server_url = $message->getArg(Auth_OpenID_OPENID2_NS, 1117 'op_endpoint'); 1118 1119 if ($to_match->server_url === null) { 1120 return new Auth_OpenID_FailureResponse($endpoint, 1121 "OP Endpoint URL missing"); 1122 } 1123 1124 // claimed_id and identifier must both be present or both be 1125 // absent 1126 if (($to_match->claimed_id === null) && 1127 ($to_match->local_id !== null)) { 1128 return new Auth_OpenID_FailureResponse($endpoint, 1129 'openid.identity is present without openid.claimed_id'); 1130 } 1131 1132 if (($to_match->claimed_id !== null) && 1133 ($to_match->local_id === null)) { 1134 return new Auth_OpenID_FailureResponse($endpoint, 1135 'openid.claimed_id is present without openid.identity'); 1136 } 1137 1138 if ($to_match->claimed_id === null) { 1139 // This is a response without identifiers, so there's 1140 // really no checking that we can do, so return an 1141 // endpoint that's for the specified `openid.op_endpoint' 1142 return Auth_OpenID_ServiceEndpoint::fromOPEndpointURL( 1143 $to_match->server_url); 1144 } 1145 1146 if (!$endpoint) { 1147 // The claimed ID doesn't match, so we have to do 1148 // discovery again. This covers not using sessions, OP 1149 // identifier endpoints and responses that didn't match 1150 // the original request. 1151 // oidutil.log('No pre-discovered information supplied.') 1152 return $this->_discoverAndVerify($to_match->claimed_id, 1153 array($to_match)); 1154 } else { 1155 1156 // The claimed ID matches, so we use the endpoint that we 1157 // discovered in initiation. This should be the most 1158 // common case. 1159 $result = $this->_verifyDiscoverySingle($endpoint, $to_match); 1160 1161 if (Auth_OpenID::isFailure($result)) { 1162 $endpoint = $this->_discoverAndVerify($to_match->claimed_id, 1163 array($to_match)); 1164 if (Auth_OpenID::isFailure($endpoint)) { 1165 return $endpoint; 1166 } 1167 } 1168 } 1169 1170 // The endpoint we return should have the claimed ID from the 1171 // message we just verified, fragment and all. 1172 if ($endpoint->claimed_id != $to_match->claimed_id) { 1173 $endpoint->claimed_id = $to_match->claimed_id; 1174 } 1175 1176 return $endpoint; 1177 } 1178 1179 /** 1180 * @access private 1181 */ 1182 function _discoverAndVerify($claimed_id, $to_match_endpoints) 1183 { 1184 // oidutil.log('Performing discovery on %s' % (claimed_id,)) 1185 list($unused, $services) = call_user_func($this->discoverMethod, 1186 $claimed_id, 1187 $this->fetcher); 1188 1189 if (!$services) { 1190 return new Auth_OpenID_FailureResponse(null, 1191 sprintf("No OpenID information found at %s", 1192 $claimed_id)); 1193 } 1194 1195 return $this->_verifyDiscoveryServices($claimed_id, $services, 1196 $to_match_endpoints); 1197 } 1198 1199 /** 1200 * @access private 1201 */ 1202 function _verifyDiscoveryServices($claimed_id, 1203 &$services, &$to_match_endpoints) 1204 { 1205 // Search the services resulting from discovery to find one 1206 // that matches the information from the assertion 1207 1208 foreach ($services as $endpoint) { 1209 foreach ($to_match_endpoints as $to_match_endpoint) { 1210 $result = $this->_verifyDiscoverySingle($endpoint, 1211 $to_match_endpoint); 1212 1213 if (!Auth_OpenID::isFailure($result)) { 1214 // It matches, so discover verification has 1215 // succeeded. Return this endpoint. 1216 return $endpoint; 1217 } 1218 } 1219 } 1220 1221 return new Auth_OpenID_FailureResponse(null, 1222 sprintf('No matching endpoint found after discovering %s', 1223 $claimed_id)); 1224 } 1225 1226 /** 1227 * Extract the nonce from an OpenID 1 response. Return the nonce 1228 * from the BARE_NS since we independently check the return_to 1229 * arguments are the same as those in the response message. 1230 * 1231 * See the openid1_nonce_query_arg_name class variable 1232 * 1233 * @returns $nonce The nonce as a string or null 1234 * 1235 * @access private 1236 */ 1237 function _idResGetNonceOpenID1($message, $endpoint) 1238 { 1239 return $message->getArg(Auth_OpenID_BARE_NS, 1240 $this->openid1_nonce_query_arg_name); 1241 } 1242 1243 /** 1244 * @access private 1245 */ 1246 function _idResCheckNonce($message, $endpoint) 1247 { 1248 if ($message->isOpenID1()) { 1249 // This indicates that the nonce was generated by the consumer 1250 $nonce = $this->_idResGetNonceOpenID1($message, $endpoint); 1251 $server_url = ''; 1252 } else { 1253 $nonce = $message->getArg(Auth_OpenID_OPENID2_NS, 1254 'response_nonce'); 1255 1256 $server_url = $endpoint->server_url; 1257 } 1258 1259 if ($nonce === null) { 1260 return new Auth_OpenID_FailureResponse($endpoint, 1261 "Nonce missing from response"); 1262 } 1263 1264 $parts = Auth_OpenID_splitNonce($nonce); 1265 1266 if ($parts === null) { 1267 return new Auth_OpenID_FailureResponse($endpoint, 1268 "Malformed nonce in response"); 1269 } 1270 1271 list($timestamp, $salt) = $parts; 1272 1273 if (!$this->store->useNonce($server_url, $timestamp, $salt)) { 1274 return new Auth_OpenID_FailureResponse($endpoint, 1275 "Nonce already used or out of range"); 1276 } 1277 1278 return null; 1279 } 1280 1281 /** 1282 * @access private 1283 */ 1284 function _idResCheckForFields($message) 1285 { 1286 $basic_fields = array('return_to', 'assoc_handle', 'sig', 'signed'); 1287 $basic_sig_fields = array('return_to', 'identity'); 1288 1289 $require_fields = array( 1290 Auth_OpenID_OPENID2_NS => array_merge($basic_fields, 1291 array('op_endpoint')), 1292 1293 Auth_OpenID_OPENID1_NS => array_merge($basic_fields, 1294 array('identity')) 1295 ); 1296 1297 $require_sigs = array( 1298 Auth_OpenID_OPENID2_NS => array_merge($basic_sig_fields, 1299 array('response_nonce', 1300 'claimed_id', 1301 'assoc_handle')), 1302 Auth_OpenID_OPENID1_NS => array_merge($basic_sig_fields, 1303 array('nonce')) 1304 ); 1305 1306 foreach ($require_fields[$message->getOpenIDNamespace()] as $field) { 1307 if (!$message->hasKey(Auth_OpenID_OPENID_NS, $field)) { 1308 return new Auth_OpenID_FailureResponse(null, 1309 "Missing required field '".$field."'"); 1310 } 1311 } 1312 1313 $signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS, 1314 'signed', 1315 Auth_OpenID_NO_DEFAULT); 1316 if (Auth_OpenID::isFailure($signed_list_str)) { 1317 return $signed_list_str; 1318 } 1319 $signed_list = explode(',', $signed_list_str); 1320 1321 foreach ($require_sigs[$message->getOpenIDNamespace()] as $field) { 1322 // Field is present and not in signed list 1323 if ($message->hasKey(Auth_OpenID_OPENID_NS, $field) && 1324 (!in_array($field, $signed_list))) { 1325 return new Auth_OpenID_FailureResponse(null, 1326 "'".$field."' not signed"); 1327 } 1328 } 1329 1330 return null; 1331 } 1332 1333 /** 1334 * @access private 1335 */ 1336 function _checkAuth($message, $server_url) 1337 { 1338 $request = $this->_createCheckAuthRequest($message); 1339 if ($request === null) { 1340 return false; 1341 } 1342 1343 $resp_message = $this->_makeKVPost($request, $server_url); 1344 if (($resp_message === null) || 1345 (is_a($resp_message, 'Auth_OpenID_ServerErrorContainer'))) { 1346 return false; 1347 } 1348 1349 return $this->_processCheckAuthResponse($resp_message, $server_url); 1350 } 1351 1352 /** 1353 * @access private 1354 */ 1355 function _createCheckAuthRequest($message) 1356 { 1357 $signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed'); 1358 if ($signed) { 1359 foreach (explode(',', $signed) as $k) { 1360 $value = $message->getAliasedArg($k); 1361 if ($value === null) { 1362 return null; 1363 } 1364 } 1365 } 1366 $ca_message = $message->copy(); 1367 $ca_message->setArg(Auth_OpenID_OPENID_NS, 'mode', 1368 'check_authentication'); 1369 return $ca_message; 1370 } 1371 1372 /** 1373 * @access private 1374 */ 1375 function _processCheckAuthResponse($response, $server_url) 1376 { 1377 $is_valid = $response->getArg(Auth_OpenID_OPENID_NS, 'is_valid', 1378 'false'); 1379 1380 $invalidate_handle = $response->getArg(Auth_OpenID_OPENID_NS, 1381 'invalidate_handle'); 1382 1383 if ($invalidate_handle !== null) { 1384 $this->store->removeAssociation($server_url, 1385 $invalidate_handle); 1386 } 1387 1388 if ($is_valid == 'true') { 1389 return true; 1390 } 1391 1392 return false; 1393 } 1394 1395 /** 1396 * Adapt a POST response to a Message. 1397 * 1398 * @param $response Result of a POST to an OpenID endpoint. 1399 * 1400 * @access private 1401 */ 1402 function _httpResponseToMessage($response, $server_url) 1403 { 1404 // Should this function be named Message.fromHTTPResponse instead? 1405 $response_message = Auth_OpenID_Message::fromKVForm($response->body); 1406 1407 if ($response->status == 400) { 1408 return Auth_OpenID_ServerErrorContainer::fromMessage( 1409 $response_message); 1410 } else if ($response->status != 200 and $response->status != 206) { 1411 return null; 1412 } 1413 1414 return $response_message; 1415 } 1416 1417 /** 1418 * @access private 1419 */ 1420 function _makeKVPost($message, $server_url) 1421 { 1422 $body = $message->toURLEncoded(); 1423 $resp = $this->fetcher->post($server_url, $body); 1424 1425 if ($resp === null) { 1426 return null; 1427 } 1428 1429 return $this->_httpResponseToMessage($resp, $server_url); 1430 } 1431 1432 /** 1433 * @access private 1434 */ 1435 function _getAssociation($endpoint) 1436 { 1437 if (!$this->_use_assocs) { 1438 return null; 1439 } 1440 1441 $assoc = $this->store->getAssociation($endpoint->server_url); 1442 1443 if (($assoc === null) || 1444 ($assoc->getExpiresIn() <= 0)) { 1445 1446 $assoc = $this->_negotiateAssociation($endpoint); 1447 1448 if ($assoc !== null) { 1449 $this->store->storeAssociation($endpoint->server_url, 1450 $assoc); 1451 } 1452 } 1453 1454 return $assoc; 1455 } 1456 1457 /** 1458 * Handle ServerErrors resulting from association requests. 1459 * 1460 * @return $result If server replied with an C{unsupported-type} 1461 * error, return a tuple of supported C{association_type}, 1462 * C{session_type}. Otherwise logs the error and returns null. 1463 * 1464 * @access private 1465 */ 1466 function _extractSupportedAssociationType(&$server_error, &$endpoint, 1467 $assoc_type) 1468 { 1469 // Any error message whose code is not 'unsupported-type' 1470 // should be considered a total failure. 1471 if (($server_error->error_code != 'unsupported-type') || 1472 ($server_error->message->isOpenID1())) { 1473 return null; 1474 } 1475 1476 // The server didn't like the association/session type that we 1477 // sent, and it sent us back a message that might tell us how 1478 // to handle it. 1479 1480 // Extract the session_type and assoc_type from the error 1481 // message 1482 $assoc_type = $server_error->message->getArg(Auth_OpenID_OPENID_NS, 1483 'assoc_type'); 1484 1485 $session_type = $server_error->message->getArg(Auth_OpenID_OPENID_NS, 1486 'session_type'); 1487 1488 if (($assoc_type === null) || ($session_type === null)) { 1489 return null; 1490 } else if (!$this->negotiator->isAllowed($assoc_type, 1491 $session_type)) { 1492 return null; 1493 } else { 1494 return array($assoc_type, $session_type); 1495 } 1496 } 1497 1498 /** 1499 * @access private 1500 */ 1501 function _negotiateAssociation($endpoint) 1502 { 1503 // Get our preferred session/association type from the negotiatior. 1504 list($assoc_type, $session_type) = $this->negotiator->getAllowedType(); 1505 1506 $assoc = $this->_requestAssociation( 1507 $endpoint, $assoc_type, $session_type); 1508 1509 if (Auth_OpenID::isFailure($assoc)) { 1510 return null; 1511 } 1512 1513 if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) { 1514 $why = $assoc; 1515 1516 $supportedTypes = $this->_extractSupportedAssociationType( 1517 $why, $endpoint, $assoc_type); 1518 1519 if ($supportedTypes !== null) { 1520 list($assoc_type, $session_type) = $supportedTypes; 1521 1522 // Attempt to create an association from the assoc_type 1523 // and session_type that the server told us it 1524 // supported. 1525 $assoc = $this->_requestAssociation( 1526 $endpoint, $assoc_type, $session_type); 1527 1528 if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) { 1529 // Do not keep trying, since it rejected the 1530 // association type that it told us to use. 1531 // oidutil.log('Server %s refused its suggested association 1532 // 'type: session_type=%s, assoc_type=%s' 1533 // % (endpoint.server_url, session_type, 1534 // assoc_type)) 1535 return null; 1536 } else { 1537 return $assoc; 1538 } 1539 } else { 1540 return null; 1541 } 1542 } else { 1543 return $assoc; 1544 } 1545 } 1546 1547 /** 1548 * @access private 1549 */ 1550 function _requestAssociation($endpoint, $assoc_type, $session_type) 1551 { 1552 list($assoc_session, $args) = $this->_createAssociateRequest( 1553 $endpoint, $assoc_type, $session_type); 1554 1555 $response_message = $this->_makeKVPost($args, $endpoint->server_url); 1556 1557 if ($response_message === null) { 1558 // oidutil.log('openid.associate request failed: %s' % (why[0],)) 1559 return null; 1560 } else if (is_a($response_message, 1561 'Auth_OpenID_ServerErrorContainer')) { 1562 return $response_message; 1563 } 1564 1565 return $this->_extractAssociation($response_message, $assoc_session); 1566 } 1567 1568 /** 1569 * @access private 1570 */ 1571 function _extractAssociation(&$assoc_response, &$assoc_session) 1572 { 1573 // Extract the common fields from the response, raising an 1574 // exception if they are not found 1575 $assoc_type = $assoc_response->getArg( 1576 Auth_OpenID_OPENID_NS, 'assoc_type', 1577 Auth_OpenID_NO_DEFAULT); 1578 1579 if (Auth_OpenID::isFailure($assoc_type)) { 1580 return $assoc_type; 1581 } 1582 1583 $assoc_handle = $assoc_response->getArg( 1584 Auth_OpenID_OPENID_NS, 'assoc_handle', 1585 Auth_OpenID_NO_DEFAULT); 1586 1587 if (Auth_OpenID::isFailure($assoc_handle)) { 1588 return $assoc_handle; 1589 } 1590 1591 // expires_in is a base-10 string. The Python parsing will 1592 // accept literals that have whitespace around them and will 1593 // accept negative values. Neither of these are really in-spec, 1594 // but we think it's OK to accept them. 1595 $expires_in_str = $assoc_response->getArg( 1596 Auth_OpenID_OPENID_NS, 'expires_in', 1597 Auth_OpenID_NO_DEFAULT); 1598 1599 if (Auth_OpenID::isFailure($expires_in_str)) { 1600 return $expires_in_str; 1601 } 1602 1603 $expires_in = Auth_OpenID::intval($expires_in_str); 1604 if ($expires_in === false) { 1605 1606 $err = sprintf("Could not parse expires_in from association ". 1607 "response %s", print_r($assoc_response, true)); 1608 return new Auth_OpenID_FailureResponse(null, $err); 1609 } 1610 1611 // OpenID 1 has funny association session behaviour. 1612 if ($assoc_response->isOpenID1()) { 1613 $session_type = $this->_getOpenID1SessionType($assoc_response); 1614 } else { 1615 $session_type = $assoc_response->getArg( 1616 Auth_OpenID_OPENID2_NS, 'session_type', 1617 Auth_OpenID_NO_DEFAULT); 1618 1619 if (Auth_OpenID::isFailure($session_type)) { 1620 return $session_type; 1621 } 1622 } 1623 1624 // Session type mismatch 1625 if ($assoc_session->session_type != $session_type) { 1626 if ($assoc_response->isOpenID1() && 1627 ($session_type == 'no-encryption')) { 1628 // In OpenID 1, any association request can result in 1629 // a 'no-encryption' association response. Setting 1630 // assoc_session to a new no-encryption session should 1631 // make the rest of this function work properly for 1632 // that case. 1633 $assoc_session = new Auth_OpenID_PlainTextConsumerSession(); 1634 } else { 1635 // Any other mismatch, regardless of protocol version 1636 // results in the failure of the association session 1637 // altogether. 1638 return null; 1639 } 1640 } 1641 1642 // Make sure assoc_type is valid for session_type 1643 if (!in_array($assoc_type, $assoc_session->allowed_assoc_types)) { 1644 return null; 1645 } 1646 1647 // Delegate to the association session to extract the secret 1648 // from the response, however is appropriate for that session 1649 // type. 1650 $secret = $assoc_session->extractSecret($assoc_response); 1651 1652 if ($secret === null) { 1653 return null; 1654 } 1655 1656 return Auth_OpenID_Association::fromExpiresIn( 1657 $expires_in, $assoc_handle, $secret, $assoc_type); 1658 } 1659 1660 /** 1661 * @access private 1662 */ 1663 function _createAssociateRequest($endpoint, $assoc_type, $session_type) 1664 { 1665 if (array_key_exists($session_type, $this->session_types)) { 1666 $session_type_class = $this->session_types[$session_type]; 1667 1668 if (is_callable($session_type_class)) { 1669 $assoc_session = $session_type_class(); 1670 } else { 1671 $assoc_session = new $session_type_class(); 1672 } 1673 } else { 1674 return null; 1675 } 1676 1677 $args = array( 1678 'mode' => 'associate', 1679 'assoc_type' => $assoc_type); 1680 1681 if (!$endpoint->compatibilityMode()) { 1682 $args['ns'] = Auth_OpenID_OPENID2_NS; 1683 } 1684 1685 // Leave out the session type if we're in compatibility mode 1686 // *and* it's no-encryption. 1687 if ((!$endpoint->compatibilityMode()) || 1688 ($assoc_session->session_type != 'no-encryption')) { 1689 $args['session_type'] = $assoc_session->session_type; 1690 } 1691 1692 $args = array_merge($args, $assoc_session->getRequest()); 1693 $message = Auth_OpenID_Message::fromOpenIDArgs($args); 1694 return array($assoc_session, $message); 1695 } 1696 1697 /** 1698 * Given an association response message, extract the OpenID 1.X 1699 * session type. 1700 * 1701 * This function mostly takes care of the 'no-encryption' default 1702 * behavior in OpenID 1. 1703 * 1704 * If the association type is plain-text, this function will 1705 * return 'no-encryption' 1706 * 1707 * @access private 1708 * @return $typ The association type for this message 1709 */ 1710 function _getOpenID1SessionType($assoc_response) 1711 { 1712 // If it's an OpenID 1 message, allow session_type to default 1713 // to None (which signifies "no-encryption") 1714 $session_type = $assoc_response->getArg(Auth_OpenID_OPENID1_NS, 1715 'session_type'); 1716 1717 // Handle the differences between no-encryption association 1718 // respones in OpenID 1 and 2: 1719 1720 // no-encryption is not really a valid session type for OpenID 1721 // 1, but we'll accept it anyway, while issuing a warning. 1722 if ($session_type == 'no-encryption') { 1723 // oidutil.log('WARNING: OpenID server sent "no-encryption"' 1724 // 'for OpenID 1.X') 1725 } else if (($session_type == '') || ($session_type === null)) { 1726 // Missing or empty session type is the way to flag a 1727 // 'no-encryption' response. Change the session type to 1728 // 'no-encryption' so that it can be handled in the same 1729 // way as OpenID 2 'no-encryption' respones. 1730 $session_type = 'no-encryption'; 1731 } 1732 1733 return $session_type; 1734 } 1735 } 1736 1737 /** 1738 * This class represents an authentication request from a consumer to 1739 * an OpenID server. 1740 * 1741 * @package OpenID 1742 */ 1743 class Auth_OpenID_AuthRequest { 1744 1745 /** 1746 * Initialize an authentication request with the specified token, 1747 * association, and endpoint. 1748 * 1749 * Users of this library should not create instances of this 1750 * class. Instances of this class are created by the library when 1751 * needed. 1752 */ 1753 function Auth_OpenID_AuthRequest(&$endpoint, $assoc) 1754 { 1755 $this->assoc = $assoc; 1756 $this->endpoint =& $endpoint; 1757 $this->return_to_args = array(); 1758 $this->message = new Auth_OpenID_Message( 1759 $endpoint->preferredNamespace()); 1760 $this->_anonymous = false; 1761 } 1762 1763 /** 1764 * Add an extension to this checkid request. 1765 * 1766 * $extension_request: An object that implements the extension 1767 * request interface for adding arguments to an OpenID message. 1768 */ 1769 function addExtension(&$extension_request) 1770 { 1771 $extension_request->toMessage($this->message); 1772 } 1773 1774 /** 1775 * Add an extension argument to this OpenID authentication 1776 * request. 1777 * 1778 * Use caution when adding arguments, because they will be 1779 * URL-escaped and appended to the redirect URL, which can easily 1780 * get quite long. 1781 * 1782 * @param string $namespace The namespace for the extension. For 1783 * example, the simple registration extension uses the namespace 1784 * 'sreg'. 1785 * 1786 * @param string $key The key within the extension namespace. For 1787 * example, the nickname field in the simple registration 1788 * extension's key is 'nickname'. 1789 * 1790 * @param string $value The value to provide to the server for 1791 * this argument. 1792 */ 1793 function addExtensionArg($namespace, $key, $value) 1794 { 1795 return $this->message->setArg($namespace, $key, $value); 1796 } 1797 1798 /** 1799 * Set whether this request should be made anonymously. If a 1800 * request is anonymous, the identifier will not be sent in the 1801 * request. This is only useful if you are making another kind of 1802 * request with an extension in this request. 1803 * 1804 * Anonymous requests are not allowed when the request is made 1805 * with OpenID 1. 1806 */ 1807 function setAnonymous($is_anonymous) 1808 { 1809 if ($is_anonymous && $this->message->isOpenID1()) { 1810 return false; 1811 } else { 1812 $this->_anonymous = $is_anonymous; 1813 return true; 1814 } 1815 } 1816 1817 /** 1818 * Produce a {@link Auth_OpenID_Message} representing this 1819 * request. 1820 * 1821 * @param string $realm The URL (or URL pattern) that identifies 1822 * your web site to the user when she is authorizing it. 1823 * 1824 * @param string $return_to The URL that the OpenID provider will 1825 * send the user back to after attempting to verify her identity. 1826 * 1827 * Not specifying a return_to URL means that the user will not be 1828 * returned to the site issuing the request upon its completion. 1829 * 1830 * @param bool $immediate If true, the OpenID provider is to send 1831 * back a response immediately, useful for behind-the-scenes 1832 * authentication attempts. Otherwise the OpenID provider may 1833 * engage the user before providing a response. This is the 1834 * default case, as the user may need to provide credentials or 1835 * approve the request before a positive response can be sent. 1836 */ 1837 function getMessage($realm, $return_to=null, $immediate=false) 1838 { 1839 if ($return_to) { 1840 $return_to = Auth_OpenID::appendArgs($return_to, 1841 $this->return_to_args); 1842 } else if ($immediate) { 1843 // raise ValueError( 1844 // '"return_to" is mandatory when 1845 //using "checkid_immediate"') 1846 return new Auth_OpenID_FailureResponse(null, 1847 "'return_to' is mandatory when using checkid_immediate"); 1848 } else if ($this->message->isOpenID1()) { 1849 // raise ValueError('"return_to" is 1850 // mandatory for OpenID 1 requests') 1851 return new Auth_OpenID_FailureResponse(null, 1852 "'return_to' is mandatory for OpenID 1 requests"); 1853 } else if ($this->return_to_args) { 1854 // raise ValueError('extra "return_to" arguments 1855 // were specified, but no return_to was specified') 1856 return new Auth_OpenID_FailureResponse(null, 1857 "extra 'return_to' arguments where specified, " . 1858 "but no return_to was specified"); 1859 } 1860 1861 if ($immediate) { 1862 $mode = 'checkid_immediate'; 1863 } else { 1864 $mode = 'checkid_setup'; 1865 } 1866 1867 $message = $this->message->copy(); 1868 if ($message->isOpenID1()) { 1869 $realm_key = 'trust_root'; 1870 } else { 1871 $realm_key = 'realm'; 1872 } 1873 1874 $message->updateArgs(Auth_OpenID_OPENID_NS, 1875 array( 1876 $realm_key => $realm, 1877 'mode' => $mode, 1878 'return_to' => $return_to)); 1879 1880 if (!$this->_anonymous) { 1881 if ($this->endpoint->isOPIdentifier()) { 1882 // This will never happen when we're in compatibility 1883 // mode, as long as isOPIdentifier() returns False 1884 // whenever preferredNamespace() returns OPENID1_NS. 1885 $claimed_id = $request_identity = 1886 Auth_OpenID_IDENTIFIER_SELECT; 1887 } else { 1888 $request_identity = $this->endpoint->getLocalID(); 1889 $claimed_id = $this->endpoint->claimed_id; 1890 } 1891 1892 // This is true for both OpenID 1 and 2 1893 $message->setArg(Auth_OpenID_OPENID_NS, 'identity', 1894 $request_identity); 1895 1896 if ($message->isOpenID2()) { 1897 $message->setArg(Auth_OpenID_OPENID2_NS, 'claimed_id', 1898 $claimed_id); 1899 } 1900 } 1901 1902 if ($this->assoc) { 1903 $message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle', 1904 $this->assoc->handle); 1905 } 1906 1907 return $message; 1908 } 1909 1910 function redirectURL($realm, $return_to = null, 1911 $immediate = false) 1912 { 1913 $message = $this->getMessage($realm, $return_to, $immediate); 1914 1915 if (Auth_OpenID::isFailure($message)) { 1916 return $message; 1917 } 1918 1919 return $message->toURL($this->endpoint->server_url); 1920 } 1921 1922 /** 1923 * Get html for a form to submit this request to the IDP. 1924 * 1925 * form_tag_attrs: An array of attributes to be added to the form 1926 * tag. 'accept-charset' and 'enctype' have defaults that can be 1927 * overridden. If a value is supplied for 'action' or 'method', it 1928 * will be replaced. 1929 */ 1930 function formMarkup($realm, $return_to=null, $immediate=false, 1931 $form_tag_attrs=null) 1932 { 1933 $message = $this->getMessage($realm, $return_to, $immediate); 1934 1935 if (Auth_OpenID::isFailure($message)) { 1936 return $message; 1937 } 1938 1939 return $message->toFormMarkup($this->endpoint->server_url, 1940 $form_tag_attrs); 1941 } 1942 1943 /** 1944 * Get a complete html document that will autosubmit the request 1945 * to the IDP. 1946 * 1947 * Wraps formMarkup. See the documentation for that function. 1948 */ 1949 function htmlMarkup($realm, $return_to=null, $immediate=false, 1950 $form_tag_attrs=null) 1951 { 1952 $form = $this->formMarkup($realm, $return_to, $immediate, 1953 $form_tag_attrs); 1954 1955 if (Auth_OpenID::isFailure($form)) { 1956 return $form; 1957 } 1958 return Auth_OpenID::autoSubmitHTML($form); 1959 } 1960 1961 function shouldSendRedirect() 1962 { 1963 return $this->endpoint->compatibilityMode(); 1964 } 1965 } 1966 1967 /** 1968 * The base class for responses from the Auth_OpenID_Consumer. 1969 * 1970 * @package OpenID 1971 */ 1972 class Auth_OpenID_ConsumerResponse { 1973 var $status = null; 1974 1975 function setEndpoint($endpoint) 1976 { 1977 $this->endpoint = $endpoint; 1978 if ($endpoint === null) { 1979 $this->identity_url = null; 1980 } else { 1981 $this->identity_url = $endpoint->claimed_id; 1982 } 1983 } 1984 1985 /** 1986 * Return the display identifier for this response. 1987 * 1988 * The display identifier is related to the Claimed Identifier, but the 1989 * two are not always identical. The display identifier is something the 1990 * user should recognize as what they entered, whereas the response's 1991 * claimed identifier (in the identity_url attribute) may have extra 1992 * information for better persistence. 1993 * 1994 * URLs will be stripped of their fragments for display. XRIs will 1995 * display the human-readable identifier (i-name) instead of the 1996 * persistent identifier (i-number). 1997 * 1998 * Use the display identifier in your user interface. Use 1999 * identity_url for querying your database or authorization server. 2000 * 2001 */ 2002 function getDisplayIdentifier() 2003 { 2004 if ($this->endpoint !== null) { 2005 return $this->endpoint->getDisplayIdentifier(); 2006 } 2007 return null; 2008 } 2009 } 2010 2011 /** 2012 * A response with a status of Auth_OpenID_SUCCESS. Indicates that 2013 * this request is a successful acknowledgement from the OpenID server 2014 * that the supplied URL is, indeed controlled by the requesting 2015 * agent. This has three relevant attributes: 2016 * 2017 * claimed_id - The identity URL that has been authenticated 2018 * 2019 * signed_args - The arguments in the server's response that were 2020 * signed and verified. 2021 * 2022 * status - Auth_OpenID_SUCCESS. 2023 * 2024 * @package OpenID 2025 */ 2026 class Auth_OpenID_SuccessResponse extends Auth_OpenID_ConsumerResponse { 2027 var $status = Auth_OpenID_SUCCESS; 2028 2029 /** 2030 * @access private 2031 */ 2032 function Auth_OpenID_SuccessResponse($endpoint, $message, $signed_args=null) 2033 { 2034 $this->endpoint = $endpoint; 2035 $this->identity_url = $endpoint->claimed_id; 2036 $this->signed_args = $signed_args; 2037 $this->message = $message; 2038 2039 if ($this->signed_args === null) { 2040 $this->signed_args = array(); 2041 } 2042 } 2043 2044 /** 2045 * Extract signed extension data from the server's response. 2046 * 2047 * @param string $prefix The extension namespace from which to 2048 * extract the extension data. 2049 */ 2050 function extensionResponse($namespace_uri, $require_signed) 2051 { 2052 if ($require_signed) { 2053 return $this->getSignedNS($namespace_uri); 2054 } else { 2055 return $this->message->getArgs($namespace_uri); 2056 } 2057 } 2058 2059 function isOpenID1() 2060 { 2061 return $this->message->isOpenID1(); 2062 } 2063 2064 function isSigned($ns_uri, $ns_key) 2065 { 2066 // Return whether a particular key is signed, regardless of 2067 // its namespace alias 2068 return in_array($this->message->getKey($ns_uri, $ns_key), 2069 $this->signed_args); 2070 } 2071 2072 function getSigned($ns_uri, $ns_key, $default = null) 2073 { 2074 // Return the specified signed field if available, otherwise 2075 // return default 2076 if ($this->isSigned($ns_uri, $ns_key)) { 2077 return $this->message->getArg($ns_uri, $ns_key, $default); 2078 } else { 2079 return $default; 2080 } 2081 } 2082 2083 function getSignedNS($ns_uri) 2084 { 2085 $args = array(); 2086 2087 $msg_args = $this->message->getArgs($ns_uri); 2088 if (Auth_OpenID::isFailure($msg_args)) { 2089 return null; 2090 } 2091 2092 foreach ($msg_args as $key => $value) { 2093 if (!$this->isSigned($ns_uri, $key)) { 2094 return null; 2095 } 2096 } 2097 2098 return $msg_args; 2099 } 2100 2101 /** 2102 * Get the openid.return_to argument from this response. 2103 * 2104 * This is useful for verifying that this request was initiated by 2105 * this consumer. 2106 * 2107 * @return string $return_to The return_to URL supplied to the 2108 * server on the initial request, or null if the response did not 2109 * contain an 'openid.return_to' argument. 2110 */ 2111 function getReturnTo() 2112 { 2113 return $this->getSigned(Auth_OpenID_OPENID_NS, 'return_to'); 2114 } 2115 } 2116 2117 /** 2118 * A response with a status of Auth_OpenID_FAILURE. Indicates that the 2119 * OpenID protocol has failed. This could be locally or remotely 2120 * triggered. This has three relevant attributes: 2121 * 2122 * claimed_id - The identity URL for which authentication was 2123 * attempted, if it can be determined. Otherwise, null. 2124 * 2125 * message - A message indicating why the request failed, if one is 2126 * supplied. Otherwise, null. 2127 * 2128 * status - Auth_OpenID_FAILURE. 2129 * 2130 * @package OpenID 2131 */ 2132 class Auth_OpenID_FailureResponse extends Auth_OpenID_ConsumerResponse { 2133 var $status = Auth_OpenID_FAILURE; 2134 2135 function Auth_OpenID_FailureResponse($endpoint, $message = null, 2136 $contact = null, $reference = null) 2137 { 2138 $this->setEndpoint($endpoint); 2139 $this->message = $message; 2140 $this->contact = $contact; 2141 $this->reference = $reference; 2142 } 2143 } 2144 2145 /** 2146 * A specific, internal failure used to detect type URI mismatch. 2147 * 2148 * @package OpenID 2149 */ 2150 class Auth_OpenID_TypeURIMismatch extends Auth_OpenID_FailureResponse { 2151 } 2152 2153 /** 2154 * Exception that is raised when the server returns a 400 response 2155 * code to a direct request. 2156 * 2157 * @package OpenID 2158 */ 2159 class Auth_OpenID_ServerErrorContainer { 2160 function Auth_OpenID_ServerErrorContainer($error_text, 2161 $error_code, 2162 $message) 2163 { 2164 $this->error_text = $error_text; 2165 $this->error_code = $error_code; 2166 $this->message = $message; 2167 } 2168 2169 /** 2170 * @access private 2171 */ 2172 function fromMessage($message) 2173 { 2174 $error_text = $message->getArg( 2175 Auth_OpenID_OPENID_NS, 'error', '<no error message supplied>'); 2176 $error_code = $message->getArg(Auth_OpenID_OPENID_NS, 'error_code'); 2177 return new Auth_OpenID_ServerErrorContainer($error_text, 2178 $error_code, 2179 $message); 2180 } 2181 } 2182 2183 /** 2184 * A response with a status of Auth_OpenID_CANCEL. Indicates that the 2185 * user cancelled the OpenID authentication request. This has two 2186 * relevant attributes: 2187 * 2188 * claimed_id - The identity URL for which authentication was 2189 * attempted, if it can be determined. Otherwise, null. 2190 * 2191 * status - Auth_OpenID_SUCCESS. 2192 * 2193 * @package OpenID 2194 */ 2195 class Auth_OpenID_CancelResponse extends Auth_OpenID_ConsumerResponse { 2196 var $status = Auth_OpenID_CANCEL; 2197 2198 function Auth_OpenID_CancelResponse($endpoint) 2199 { 2200 $this->setEndpoint($endpoint); 2201 } 2202 } 2203 2204 /** 2205 * A response with a status of Auth_OpenID_SETUP_NEEDED. Indicates 2206 * that the request was in immediate mode, and the server is unable to 2207 * authenticate the user without further interaction. 2208 * 2209 * claimed_id - The identity URL for which authentication was 2210 * attempted. 2211 * 2212 * setup_url - A URL that can be used to send the user to the server 2213 * to set up for authentication. The user should be redirected in to 2214 * the setup_url, either in the current window or in a new browser 2215 * window. Null in OpenID 2. 2216 * 2217 * status - Auth_OpenID_SETUP_NEEDED. 2218 * 2219 * @package OpenID 2220 */ 2221 class Auth_OpenID_SetupNeededResponse extends Auth_OpenID_ConsumerResponse { 2222 var $status = Auth_OpenID_SETUP_NEEDED; 2223 2224 function Auth_OpenID_SetupNeededResponse($endpoint, 2225 $setup_url = null) 2226 { 2227 $this->setEndpoint($endpoint); 2228 $this->setup_url = $setup_url; 2229 } 2230 } 2231 2232 ?>
titolo
Descrizione
Corpo
titolo
Descrizione
Corpo
titolo
Descrizione
Corpo
titolo
Corpo
| Generato il: Mon Oct 19 20:29:27 2009 | Generato con PHPXref 0.7 |