Apache Zeta Components Manual :: File Source for openid_filter.php

Source for file openid_filter.php

Documentation is available at openid_filter.php

  1. <?php
  2. /**
  3.  * File containing the ezcAuthenticationOpenidFilter class.
  4.  *
  5.  * Licensed to the Apache Software Foundation (ASF) under one
  6.  * or more contributor license agreements.  See the NOTICE file
  7.  * distributed with this work for additional information
  8.  * regarding copyright ownership.  The ASF licenses this file
  9.  * to you under the Apache License, Version 2.0 (the
  10.  * "License"); you may not use this file except in compliance
  11.  * with the License.  You may obtain a copy of the License at
  12.  * 
  13.  *   http://www.apache.org/licenses/LICENSE-2.0
  14.  * 
  15.  * Unless required by applicable law or agreed to in writing,
  16.  * software distributed under the License is distributed on an
  17.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  18.  * KIND, either express or implied.  See the License for the
  19.  * specific language governing permissions and limitations
  20.  * under the License.
  21.  *
  22.  * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
  23.  * @filesource
  24.  * @package Authentication
  25.  * @version //autogen//
  26.  */
  27.  
  28. /**
  29.  * Filter to authenticate against OpenID. Currently supporting OpenID 1.0, 1.1
  30.  * and 2.0.
  31.  *
  32.  * The filter takes an identifier (URL) as credentials, and performs these steps
  33.  * (by default, with redirection of the user agent to the OpenID provider):
  34.  *  1. Normalize the identifier
  35.  *  2. Discover the provider and delegate by requesting the URL
  36.  *       - first using the Yadis discovery protocol
  37.  *       - if Yadis fails then discover by parsing the HTML page source at URL
  38.  *  3. (Optional) OpenID associate request - for the so-called 'smart' (stateful) mode.
  39.  *  4. OpenID checkid_setup request. This step redirects the browser to the OpenID
  40.  *     provider discovered in step 2. After user enters his OpenID username and
  41.  *     password at this page and accepts the originating site, the browser is
  42.  *     redirected back to the originating site. The return URL can be changed
  43.  *     with the OpenID option returnUrl (see {@link ezcAuthenticationOpenidOptions}).
  44.  *  5. OpenID check_authentication request. After the redirection from the provider
  45.  *     to the originating site, the values provided by the provider must be checked
  46.  *     in an extra request against the provider. The provider responds with is_valid
  47.  *     true or false.
  48.  *
  49.  * The OpenID request checkid_immediate is supported, which allows for user
  50.  * authentication in a pop-up window or iframe (or similar techniques). The steps
  51.  * of the authentication process are the same as above, but step 4 changes as
  52.  * follows:
  53.  *  4. OpenID checkid_immediate request. This step asks the OpenID provider if the
  54.  *     user can be authenticated on the spot, with no redirection. If the user
  55.  *     cannot be authenticated, the provider sends back a setup URL, which the
  56.  *     application can use in a pop-up window or iframe to display to the user
  57.  *     so that he can authenticate himself to the OpenID provider. After user
  58.  *     enters his OpenID username and password at this page and accepts the
  59.  *     originating site, the pop-up window or iframe is redirected to the
  60.  *     return URL value (which should be a different page than the page which
  61.  *     opens the pop-up window). The return URL page will then inform the
  62.  *     main page of success or failure through JavaScript, and the main page
  63.  *     can do the action that it needs to perform based on the outcome in the
  64.  *     pop-up page. The checkid_immediate mode is enabled by setting the
  65.  *     option immediate to true.
  66.  *
  67.  * For example, this is one simple way of implementing checkid_immediate:
  68.  *  - the main page contains the OpenID login form (where the user types in his
  69.  *    OpenID identifier). This page contains also a hidded form value which
  70.  *    specifies to which page to return to in the pop-up window. The Enter key
  71.  *    and the submit button should be disabled on the form. When user clicks on
  72.  *    the Login button, the main page should employ AJAX to request the return
  73.  *    URL. When the return URL finishes loading, the main page will read from the
  74.  *    return URL page the setup URL and it will open it in a pop-up/iframe.
  75.  *  - the return URL page enables the option immediate to the OpenID filter, and
  76.  *    runs the filter. It gets back the setup URL and it echoes it to be picked-up
  77.  *    by the main page once the return URL page will finish loading. The setup URL
  78.  *    should be the only thing that the return URL page is echoing, to not interfere
  79.  *    with the main page.
  80.  *  - in the pop-up/iframe the setup URL will load, which basically depends on
  81.  *    the OpenID provider how it is handled by the user. After the user enters
  82.  *    his credentials on the setup URL page, he will be redirected to the return URL,
  83.  *    which should detect this, and which should inform the main page that the
  84.  *    user was authenticated to the OpenID provider.
  85.  *
  86.  * As this mode required advanced JavaScript techniques and AJAX, no example
  87.  * source code will be provided here as it is out of the scope of this
  88.  * documentation. A rudimentary example is provided in the tutorial.
  89.  *
  90.  * Specifications:
  91.  *  - OpenID 1.0: {@link http://openid.net/specs/openid-simple-registration-extension-1_0.html}
  92.  *  - OpenID 1.1: {@link http://openid.net/specs/openid-authentication-1_1.html}
  93.  *  - OpenID 2.0: {@link http://openid.net/specs/openid-authentication-2_0.html}
  94.  *  - Yadis  1.0: {@link http://yadis.org}
  95.  *  - XRDS discovery: {@link http://docs.oasis-open.org/xri/2.0/specs/cd02/xri-resolution-V2.0-cd-02.html}
  96.  *
  97.  * To specify which OpenID version to authenticate against, use the
  98.  * openidVersion option (default is '1.1'):
  99.  *
  100.  * <code>
  101.  * $options = new ezcAuthenticationOpenidOptions();
  102.  * $options->version = ezcAuthenticationOpenidFilter::VERSION_2_0;
  103.  * $filter = new ezcAuthenticationOpenidFilter( $options );
  104.  * </code>
  105.  *
  106.  * OpenID 2.0 will use XRDS discovery by default instead of Yadis. If XRDS
  107.  * discovery fails (or if the specified version is not '2.0') then Yadis
  108.  * discovery will be performed, and if Yadis fails then HTML discovery
  109.  * will be performed.
  110.  *
  111.  * Example of use (authentication code + login form + logout support) - stateless ('dumb'):
  112.  * <code>
  113.  * // no headers should be sent before calling $session->start()
  114.  * $session = new ezcAuthenticationSession();
  115.  * $session->start();
  116.  *
  117.  * $url = isset( $_GET['openid_identifier'] ) ? $_GET['openid_identifier'] : $session->load();
  118.  * $action = isset( $_GET['action'] ) ? strtolower( $_GET['action'] ) : null;
  119.  *
  120.  * $credentials = new ezcAuthenticationIdCredentials( $url );
  121.  * $authentication = new ezcAuthentication( $credentials );
  122.  * $authentication->session = $session;
  123.  *
  124.  * if ( $action === 'logout' )
  125.  * {
  126.  *     $session->destroy();
  127.  * }
  128.  * else
  129.  * {
  130.  *     $filter = new ezcAuthenticationOpenidFilter();
  131.  *     $authentication->addFilter( $filter );
  132.  * }
  133.  *
  134.  * if ( !$authentication->run() )
  135.  * {
  136.  *     // authentication did not succeed, so inform the user
  137.  *     $status = $authentication->getStatus();
  138.  *     $err = array(
  139.  *              'ezcAuthenticationOpenidFilter' => array(
  140.  *                  ezcAuthenticationOpenidFilter::STATUS_SIGNATURE_INCORRECT => 'OpenID said the provided identifier was incorrect',
  141.  *                  ezcAuthenticationOpenidFilter::STATUS_CANCELLED => 'The OpenID authentication was cancelled',
  142.  *                  ezcAuthenticationOpenidFilter::STATUS_URL_INCORRECT => 'The identifier you provided is invalid'
  143.  *                  ),
  144.  *              'ezcAuthenticationSession' => array(
  145.  *                  ezcAuthenticationSession::STATUS_EMPTY => '',
  146.  *                  ezcAuthenticationSession::STATUS_EXPIRED => 'Session expired'
  147.  *                  )
  148.  *              );
  149.  *     foreach ( $status as $line )
  150.  *     {
  151.  *         list( $key, $value ) = each( $line );
  152.  *         echo $err[$key][$value] . "\n";
  153.  *     }
  154.  * ?>
  155.  * Please login with your OpenID identifier (an URL, eg. www.example.com or http://www.example.com):
  156.  * <form method="GET" action="">
  157.  * <input type="hidden" name="action" value="login" />
  158.  * <img src="http://openid.net/login-bg.gif" /> <input type="text" name="openid_identifier" />
  159.  * <input type="submit" value="Login" />
  160.  * </form>
  161.  *
  162.  * <?php
  163.  * }
  164.  * else
  165.  * {
  166.  * ?>
  167.  *
  168.  * You are logged-in as <b><?php echo $url; ?></b> | <a href="?action=logout">Logout</a>
  169.  *
  170.  * <?php
  171.  * }
  172.  * ?>
  173.  * </code>
  174.  *
  175.  * To use stateful ('smart') mode, the only changes to the above example are in
  176.  * the else branch of the "if ( $action === 'logout' )":
  177.  * <code>
  178.  * // ...
  179.  * if ( $action === 'logout' )
  180.  * {
  181.  *     $session->destroy();
  182.  * }
  183.  * else
  184.  * {
  185.  *     $options = new ezcAuthenticationOpenidOptions();
  186.  *     $options->mode = ezcAuthenticationOpenidFilter::MODE_SMART;
  187.  *     $options->store = new ezcAuthenticationOpenidFileStore( '/tmp/store' );
  188.  *     $filter = new ezcAuthenticationOpenidFilter( $options );
  189.  *     $authentication->addFilter( $filter );
  190.  * }
  191.  * // ...
  192.  * </code>
  193.  *
  194.  * Extra data can be fetched from the OpenID provider during the authentication
  195.  * process, by registering the data to be fetched before calling run(). Example:
  196.  * <code>
  197.  * // $filter is an ezcAuthenticationOpenidFilter object
  198.  * $filter->registerFetchData( array( 'fullname', 'gender', 'country', 'language' ) );
  199.  *
  200.  * // after run()
  201.  * $data = $filter->fetchData();
  202.  * </code>
  203.  *
  204.  * The $data array will be something like:
  205.  * <code>
  206.  * array( 'fullname' => array( 'John Doe' ),
  207.  *        'gender' => array( 'M' ),
  208.  *        'country' => array( 'US' ),
  209.  *        'language' => array( 'FR' )
  210.  *      );
  211.  * </code>
  212.  *
  213.  * The extra data which is possible to be fetched during the authentication
  214.  * process is:
  215.  *  - nickname - the user's nickname (short name, alias)
  216.  *  - email - the user's email address
  217.  *  - fullname - the user's full name
  218.  *  - dob - the user's date of birth as YYYY-MM-DD. Any component value whose
  219.  *    representation uses fewer than the specified number of digits should
  220.  *    be zero-padded (eg. 02 for February). If the user does not want to
  221.  *    reveal any particular component of this value, it should be zero
  222.  *    (eg. "1980-00-00" if the user is born in 1980 but does not want to
  223.  *    specify his month and day of birth)
  224.  *  - gender - the user's gender, "M" for male, "F" for female
  225.  *  - postcode - the user's postal code
  226.  *  - country - the user's country as an ISO3166 string, (eg. "US")
  227.  *  - language - the user's preferred language as an ISO639 string (eg. "FR")
  228.  *  - timezone - the user's timezone, for example "Europe/Paris"
  229.  *
  230.  * Note: if using the checkid_immediate mode (by setting the option immediate to
  231.  * true), then retrieval of extra data is not possible.
  232.  *
  233.  * @todo add support for fetching extra data as in OpenID attribute exchange
  234.  *        {@link http://issues.ez.no/14847}
  235.  * @todo add support for multiple URLs in each category at discovery
  236.  * @todo add support for XRI identifiers and discovery
  237.  * @todo check if the nonce handling is correct (openid.response_nonce?)
  238.  * @todo check return_to in the response
  239.  *
  240.  * @package Authentication
  241.  * @version //autogen//
  242.  * @mainclass
  243.  */
  244. {
  245.     /**
  246.      * The OpenID provider did not authorize the provided URL.
  247.      */
  248.     const STATUS_SIGNATURE_INCORRECT = 1;
  249.  
  250.     /**
  251.      * The OpenID provider did not return a valid nonce in the response.
  252.      */
  253.     const STATUS_NONCE_INCORRECT = 2;
  254.  
  255.     /**
  256.      * User cancelled the OpenID authentication.
  257.      */
  258.     const STATUS_CANCELLED = 3;
  259.  
  260.     /**
  261.      * The URL provided by user was empty, or the required information could
  262.      * not be discovered from it.
  263.      *
  264.      * @todo remove and return STATUS_SIGNATURE_INCORRECT instead?
  265.      */
  266.     const STATUS_URL_INCORRECT = 4;
  267.  
  268.     /**
  269.      * The OpenID server returned a setup URL after a checkid_immediate request,
  270.      * which is available by calling the getSetupUrl() method.
  271.      */
  272.     const STATUS_SETUP_URL = 5;
  273.  
  274.     /**
  275.      * OpenID authentication mode where the OpenID provider generates a secret
  276.      * for every request.
  277.      *
  278.      * The server (consumer) is stateless.
  279.      * An extra check_authentication request to the provider is needed.
  280.      * This is the default mode.
  281.      */
  282.     const MODE_DUMB = 1;
  283.  
  284.     /**
  285.      * OpenID authentication mode where the server generates a secret which will
  286.      * be shared with the OpenID provider.
  287.      *
  288.      * The server (consumer) is keeping state.
  289.      * The extra check_authentication request is not needed.
  290.      * The shared secret must be established once in a while (defined by the
  291.      * option secretValidity, default 1 day = 86400 seconds).
  292.      */
  293.     const MODE_SMART = 2;
  294.  
  295.     /**
  296.      * OpenID version 1.0.
  297.      */
  298.     const VERSION_1_0 = '1.0';
  299.  
  300.     /**
  301.      * OpenID version 1.1.
  302.      */
  303.     const VERSION_1_1 = '1.1';
  304.  
  305.     /**
  306.      * OpenID version 2.0.
  307.      */
  308.     const VERSION_2_0 = '2.0';
  309.  
  310.     /**
  311.      * The default value for p used in the Diffie-Hellman exchange.
  312.      *
  313.      * It is a confirmed prime number.
  314.      *
  315.      * @ignore
  316.      */
  317.     const DEFAULT_P '155172898181473697471232257763715539915724801966915404479707795314057629378541917580651227423698188993727816152646631438561595825688188889951272158842675419950341258706556549803580104870537681476726513255747040765857479291291572334510643245094715007229621094194349783925984760375594985848253359305585439638443';
  318.  
  319.     /**
  320.      * The default value for q used in the Diffie-Hellman exchange.
  321.      *
  322.      * @ignore
  323.      */
  324.     const DEFAULT_Q '2';
  325.  
  326.     /**
  327.      * Holds the setup URL retrieved during the checkid_immediate OpenID request.
  328.      *
  329.      * This URL can be used by the application to authenticate the user in a
  330.      * pop-up window or iframe (or similar techniques).
  331.      *
  332.      * @var string 
  333.      */
  334.     protected $setupUrl = false;
  335.  
  336.     /**
  337.      * Holds the attributes which will be requested during the authentication
  338.      * process.
  339.      *
  340.      * Usually it has this structure:
  341.      * <code>
  342.      * array( 'fullname', 'gender', 'country', 'language' );
  343.      * </code>
  344.      *
  345.      * @var array(string) 
  346.      */
  347.     protected $requestedData = array();
  348.  
  349.     /**
  350.      * Holds the extra data fetched during the authentication process.
  351.      *
  352.      * Usually it has this structure:
  353.      * <code>
  354.      * array( 'fullname' => array( 'John Doe' ),
  355.      *        'gender' => array( 'M' ),
  356.      *        'country' => array( 'NO' ),
  357.      *        'language' => array( 'FR' )
  358.      *      );
  359.      * </code>
  360.      *
  361.      * @var array(string=>mixed) 
  362.      */
  363.     protected $data = array();
  364.  
  365.     /**
  366.      * Creates a new object of this class.
  367.      *
  368.      * @param ezcAuthenticationOpenidOptions $options Options for this class
  369.      */
  370.     public function __constructezcAuthenticationOpenidOptions $options null )
  371.     {
  372.         $this->options = $options === null new ezcAuthenticationOpenidOptions($options;
  373.     }
  374.  
  375.     /**
  376.      * Runs the filter and returns a status code when finished.
  377.      *
  378.      * @throws ezcAuthenticationOpenidModeNotSupportedException
  379.      *          if trying to authenticate with an unsupported OpenID mode
  380.      *
  381.      * @param ezcAuthenticationIdCredentials $credentials Authentication credentials
  382.      * @return int 
  383.      */
  384.     public function run$credentials )
  385.     {
  386.         $source $this->options->requestSource;
  387.         $mode = isset$source['openid_mode'strtolower$source['openid_mode'null;
  388.         switch $mode )
  389.         {
  390.             case null:
  391.                 if empty$credentials->id ) )
  392.                 {
  393.                     return self::STATUS_URL_INCORRECT;
  394.                 }
  395.  
  396.                 $providers $this->discover$credentials->id );
  397.  
  398.                 // @todo add support for multiple URLs in each category
  399.                 if !$provider $this->getProvider$providers ) )
  400.                 {
  401.                     return self::STATUS_URL_INCORRECT;
  402.                 }
  403.  
  404.                 $params $this->createCheckidRequest$credentials->id$providers );
  405.  
  406.                 if $this->options->immediate === true )
  407.                 {
  408.                     $params['openid.mode''checkid_immediate';
  409.                     $response $this->checkImmediate$provider$params );
  410.  
  411.                     if $response !== false )
  412.                     {
  413.                         $this->setupUrl = $response;
  414.                         return self::STATUS_SETUP_URL;
  415.                     }
  416.                     else
  417.                     {
  418.                         return self::STATUS_URL_INCORRECT;
  419.                     }
  420.                 }
  421.                 else
  422.                 {
  423.                     $params['openid.mode''checkid_setup';
  424.                     $this->redirectToOpenidProvider$provider$params );
  425.                 }
  426.                 break;
  427.  
  428.             case 'id_res':
  429.                 $assocHandle = isset$source['openid_assoc_handle'$source['openid_assoc_handle'null;
  430.                 $identity = isset$source['openid_identity'$source['openid_identity'null;
  431.                 $sig = isset$source['openid_sig'$source['openid_sig'null;
  432.                 $signed = isset$source['openid_signed'$source['openid_signed'null;
  433.                 $returnTo = isset$source['openid_return_to'$source['openid_return_to'null;
  434.                 $claimedId = isset$source['openid_claimed_id'$source['openid_claimed_id'null;
  435.  
  436.                 // @todo add verification of openid_identity and openid_claimed_id to the initial openid_identifier
  437.  
  438.                 if $this->options->store !== null )
  439.                 {
  440.                     $nonce ezcAuthenticationUrl::fetchQuery$returnTo$this->options->nonceKey );
  441.                     if $nonce !== null )
  442.                     {
  443.                         $nonceTimestamp $this->options->store->useNonce$nonce );
  444.                         if $nonceTimestamp === false || time($nonceTimestamp $this->options->nonceValidity )
  445.                         {
  446.                             return self::STATUS_NONCE_INCORRECT;
  447.                         }
  448.                     }
  449.                 }
  450.  
  451.                 $params array(
  452.                     'openid.assoc_handle' => $assocHandle,
  453.                     'openid.signed' => $signed,
  454.                     'openid.sig' => $sig,
  455.                     'openid.mode' => 'id_res'
  456.                 );
  457.  
  458.                 $signed explode','$signed );
  459.                 for $i 0$i count$signed )$i++ )
  460.                 {
  461.                     $s str_replace'.''_'$signed[$i);
  462.                     $c $source['openid_' $s];
  463.                     $params['openid.' $signed[$i]] = isset$params['openid.' $s$params['openid.' $s$c;
  464.                     if strpos$s'sreg_' !== false )
  465.                     {
  466.                         $this->data[str_replace'sreg_'''$s )array$c );
  467.                     }
  468.                 }
  469.  
  470.                 if isset$source['openid_op_endpoint') )
  471.                 {
  472.                     // if the endpoint is available then use it, otherwise discover it
  473.                     $provider $source['openid_op_endpoint'];
  474.  
  475.                     // @todo check how to detect OpenID version from id_res response
  476.                 }
  477.                 else
  478.                 {
  479.                     // @todo cache this somewhere (in the request URL for example)
  480.                     $providers $this->discover$credentials->id );
  481.  
  482.                     // @todo add support for multiple URLs in each category
  483.                     if !$provider $this->getProvider$providers ) )
  484.                     {
  485.                         return self::STATUS_URL_INCORRECT;
  486.                     }
  487.                 }
  488.  
  489.                 if $this->options->mode === self::MODE_SMART )
  490.                 {
  491.                     $store $this->options->store;
  492.                     if $store !== null )
  493.                     {
  494.                         $association $store->getAssociation$provider );
  495.                         if $association !== false &&
  496.                              time($association->issued <= $association->validity
  497.                            )
  498.                         {
  499.                             if $this->checkSignatureSmart$association$params ) )
  500.                             {
  501.                                 return self::STATUS_OK;
  502.                             }
  503.                             else
  504.                             {
  505.                                 return self::STATUS_SIGNATURE_INCORRECT;
  506.                             }
  507.                         }
  508.                     }
  509.                 }
  510.  
  511.                 // if smart mode didn't succeed continue with the dumb mode as usual
  512.                 $params['openid.mode''check_authentication';
  513.  
  514.                 if $this->options->openidVersion === self::VERSION_2_0 )
  515.                 {
  516.                     $params['openid.ns''http://specs.openid.net/auth/2.0';
  517.                 }
  518.  
  519.                 foreach $params as $key => $value )
  520.                 {
  521.                     $params[$keyurlencode$value );
  522.                 }
  523.  
  524.                 if $this->checkSignature$provider$params ) )
  525.                 {
  526.                     return self::STATUS_OK;
  527.                 }
  528.                 break;
  529.  
  530.             case 'checkid_setup':
  531.                 return self::STATUS_CANCELLED;
  532.  
  533.             case 'cancel':
  534.                 return self::STATUS_CANCELLED;
  535.  
  536.             default:
  537.                 throw new ezcAuthenticationOpenidModeNotSupportedException$mode );
  538.         }
  539.         return self::STATUS_SIGNATURE_INCORRECT;
  540.     }
  541.  
  542.     /**
  543.      * Returns an array of parameters for use in an OpenID check_id request.
  544.      *
  545.      * @param string $id The OpenID identifier from the user
  546.      * @param array(string) $providers OpenID providers retrieved during discovery
  547.      * @return array(string=>array) 
  548.      */
  549.     public function createCheckidRequest$idarray $providers )
  550.     {
  551.         $provider $providers['openid.server'][0];
  552.  
  553.         // if a delegate is found, it is used instead of the credentials
  554.         $identity = isset$providers['openid.delegate'][0$providers['openid.delegate'][0:
  555.                                                                 $id;
  556.  
  557.         $host = isset$_SERVER["HTTP_HOST"$_SERVER["HTTP_HOST"null;
  558.         $path = isset$_SERVER["REQUEST_URI"$_SERVER["REQUEST_URI"null;
  559.  
  560.         if $this->options->mode === self::MODE_SMART )
  561.         {
  562.             $store $this->options->store;
  563.             if $store !== null )
  564.             {
  565.                 $association $store->getAssociation$provider );
  566.                 if $association === false ||
  567.                      time($association->issued $association->validity
  568.                    )
  569.                 {
  570.                     $params $this->createAssociateRequest();
  571.                     $result $this->associate$provider$params );
  572.  
  573.                     $secret = isset$result['enc_mac_key'$result['enc_mac_key'$result['mac_key'];
  574.                     $association new ezcAuthenticationOpenidAssociation$result['assoc_handle'],
  575.                                                                            $secret,
  576.                                                                            time(),
  577.                                                                            $result['expires_in'],
  578.                                                                            $result['assoc_type');
  579.                     $store->storeAssociation$provider$association );
  580.                 }
  581.             }
  582.         }
  583.  
  584.         $returnUrl $this->options->returnUrl;
  585.         if $returnUrl === null )
  586.         {
  587.             $returnUrl "http://{$host}{$path}";
  588.         }
  589.  
  590.         $nonce $this->generateNonce$this->options->nonceLength );
  591.         $returnTo ezcAuthenticationUrl::appendQuery$returnUrl$this->options->nonceKey$nonce );
  592.         $trustRoot "http://{$host}";
  593.  
  594.         if $this->options->mode === self::MODE_SMART && $store !== null )
  595.         {
  596.             $store->storeNonce$nonce );
  597.         }
  598.  
  599.         $params array(
  600.             'openid.return_to' => urlencode$returnTo ),
  601.             'openid.trust_root' => urlencode$trustRoot ),
  602.             'openid.identity' => urlencode$id ),
  603.             );
  604.  
  605.         if $this->options->openidVersion === self::VERSION_2_0 )
  606.         {
  607.             $params['openid.identity' urlencode'http://specs.openid.net/auth/2.0/identifier_select' );
  608.             $params['openid.claimed_id' urlencode'http://specs.openid.net/auth/2.0/identifier_select' );
  609.             $params['openid.realm'urlencode$trustRoot );
  610.             $params['openid.ns'urlencode'http://specs.openid.net/auth/2.0' );
  611.         }
  612.  
  613.         if count$this->requestedData )
  614.         {
  615.             $params['openid.sreg.optional'implode','$this->requestedData );
  616.             $params['openid.ns.sreg'urlencode'http://openid.net/sreg/1.0' );
  617.         }
  618.  
  619.         if $this->options->mode === self::MODE_SMART && $store !== null )
  620.         {
  621.             $association $store->getAssociation$provider );
  622.             if $association !== false &&
  623.                  time($association->issued <= $association->validity
  624.                )
  625.             {
  626.                 $params['openid.assoc_handle'urlencode$association->handle );
  627.             }
  628.         }
  629.  
  630.         return $params;
  631.     }
  632.  
  633.     /**
  634.      * Returns an array of parameters for use in an OpenID associate request.
  635.      *
  636.      * @return array(string=>array) 
  637.      */
  638.     protected function createAssociateRequest()
  639.     {
  640.         $lib ezcAuthenticationMath::createBignumLibrary();
  641.         $p self::DEFAULT_P;
  642.         $q self::DEFAULT_Q;
  643.  
  644.         $private $lib->rand$p );
  645.         $public $lib->powmod$q$private$p );
  646.         $params array(
  647.             'openid.mode' => 'associate',
  648.             'openid.assoc_type' => 'HMAC-SHA1',
  649.  
  650.             // @todo add support for DH-SHA1 (is it needed if the connection is SSL?)
  651.             // 'openid.session_type' => 'DH-SHA1', // not supported yet
  652.             'openid.dh_modulus' => urlencodebase64_encode$lib->btwoc$p ) ) ),
  653.             'openid.dh_gen' => 2urlencodebase64_encode$lib->btwoc$q ) ) ),
  654.             'openid.dh_consumer_public' => urlencodebase64_encode$lib->btwoc$public ) ) )
  655.             );
  656.  
  657.         return $params;
  658.     }
  659.  
  660.     /**
  661.      * Discovers the OpenID server information from the provided URL.
  662.      *
  663.      * First the XRDS discovery is tried, if the openidVersion option is
  664.      * "2.0". If XRDS discovery fails, or if the openidVersion option is
  665.      * not "2.0", Yadis discovery is tried. If it doesn't succeed, then the
  666.      * HTML discovery is tried.
  667.      *
  668.      * The format of the returned array is (example):
  669.      * <code>
  670.      *   array( 'openid.server' => array( 0 => 'http://www.example-provider.com' ),
  671.      *          'openid.delegate' => array( 0 => 'http://www.example-delegate.com' )
  672.      *        );
  673.      * </code>
  674.      *
  675.      * @throws ezcAuthenticationOpenidConnectionException
  676.      *          if connection to the URL could not be opened
  677.      * @param string $url URL to connect to and discover the OpenID information
  678.      * @return array(string=>array) 
  679.      */
  680.     protected function discover$url )
  681.     {
  682.         $providers array();
  683.  
  684.         if $this->options->openidVersion === self::VERSION_2_0 )
  685.         {
  686.             $providers $this->discoverXrds$url );
  687.         }
  688.  
  689.         if count$providers === )
  690.         {
  691.             $providers $this->discoverYadis$url );
  692.             if count$providers === )
  693.             {
  694.                 $providers $this->discoverHtml$url );
  695.             }
  696.         }
  697.  
  698.         foreach $providers as $key => $provider )
  699.         {
  700.             // normalize array keys
  701.             if $key === 'openid2.provider' )
  702.             {
  703.                 unset$providers[$key);
  704.                 $providers['openid.server'$provider;
  705.             }
  706.  
  707.             if $key === 'openid2.local_id' )
  708.             {
  709.                 unset$providers[$key);
  710.                 $providers['openid.delegate'$provider;
  711.             }
  712.         }
  713.         return $providers;
  714.     }
  715.  
  716.     /**
  717.      * Discovers the OpenID server information from the provided URL using XRDS.
  718.      *
  719.      * The format of the returned array is (example):
  720.      * <code>
  721.      *   array( 'openid.server' => array( 0 => 'http://www.example-provider.com' ),
  722.      *          'openid.delegate' => array( 0 => 'http://www.example-delegate.com' )
  723.      *        );
  724.      * </code>
  725.      *
  726.      * @throws ezcAuthenticationOpenidConnectionException
  727.      *          if connection to the URL could not be opened
  728.      * @param string $url URL to connect to and discover the OpenID information
  729.      * @return array(string=>array) 
  730.      */
  731.     protected function discoverXrds$url )
  732.     {
  733.         $url ezcAuthenticationUrl::normalize$url );
  734.  
  735.         $src ezcAuthenticationUrl::getUrl$url'HEAD''application/xrds+xml' );
  736.  
  737.         preg_match'@X-XRDS-Location:\s(.*)@'$src$matches );
  738.         if isset$matches[1) )
  739.         {
  740.             // get the XRDS document from the X-XRDS-Location URL
  741.             $url $matches[1];
  742.             $src ezcAuthenticationUrl::getUrl$url'GET''application/xrds+xml' );
  743.         }
  744.         else
  745.         {
  746.             // get the XRDS document from the original location (provided $url)
  747.             $src ezcAuthenticationUrl::getUrl$url'GET''application/xrds+xml' );
  748.         }
  749.         $result array();
  750.  
  751.         // @todo check the regexp in this function, maybe they should be rewritten
  752.  
  753.         // get the OpenID servers
  754.         $pattern "#<URI[^>]*>(.*?)</URI>#si";
  755.         preg_match_all$pattern$src$matches );
  756.         $count count$matches[0);
  757.         for $i 0$i $count$i++ )
  758.         {
  759.             // force OpenID 2.0
  760.             $result['openid2.provider'][$matches[1][$i];
  761.         }
  762.         return $result;
  763.     }
  764.  
  765.     /**
  766.      * Discovers the OpenID server information from the provided URL using Yadis.
  767.      *
  768.      * The format of the returned array is (example):
  769.      * <code>
  770.      *   array( 'openid.server' => array( 0 => 'http://www.example-provider.com' ),
  771.      *          'openid.delegate' => array( 0 => 'http://www.example-delegate.com' )
  772.      *        );
  773.      * </code>
  774.      *
  775.      * @throws ezcAuthenticationOpenidConnectionException
  776.      *          if connection to the URL could not be opened
  777.      * @param string $url URL to connect to and discover the OpenID information
  778.      * @return array(string=>array) 
  779.      */
  780.     protected function discoverYadis$url )
  781.     {
  782.         $url ezcAuthenticationUrl::normalize$url );
  783.  
  784.         $src ezcAuthenticationUrl::getUrl$url'GET''application/xrds+xml' );
  785.         $result array();
  786.  
  787.         // @todo check the regexp in this function, maybe they should be rewritten
  788.  
  789.         // get the OpenID servers
  790.         $pattern "#<URI[^>]*>(.*?)</URI>#si";
  791.         preg_match_all$pattern$src$matches );
  792.         $count count$matches[0);
  793.         for $i 0$i $count$i++ )
  794.         {
  795.             $result['openid.server'][$matches[1][$i];
  796.         }
  797.  
  798.         // get the OpenID delegates
  799.         // @todo Add support for OpenID 2.0 <openid:LocalID> tags
  800.         $pattern "#<openid:Delegate[^>]*>(.*?)</openid:Delegate>#si";
  801.         preg_match_all$pattern$src$matches );
  802.         $count count$matches[0);
  803.         for $i 0$i $count$i++ )
  804.         {
  805.             $result['openid.delegate'][$matches[1][$i];
  806.         }
  807.         return $result;
  808.     }
  809.  
  810.     /**
  811.      * Discovers the OpenID server information from the provided URL by parsing
  812.      * the HTML page source.
  813.      *
  814.      * The format of the returned array is (example):
  815.      * <code>
  816.      *   array( 'openid.server' => array( 0 => 'http://www.example-provider.com' ),
  817.      *          'openid.delegate' => array( 0 => 'http://www.example-delegate.com' )
  818.      *        );
  819.      * </code>
  820.      *
  821.      * @throws ezcAuthenticationOpenidConnectionException
  822.      *          if connection to the URL could not be opened
  823.      * @param string $url URL to connect to and discover the OpenID information
  824.      * @return array(string=>array) 
  825.      */
  826.     protected function discoverHtml$url )
  827.     {
  828.         $url ezcAuthenticationUrl::normalize$url );
  829.  
  830.         $src ezcAuthenticationUrl::getUrl$url'GET''text/html' );
  831.         $result array();
  832.  
  833.         $pattern "%<\w.*rel\=[\s\"'`]*([\w:?=@&\/#._;-]+)[\s\"'`]*[^>]*>%i";
  834.         preg_match_all$pattern$src$matches );
  835.         $count count$matches[0);
  836.  
  837.         for $i 0$i $count$i++ )
  838.         {
  839.             if stristr$matches[1][$i]'openid' !== false )
  840.             {
  841.                 $pattern "%.*href\=[\s\"'`]*([\w:?=@&\/#._;-]+)[\s\"'`]*%i";
  842.                 preg_match$pattern$matches[0][$i]$href );
  843.                 $result[strtolower$matches[1][$i)][$href[1];
  844.             }
  845.         }
  846.         return $result;
  847.     }
  848.  
  849.     /**
  850.      * Performs a redirection to $provider with the specified parameters $params
  851.      * (checkid_setup OpenID request).
  852.      *
  853.      * The format of the checkid_setup $params array is:
  854.      * <code>
  855.      * array(
  856.      *        'openid.return_to' => urlencode( URL ),
  857.      *        'openid.trust_root' => urlencode( URL ),
  858.      *        'openid.identity' => urlencode( URL ),
  859.      *        'openid.mode' => 'checkid_setup'
  860.      *      );
  861.      * </code>
  862.      *
  863.      * @throws ezcAuthenticationOpenidRedirectException
  864.      *          if redirection could not be performed
  865.      * @param string $provider The OpenID provider (discovered in HTML or Yadis)
  866.      * @param array(string=>string) $params OpenID parameters for checkid_setup
  867.      */
  868.     protected function redirectToOpenidProvider$providerarray $params )
  869.     {
  870.         $redirect $provider "?" urldecodehttp_build_query$params ) );
  871.  
  872.         if PHP_SAPI !== 'cli' )
  873.         {
  874.             if headers_sent() )
  875.             {
  876.                 echo "<script language='JavaScript'>window.location='{$redirect}';</script>";
  877.             }
  878.             else
  879.             {
  880.                 header'Location: ' $redirect );
  881.             }
  882.         }
  883.  
  884.         // Normally the user should not see the following error because he was redirected
  885.         throw new ezcAuthenticationOpenidRedirectException$redirect );
  886.     }
  887.  
  888.     /**
  889.      * Returns the first provider in the $providers array if exists, otherwise
  890.      * returns false.
  891.      *
  892.      * @param array(string=>array) $providers OpenID providers discovered during OpenID discovery
  893.      * @return bool|string
  894.      */
  895.     protected function getProviderarray $providers )
  896.     {
  897.         if isset$providers['openid.server'][0) )
  898.         {
  899.             return $providers['openid.server'][0];
  900.         }
  901.  
  902.         return false;
  903.     }
  904.  
  905.     /**
  906.      * Connects to $provider (checkid_immediate OpenID request) and returns an
  907.      * URL (setup URL) which can be used by the application in a pop-up window.
  908.      *
  909.      * The format of the check_authentication $params array is:
  910.      * <code>
  911.      * array(
  912.      *        'openid.return_to' => urlencode( URL ),
  913.      *        'openid.trust_root' => urlencode( URL ),
  914.      *        'openid.identity' => urlencode( URL ),
  915.      *        'openid.mode' => 'checkid_immediate'
  916.      *      );
  917.      * </code>
  918.      *
  919.      * @throws ezcAuthenticationOpenidException
  920.      *          if connection to the OpenID provider could not be opened
  921.      * @param string $provider The OpenID provider (discovered in HTML or Yadis)
  922.      * @param array(string=>string) $params OpenID parameters for checkid_immediate mode
  923.      * @param string $method The method to connect to the provider (default GET)
  924.      * @return bool 
  925.      */
  926.     protected function checkImmediate$providerarray $params$method 'GET' )
  927.     {
  928.         $parts parse_url$provider );
  929.         $path = isset$parts['path'$parts['path''/';
  930.         $host = isset$parts['host'$parts['host'null;
  931.         $port 80;
  932.  
  933.         // suppress warnings caused by fsockopen() if $host is not a valid domain
  934.         $connection @fsockopen$host$port$errno$errstr$this->options->timeoutOpen );
  935.         if !$connection )
  936.         {
  937.             throw new ezcAuthenticationOpenidException"Could not connect to host {$host}:{$port}: {$errstr}.);
  938.         }
  939.         else
  940.         {
  941.             stream_set_timeout$connection$this->options->timeout );
  942.             $url $path '?' urldecodehttp_build_query$params ) );
  943.             $headers array"{$method} {$url} HTTP/1.0""Host: {$host}""Connection: close" );
  944.             fputs$connectionimplode"\r\n"$headers "\r\n\r\n" );
  945.  
  946.             $src stream_get_contents$connection );
  947.             fclose$connection );
  948.  
  949.             $pattern "/Location:\s(.*)/";
  950.             if preg_match$pattern$src$matches )
  951.             {
  952.                 $returnUrl trim$matches[1);
  953.  
  954.                 // get the query parameters from the response URL
  955.                 $query parse_url$returnUrlPHP_URL_QUERY );
  956.                 $vars ezcAuthenticationUrl::parseQueryString$query );
  957.  
  958.                 // get the openid.user_setup_url value from the response URL
  959.                 $setupUrl = isset$vars['openid.user_setup_url'$vars['openid.user_setup_url'false;
  960.  
  961.                 if $setupUrl !== false )
  962.                 {
  963.                     // the next call to OpenID will be check_authentication
  964.                     $vars['openid.mode''check_authentication';
  965.  
  966.                     // get the query parameters from the openid.user_setup_url in $setupParams
  967.                     // and the other parts of the URL in $parts
  968.                     $parts parse_url$setupUrl );
  969.                     $query = isset$parts['query'$parts['query'false;
  970.                     $setupParams ezcAuthenticationUrl::parseQueryString$query );
  971.  
  972.                     // merge the setup_url query parameters with all the other query parameters
  973.                     $params array_merge$vars$setupParams );
  974.  
  975.                     // return the setup URL combined with the rest of the query parameters
  976.                     $parts['query'$params;
  977.                     $setupUrl ezcAuthenticationUrl::buildUrl$parts );
  978.                 }
  979.  
  980.                 return $setupUrl;
  981.             }
  982.         }
  983.  
  984.         // the response from the OpenID server did not contain setup_url
  985.         return false;
  986.     }
  987.  
  988.     /**
  989.      * Opens a connection with the OpenID provider and checks if $params are
  990.      * correct (check_authentication OpenID request).
  991.      *
  992.      * The format of the check_authentication $params array is:
  993.      * <code>
  994.      * array(
  995.      *        'openid.assoc_handle' => urlencode( HANDLE ),
  996.      *        'openid.signed' => urlencode( SIGNED ),
  997.      *        'openid.sig' => urlencode( SIG ),
  998.      *        'openid.mode' => 'check_authentication'
  999.      *      );
  1000.      * </code>
  1001.      * where HANDLE, SIGNED and SIG are parameters returned from the provider in
  1002.      * the id_res step of OpenID authentication. In addition, the $params array
  1003.      * must contain the values present in SIG.
  1004.      *
  1005.      * @throws ezcAuthenticationOpenidException
  1006.      *          if connection to the OpenID provider could not be opened
  1007.      * @param string $provider The OpenID provider (discovered in HTML or Yadis)
  1008.      * @param array(string=>string) $params OpenID parameters for check_authentication mode
  1009.      * @param string $method The method to connect to the provider (default GET)
  1010.      * @return bool 
  1011.      */
  1012.     protected function checkSignature$providerarray $params$method 'GET' )
  1013.     {
  1014.         $parts parse_url$provider );
  1015.         $path = isset$parts['path'$parts['path''/';
  1016.         $host = isset$parts['host'$parts['host'null;
  1017.         $port 443;
  1018.  
  1019.         // suppress warnings caused by fsockopen() if $host is not a valid domain
  1020.         $connection @fsockopen'ssl://' $host$port$errno$errstr$this->options->timeoutOpen );
  1021.         if !$connection )
  1022.         {
  1023.             throw new ezcAuthenticationOpenidException"Could not connect to host {$host}:{$port}: {$errstr}.);
  1024.         }
  1025.         else
  1026.         {
  1027.             stream_set_timeout$connection$this->options->timeout );
  1028.             $url $path '?' urldecodehttp_build_query$params ) );
  1029.             $headers array"{$method} {$url} HTTP/1.0""Host: {$host}""Connection: close" );
  1030.             fputs$connectionimplode"\r\n"$headers "\r\n\r\n" );
  1031.  
  1032.             $src stream_get_contents$connection );
  1033.             fclose$connection );
  1034.  
  1035.             $r array();
  1036.             $response explode"\n"$src );
  1037.             foreach $response as $line )
  1038.             {
  1039.                 $line trim$line );
  1040.                 if !empty$line && strpos$line':' !== false )
  1041.                 {
  1042.                     list$key$value explode':'$line);
  1043.                     $r[trim$key )trim$value );
  1044.                 }
  1045.             }
  1046.  
  1047.             if isset$r['is_valid') )
  1048.             {
  1049.                 return trim$r['is_valid'=== 'true' true false;
  1050.             }
  1051.             return false;
  1052.         }
  1053.     }
  1054.  
  1055.     /**
  1056.      * Checks if $params are correct by signing with $association->secret.
  1057.      *
  1058.      * The format of the $params array is:
  1059.      * <code>
  1060.      * array(
  1061.      *        'openid.assoc_handle' => HANDLE,
  1062.      *        'openid.signed' => SIGNED,
  1063.      *        'openid.sig' => SIG,
  1064.      *        'openid.mode' => 'id_res'
  1065.      *      );
  1066.      * </code>
  1067.      * where HANDLE, SIGNED and SIG are parameters returned from the provider in
  1068.      * the id_res step of OpenID authentication. In addition, the $params array
  1069.      * must contain the values present in SIG.
  1070.      *
  1071.      * @param ezcAuthenticationOpenidAssociation $association The OpenID association used for signing $params
  1072.      * @param array(string=>string) $params OpenID parameters for id_res mode
  1073.      * @return bool 
  1074.      */
  1075.     protected function checkSignatureSmartezcAuthenticationOpenidAssociation $associationarray $params )
  1076.     {
  1077.         $sig $params['openid.sig'];
  1078.         $signed explode','$params['openid.signed');
  1079.  
  1080.         ksort$signed );
  1081.  
  1082.         for $i 0$i count$signed )$i++ )
  1083.         {
  1084.             $data[$signed[$i]] = isset$params['openid.' $signed[$i]] $params['openid.' $signed[$i]] null;
  1085.         }
  1086.  
  1087.         $serialized '';
  1088.         foreach $data as $key => $value )
  1089.         {
  1090.             $serialized .= "{$key}:{$value}\n";
  1091.         }
  1092.         
  1093.         $key base64_decode$association->secret );
  1094.         if strlen$key 64 )
  1095.         {
  1096.             $key ezcAuthenticationMath::sha1$key );
  1097.         }
  1098.  
  1099.         $key str_pad$key64chr0x00 ) );
  1100.         $hashed ezcAuthenticationMath::sha1( ( $key str_repeatchr0x36 )64 ) ) $serialized );
  1101.         $hashed ezcAuthenticationMath::sha1( ( $key str_repeatchr0x5c )64 ) ) $hashed );
  1102.         $hashed base64_encode$hashed );
  1103.  
  1104.         return $sig === $hashed );
  1105.     }
  1106.  
  1107.     /**
  1108.      * Attempts to establish a shared secret with the OpenID provider and
  1109.      * returns it (for "smart" mode).
  1110.      *
  1111.      * If the shared secret is still in its validity period, then it will be
  1112.      * returned instead of establishing a new one.
  1113.      *
  1114.      * If the shared secret could not be established the null will be returned,
  1115.      * and the OpenID connection will be in "dumb" mode.
  1116.      *
  1117.      * The format of the returned array is:
  1118.      *   array( 'assoc_handle' => assoc_handle_value,
  1119.      *          'mac_key' => mac_key_value
  1120.      *        )
  1121.      *
  1122.      * @param string $provider The OpenID provider (discovered in HTML or Yadis)
  1123.      * @param array(string=>string) $params OpenID parameters for associate mode
  1124.      * @param string $method The method to connect to the provider (default GET)
  1125.      * @return array(string=>mixed)||null
  1126.      */
  1127.     protected function associate$providerarray $params$method 'GET' )
  1128.     {
  1129.         $parts parse_url$provider );
  1130.         $path = isset$parts['path'$parts['path''/';
  1131.         $host = isset$parts['host'$parts['host'null;
  1132.         $port 443;
  1133.  
  1134.         // suppress warnings caused by fsockopen() if $host is not a valid domain
  1135.         $connection @fsockopen'ssl://' $host$port$errno$errstr$this->options->timeoutOpen );
  1136.         if !$connection )
  1137.         {
  1138.             throw new ezcAuthenticationOpenidException"Could not connect to host {$host}:{$port}: {$errstr}.);
  1139.         }
  1140.         else
  1141.         {
  1142.             stream_set_timeout$connection$this->options->timeout );
  1143.             $url $path '?' urldecodehttp_build_query$params ) );
  1144.  
  1145.             $headers array"{$method} {$url} HTTP/1.0""Host: {$host}""Connection: close" );
  1146.             fputs$connectionimplode"\r\n"$headers "\r\n\r\n" );
  1147.  
  1148.             $src stream_get_contents$connection );
  1149.             fclose$connection );
  1150.  
  1151.             $r array();
  1152.             $response explode"\n"$src );
  1153.             foreach $response as $line )
  1154.             {
  1155.                 $line trim$line );
  1156.                 if !empty$line && strpos$line':' !== false )
  1157.                 {
  1158.                     list$key$value explode':'$line);
  1159.                     $r[trim$key )trim$value );
  1160.                 }
  1161.             }
  1162.  
  1163.             if isset$r['assoc_handle') )
  1164.             {
  1165.                 $result array(
  1166.                     'assoc_handle' => $r['assoc_handle'],
  1167.                     'assoc_type' => $r['assoc_type'],
  1168.                     'expires_in' => $r['expires_in']
  1169.                     );
  1170.  
  1171.                 if isset$r['mac_key') )
  1172.                 {
  1173.                     $result['mac_key'$r['mac_key'];
  1174.                 }
  1175.  
  1176.                 if isset$r['enc_mac_key') )
  1177.                 {
  1178.                     $result['enc_mac_key'$r['enc_mac_key'];
  1179.                 }
  1180.  
  1181.                 return $result;
  1182.             }
  1183.         }
  1184.         return false;
  1185.     }
  1186.  
  1187.     /**
  1188.      * Generates a new nonce value with the specified length (default 6).
  1189.      *
  1190.      * @param int $length The length of the generated nonce, default 6
  1191.      * @return string 
  1192.      */
  1193.     protected function generateNonce$length )
  1194.     {
  1195.         $result '';
  1196.  
  1197.         for $i 0$i $length++$i )
  1198.         {
  1199.             $result .= rand0);
  1200.         }
  1201.  
  1202.         return $result;
  1203.     }
  1204.  
  1205.     /**
  1206.      * Registers which extra data to fetch during the authentication process.
  1207.      *
  1208.      * The extra data which is possible to be fetched during the authentication
  1209.      * process is:
  1210.      *  - nickname - the user's nickname (short name, alias)
  1211.      *  - email - the user's email address
  1212.      *  - fullname - the user's full name
  1213.      *  - dob - the user's date of birth as YYYY-MM-DD. Any component value whose
  1214.      *    representation uses fewer than the specified number of digits should
  1215.      *    be zero-padded (eg. 02 for February). If the user does not want to
  1216.      *    reveal any particular component of this value, it should be zero
  1217.      *    (eg. "1980-00-00" if the user is born in 1980 but does not want to
  1218.      *    specify his month and day of birth)
  1219.      *  - gender - the user's gender, "M" for male, "F" for female
  1220.      *  - postcode - the user's postal code
  1221.      *  - country - the user's country as an ISO3166 string, (eg. "US")
  1222.      *  - language - the user's preferred language as an ISO639 string (eg. "FR")
  1223.      *  - timezone - the user's timezone, for example "Europe/Paris"
  1224.      *
  1225.      * The input $data should be an array of attributes to request, for example:
  1226.      * <code>
  1227.      * array( 'fullname', 'gender', 'country', 'language' );
  1228.      * </code>
  1229.      *
  1230.      * @param array(string) $data A list of attributes to fetch during authentication
  1231.      */
  1232.     public function registerFetchDataarray $data array() )
  1233.     {
  1234.         $this->requestedData $data;
  1235.     }
  1236.  
  1237.     /**
  1238.      * Returns the extra data fetched during the authentication process.
  1239.      *
  1240.      * The return is something like:
  1241.      * <code>
  1242.      * array( 'fullname' => array( 'John Doe' ),
  1243.      *        'gender' => array( 'M' ),
  1244.      *        'country' => array( 'US' ),
  1245.      *        'language' => array( 'FR' )
  1246.      *      );
  1247.      * </code>
  1248.      *
  1249.      * @return array(string=>mixed) 
  1250.      */
  1251.     public function fetchData()
  1252.     {
  1253.         return $this->data;
  1254.     }
  1255.  
  1256.     /**
  1257.      * Returns the setup URL.
  1258.      *
  1259.      * @return string 
  1260.      */
  1261.     public function getSetupUrl()
  1262.     {
  1263.         return $this->setupUrl;
  1264.     }
  1265. }
  1266. ?>
Documentation generated by phpDocumentor 1.4.3