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

Source for file typekey_filter.php

Documentation is available at typekey_filter.php

  1. <?php
  2. /**
  3.  * File containing the ezcAuthenticationTypekeyFilter 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 TypeKey.
  30.  *
  31.  * The filter deals with the validation of information returned by the TypeKey
  32.  * server in response to a login command.
  33.  *
  34.  * Specifications: {@link http://www.sixapart.com/typekey/api}
  35.  *
  36.  * In order to access a protected page, user logs in by using a request like:
  37.  *  - https://www.typekey.com/t/typekey/login?
  38.  *        t=391jbj25WAQANzJrKvb5&
  39.  *        _return=http://example.com/login.php
  40.  *
  41.  * where:
  42.  *  - t = TypeKey token generated for each TypeKey account.
  43.  *        It is found at https://www.typekey.com/t/typekey/prefs.
  44.  *        This value is also used as a session key, so it must be passed to the
  45.  *        page performing the TypeKey authentication via the _return URL.
  46.  *  - _return = the URL where to return after user logs in with his TypeKey
  47.  *              username and password. The URL can contain query arguments, such
  48.  *              as the value t which can be used as a session key.
  49.  *
  50.  * The login link can also contain these 2 optional values:
  51.  *  - v = TypeKey version to use. Default is 1.
  52.  *  - need_email = the mail address which was used to register with TypeKey. It
  53.  *                 needs to be set to a value different than 0 in order to get
  54.  *                 the email address of the user when calling fetchData() after
  55.  *                 the authentication process has been completed.
  56.  *
  57.  * So the TypeKey authentication filter will run in the _return page and will
  58.  * verify the signature and the other information in the URL.
  59.  *
  60.  * The application link (eg. http://example.com) must be registered in the
  61.  * TypeKey preferences page (https://www.typekey.com/t/typekey/prefs) in one
  62.  * of the 5 lines from "Your Weblog Preferences", otherwise TypeKey will
  63.  * not accept the login request.
  64.  *
  65.  * The link returned by TypeKey after user logs in with his TypeKey username
  66.  * and password looks like this:
  67.  *  - http://example.com/typekey.php?
  68.  *       ts=1177319974&email=5098f1e87a608675ded4d933f31899cae6b4f968&
  69.  *       name=ezc&nick=ezctest&
  70.  *       sig=I9Dop72+oahY82bpL7ymBoxdQ+k=:Vj/t7oZVL2zMSzwHzdOWop5NG/g=
  71.  *
  72.  * where:
  73.  *  - ts = timestamp (in seconds) of the TypeKey server time at login.
  74.  *         The TypeKey filter compares this timestamp with the application
  75.  *         server's timestamp to make sure the login is in a reasonable
  76.  *         time window (specified by the validity option). Don't use a too small
  77.  *         value for validity, because servers are not always synchronized.
  78.  *  - email = sha1 hash of "mailto:$mail", where $mail is the mail address
  79.  *            used to register with TypeKey.
  80.  *  - nick = TypeKey nickname/display name.
  81.  *  - sig = signature which must be validated by the TypeKey filter.
  82.  *
  83.  * For more information on the login request and the TypeKey response link see
  84.  * {@link http://www.sixapart.com/typekey/api}.
  85.  *
  86.  * Example of use (authentication + input form):
  87.  * <code>
  88.  * <?php
  89.  * // no headers should be sent before calling $session->start()
  90.  * $session = new ezcAuthenticationSession();
  91.  * $session->start();
  92.  *
  93.  * // $token is used as a key in the session to store the authenticated state between requests
  94.  * $token = isset( $_GET['token'] ) ? $_GET['token'] : $session->load();
  95.  *
  96.  * $credentials = new ezcAuthenticationIdCredentials( $token );
  97.  * $authentication = new ezcAuthentication( $credentials );
  98.  * $authentication->session = $session;
  99.  *
  100.  * $filter = new ezcAuthenticationTypekeyFilter();
  101.  * $authentication->addFilter( $filter );
  102.  * // add other filters if needed
  103.  *
  104.  * if ( !$authentication->run() )
  105.  * {
  106.  *     // authentication did not succeed, so inform the user
  107.  *     $status = $authentication->getStatus();
  108.  *     $err = array(
  109.  *              'ezcAuthenticationTypekeyFilter' => array(
  110.  *                  ezcAuthenticationTypekeyFilter::STATUS_SIGNATURE_INCORRECT => 'Signature returned by TypeKey is incorrect',
  111.  *                  ezcAuthenticationTypekeyFilter::STATUS_SIGNATURE_EXPIRED => 'The signature returned by TypeKey expired'
  112.  *                  ),
  113.  *              'ezcAuthenticationSession' => array(
  114.  *                  ezcAuthenticationSession::STATUS_EMPTY => '',
  115.  *                  ezcAuthenticationSession::STATUS_EXPIRED => 'Session expired'
  116.  *                  )
  117.  *              );
  118.  *     foreach ( $status as $line )
  119.  *     {
  120.  *         list( $key, $value ) = each( $line );
  121.  *         echo $err[$key][$value] . "\n";
  122.  *     }
  123.  * ?>
  124.  * <!-- OnSubmit hack to append the value of t to the _return value, to pass
  125.  *      the TypeKey token after the TypeKey request -->
  126.  * <form method="GET" action="https://www.typekey.com/t/typekey/login" onsubmit="document.getElementById('_return').value += '?token=' + document.getElementById('t').value;">
  127.  * TypeKey token: <input type="text" name="t" id="t" />
  128.  * <input type="hidden" name="_return" id="_return" value="http://localhost/typekey.php" />
  129.  * <input type="submit" />
  130.  * </form>
  131.  * <?php
  132.  * }
  133.  * else
  134.  * {
  135.  *     // authentication succeeded, so allow the user to see his content
  136.  *     echo "<b>Logged-in</b>";
  137.  * }
  138.  * ?>
  139.  * </code>
  140.  *
  141.  * Another method, which doesn't use JavaScript, is using an intermediary page
  142.  * which saves the token in the session, then calls the TypeKey login page:
  143.  *
  144.  * - original file is modified as follows:
  145.  * <code>
  146.  * <form method="GET" action="save_typekey.php">
  147.  * TypeKey token: <input type="text" name="t" id="t" />
  148.  * <input type="hidden" name="_return" id="_return" value="http://localhost/typekey.php" />
  149.  * <input type="submit" />
  150.  * </form>
  151.  * </code>
  152.  *
  153.  * - intermediary page:
  154.  * <code>
  155.  * <?php
  156.  * // no headers should be sent before calling $session->start()
  157.  * $session = new ezcAuthenticationSession();
  158.  * $session->start();
  159.  *
  160.  * // $token is used as a key in the session to store the authenticated state between requests
  161.  * $token = isset( $_GET['t'] ) ? $_GET['t'] : $session->load();
  162.  * if ( $token !== null )
  163.  * {
  164.  *     $session->save( $token );
  165.  * }
  166.  * $url = isset( $_GET['_return'] ) ? $_GET['_return'] : null;
  167.  * $url .= "?token={$token}";
  168.  * header( "Location: https://www.typekey.com/t/typekey/login?t={$token}&_return={$url}" );
  169.  * ?>
  170.  * </code>
  171.  *
  172.  * Extra data can be fetched from the TypeKey server during the authentication
  173.  * process. Different from the other filters, for TypeKey there is no registration
  174.  * needed for fetching the extra data, because all the possible extra data is
  175.  * available in the response sent by the TypeKey server.
  176.  *
  177.  * To be able to get the email address of the user, need_email must be set
  178.  * to a value different than 0 in the initial request sent to the TypeKey
  179.  * server (along with the t and _return values). Example:
  180.  *  - https://www.typekey.com/t/typekey/login?t=<token>&_return=<url>&need_email=1
  181.  *
  182.  * Example of fetching the extra data after the initial request has been sent:
  183.  * <code>
  184.  * // after run()
  185.  * // $filter is an ezcAuthenticationTypekeyFilter object
  186.  * $data = $filter->fetchData();
  187.  * </code>
  188.  *
  189.  * The $data array contains name (TypeKey username), nick (TypeKey display name)
  190.  * and optionally email (if the user allowed the sharing of his email address
  191.  * in the TypeKey profile page; otherwise it is not set).
  192.  * <code>
  193.  * array( 'name' => array( 'john' ),
  194.  *        'nick' => array( 'John Doe' ),
  195.  *        'email' => array( 'john.doe@example.com' ) // or not set
  196.  *      );
  197.  * </code>
  198.  *
  199.  * @property ezcAuthenticationBignumLibrary $lib 
  200.  *            The wrapper for the PHP extension to use for big number operations.
  201.  *            This will be autodetected in the constructor, but you can specify
  202.  *            your own wrapper before calling run().
  203.  *
  204.  * @package Authentication
  205.  * @version //autogen//
  206.  * @mainclass
  207.  */
  208. {
  209.     /**
  210.      * The request does not contain the needed information (like $_GET['sig']).
  211.      */
  212.     const STATUS_SIGNATURE_MISSING = 1;
  213.  
  214.     /**
  215.      * Signature verification was incorect.
  216.      */
  217.     const STATUS_SIGNATURE_INCORRECT = 2;
  218.  
  219.     /**
  220.      * Login is outside of the timeframe.
  221.      */
  222.     const STATUS_SIGNATURE_EXPIRED = 3;
  223.  
  224.     /**
  225.      * Holds the extra data fetched during the authentication process.
  226.      *
  227.      * Contains name (TypeKey username), nick (TypeKey display name) and
  228.      * optionally email (if the user allowed the sharing of his email address
  229.      * in the TypeKey profile page; otherwise it is not set).
  230.      *
  231.      * Usually it has this structure:
  232.      * <code>
  233.      * array( 'name' => array( 'john' ),
  234.      *        'nick' => array( 'John Doe' ),
  235.      *        'email' => array( 'john.doe@example.com' ) // or not set
  236.      *      );
  237.      * </code>
  238.      *
  239.      * @var array(string=>mixed) 
  240.      */
  241.     protected $data = array();
  242.  
  243.     /**
  244.      * Holds the properties of this class.
  245.      *
  246.      * @var array(string=>mixed) 
  247.      */
  248.     private $properties array();
  249.  
  250.     /**
  251.      * Creates a new object of this class.
  252.      *
  253.      * @throws ezcBaseExtensionNotFoundException
  254.      *          if neither of the PHP gmp and bcmath extensions are installed
  255.      * @param ezcAuthenticationTypekeyOptions $options Options for this class
  256.      */
  257.     public function __constructezcAuthenticationTypekeyOptions $options null )
  258.     {
  259.         $this->options = $options === null new ezcAuthenticationTypekeyOptions($options;
  260.         $this->lib ezcAuthenticationMath::createBignumLibrary();
  261.     }
  262.  
  263.     /**
  264.      * Sets the property $name to $value.
  265.      *
  266.      * @throws ezcBasePropertyNotFoundException
  267.      *          if the property $name does not exist
  268.      * @throws ezcBaseValueException
  269.      *          if $value is not correct for the property $name
  270.      * @param string $name The name of the property to set
  271.      * @param mixed $value The new value of the property
  272.      * @ignore
  273.      */
  274.     public function __set$name$value )
  275.     {
  276.         switch $name )
  277.         {
  278.             case 'lib':
  279.                 if $value instanceof ezcAuthenticationBignumLibrary )
  280.                 {
  281.                     $this->properties[$name$value;
  282.                 }
  283.                 else
  284.                 {
  285.                     throw new ezcBaseValueException$name$value'ezcAuthenticationBignumLibrary' );
  286.                 }
  287.                 break;
  288.  
  289.             default:
  290.                 throw new ezcBasePropertyNotFoundException$name );
  291.         }
  292.     }
  293.  
  294.     /**
  295.      * Returns the value of the property $name.
  296.      *
  297.      * @throws ezcBasePropertyNotFoundException
  298.      *          if the property $name does not exist
  299.      * @param string $name The name of the property for which to return the value
  300.      * @return mixed 
  301.      * @ignore
  302.      */
  303.     public function __get$name )
  304.     {
  305.         switch $name )
  306.         {
  307.             case 'lib':
  308.                 return $this->properties[$name];
  309.  
  310.             default:
  311.                 throw new ezcBasePropertyNotFoundException$name );
  312.         }
  313.     }
  314.  
  315.     /**
  316.      * Returns true if the property $name is set, otherwise false.
  317.      *
  318.      * @param string $name The name of the property to test if it is set
  319.      * @return bool 
  320.      * @ignore
  321.      */
  322.     public function __isset$name )
  323.     {
  324.         switch $name )
  325.         {
  326.             case 'lib':
  327.                 return isset$this->properties[$name);
  328.  
  329.             default:
  330.                 return false;
  331.         }
  332.     }
  333.  
  334.     /**
  335.      * Runs the filter and returns a status code when finished.
  336.      *
  337.      * @throws ezcAuthenticationTypekeyException
  338.      *          if the keys from the TypeKey public keys file could not be fetched
  339.      * @param ezcAuthenticationIdCredentials $credentials Authentication credentials
  340.      * @return int 
  341.      */
  342.     public function run$credentials )
  343.     {
  344.         $source $this->options->requestSource;
  345.         if isset$source['name'&& isset$source['email'&& isset$source['nick'&& isset$source['ts'&& isset$source['sig') )
  346.         {
  347.             // parse the response URL sent by the TypeKey server
  348.             $id = isset$source['name'$source['name'null;
  349.             $mail = isset$source['email'$source['email'null;
  350.             $nick = isset$source['nick'$source['nick'null;
  351.             $timestamp = isset$source['ts'$source['ts'null;
  352.             $signature = isset$source['sig'$source['sig'null;
  353.  
  354.             // extra data which will be returned by fetchData()
  355.             $this->data['name'array$id );
  356.             $this->data['nick'array$nick );
  357.             if strpos$mail'@' !== false )
  358.             {
  359.                 $this->data['email'array$mail );
  360.             }
  361.         }
  362.         else
  363.         {
  364.             return self::STATUS_SIGNATURE_MISSING;
  365.         }
  366.         if $this->options->validity !== &&
  367.              time($timestamp >= $this->options->validity
  368.            )
  369.         {
  370.             return self::STATUS_SIGNATURE_EXPIRED;
  371.         }
  372.         $keys $this->fetchPublicKeys$this->options->keysFile );
  373.         $msg "{$mail}::{$id}::{$nick}::{$timestamp}";
  374.         $signature rawurldecodeurlencode$signature ) );
  375.         list$r$s explode':'$signature );
  376.         if $this->checkSignature$msg$r$s$keys ) )
  377.         {
  378.             return self::STATUS_OK;
  379.         }
  380.         return self::STATUS_SIGNATURE_INCORRECT;
  381.     }
  382.  
  383.     /**
  384.      * Checks the information returned by the TypeKey server.
  385.      *
  386.      * @param string $msg Plain text signature which needs to be verified
  387.      * @param string $r First part of the signature retrieved from TypeKey
  388.      * @param string $s Second part of the signature retrieved from TypeKey
  389.      * @param array(string=>string) $keys Public keys retrieved from TypeKey
  390.      * @return bool 
  391.      */
  392.     protected function checkSignature$msg$r$s$keys )
  393.     {
  394.         $lib $this->lib;
  395.  
  396.         $r base64_decode$r );
  397.         $s base64_decode$s );
  398.  
  399.         foreach $keys as $key => $value )
  400.         {
  401.             $keys[$key$lib->init(string) $value );
  402.         }
  403.  
  404.         $s1 $lib->init$lib->binToDec$r ) );
  405.         $s2 $lib->init$lib->binToDec$s ) );
  406.  
  407.         $w $lib->invert$s2$keys['q');
  408.  
  409.         $msg $lib->hexToDecsha1$msg ) );
  410.  
  411.         $u1 $lib->mod$lib->mul$msg$w )$keys['q');
  412.         $u2 $lib->mod$lib->mul$s1$w )$keys['q');
  413.  
  414.         $v $lib->mul$lib->powmod$keys['g']$u1$keys['p')$lib->powmod$keys['pub_key']$u2$keys['p') );
  415.         $v $lib->mod$lib->mod$v$keys['p')$keys['q');
  416.  
  417.         return $lib->cmp$v$s1 === );
  418.     }
  419.  
  420.     /**
  421.      * Fetches the public keys from the specified file or URL $file.
  422.      *
  423.      * The file must be composed of space-separated values for p, g, q, and
  424.      * pub_key, like this:
  425.      *   p=<value> g=<value> q=<value> pub_key=<value>
  426.      *
  427.      * The format of the returned array is:
  428.      * <code>
  429.      *   array( 'p' => p_val, 'g' => g_val, 'q' => q_val, 'pub_key' => pub_key_val )
  430.      * </code>
  431.      *
  432.      * @throws ezcAuthenticationTypekeyPublicKeysMissingException
  433.      *          if the keys from the TypeKey public keys file could not be fetched
  434.      * @throws ezcAuthenticationTypekeyPublicKeysInvalidException
  435.      *          if the keys fetched from the TypeKey public keys file are invalid
  436.      * @param string $file The public keys file or URL
  437.      * @return array(string=>string) 
  438.      */
  439.     protected function fetchPublicKeys$file )
  440.     {
  441.         // suppress warnings caused by file_get_contents() if $file could not be opened
  442.         $data @file_get_contents$file );
  443.         if empty$data ) )
  444.         {
  445.             throw new ezcAuthenticationTypekeyPublicKeysMissingException"Could not fetch public keys from '{$file}'.);
  446.         }
  447.         $lines explode' 'trim$data ) );
  448.         foreach $lines as $line )
  449.         {
  450.             $val explode'='$line );
  451.             if count$val )
  452.             {
  453.                 throw new ezcAuthenticationTypekeyPublicKeysInvalidException"The data retrieved from '{$file}' is invalid.);
  454.             }
  455.             $keys[$val[0]] $val[1];
  456.         }
  457.         return $keys;
  458.     }
  459.  
  460.     /**
  461.      * Registers the extra data which will be fetched by the filter during the
  462.      * authentication process.
  463.      *
  464.      * For TypeKey there is no registration needed, because all the possible
  465.      * extra data is available in the response sent by the TypeKey server. So
  466.      * a call to this function is not needed.
  467.      *
  468.      * To be able to get the email address of the user, need_email must be set
  469.      * to a value different than 0 in the initial request sent to the TypeKey
  470.      * server (along with the t and _return values).
  471.      *
  472.      * @param array(string) $data A list of attributes to fetch during authentication
  473.      */
  474.     public function registerFetchDataarray $data array() )
  475.     {
  476.         // does not need to do anything because all the extra data is returned by default
  477.     }
  478.  
  479.     /**
  480.      * Returns the extra data which was fetched during the authentication process.
  481.      *
  482.      * The TypeKey extra data is an array containing the values for name (the
  483.      * TypeKey username), nick (the TypeKey display name) and email (the email
  484.      * address of the user, fetched only if the initial request to the TypeKey
  485.      * server contains need_email, and the user allowed the sharing of his email
  486.      * address).
  487.      *
  488.      * Example of returned array:
  489.      * <code>
  490.      * array( 'name' => array( 'john' ),
  491.      *        'nick' => array( 'John Doe' ),
  492.      *        'email' => array( 'john.doe@example.com' ) // or not set
  493.      *      );
  494.      * </code>
  495.      *
  496.      * @return array(string=>mixed) 
  497.      */
  498.     public function fetchData()
  499.     {
  500.         return $this->data;
  501.     }
  502. }
  503. ?>
Documentation generated by phpDocumentor 1.4.3