Home | History | Annotate | Download | only in hello-php
      1 <?php
      2 /**
      3  * Copyright (c) 2012 The Chromium Authors. All rights reserved.
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  *
      7  * A "Hello world!" for the Chrome Web Store Licensing API, in PHP. This
      8  * program logs the user in with Google's Federated Login API (OpenID), fetches
      9  * their license state with OAuth, and prints one of these greetings as
     10  * appropriate:
     11  *
     12  *   1. This user has FREE_TRIAL access to this application ( appId: 1 )
     13  *   2. This user has FULL access to this application ( appId: 1 )
     14  *   3. This user has NO access to this application ( appId: 1 )
     15  *
     16  * This code makes use of a popup ui extension to the OpenID protocol. Instead
     17  * of the user being redirected to the Google login page, a popup window opens
     18  * to the login page, keeping the user on the main application page. See
     19  * popuplib.js
     20  *
     21  * Eric Bidelman <ericbidelman (at) chromium.org>
     22  */
     23 
     24 session_start();
     25 
     26 require_once 'lib/oauth/OAuth.php';
     27 require_once 'lib/lightopenid/openid.php';
     28 
     29 // Full URL of the current application is running under.
     30 $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != 'on') ? 'http' :
     31                                                                      'https';
     32 $selfUrl = "$scheme://{$_SERVER['HTTP_HOST']}{$_SERVER['PHP_SELF']}";
     33 
     34 
     35 /**
     36  * Wrapper class to make calls to the Chrome Web Store License Server.
     37  */
     38 class LicenseServerClient {
     39 
     40   const LICENSE_SERVER_HOST = 'https://www.googleapis.com';
     41   const CONSUMER_KEY = 'anonymous';
     42   const CONSUMER_SECRET = 'anonymous';
     43   const APP_ID = '1';  // Change to the correct id of your application.
     44   const TOKEN = '[REPLACE THIS WITH YOUR OAUTH TOKEN]';
     45   const TOKEN_SECRET = '[REPLACE THIS WITH YOUR OAUTH TOKEN SECRET]';
     46   public $consumer;
     47   public $token;
     48   public $signatureMethod;
     49 
     50   public function __construct() {
     51     $this->consumer = new OAuthConsumer(
     52         self::CONSUMER_KEY, self::CONSUMER_SECRET, NULL);
     53     $this->token = new OAuthToken(self::TOKEN, self::TOKEN_SECRET);
     54     $this->signatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
     55   }
     56 
     57   /**
     58    * Makes an HTTP GET request to the specified URL.
     59    *
     60    * @param string $url Full URL of the resource to access
     61    * @param string $request OAuthRequest containing the signed request to make.
     62    * @param array $extraHeaders (optional) Array of headers.
     63    * @param bool $returnResponseHeaders True if resp headers should be returned.
     64    * @return string Response body from the server.
     65    */
     66   protected function send_signed_get($request, $extraHeaders=NULL,
     67                                      $returnRequestHeaders=false,
     68                                      $returnResponseHeaders=false) {
     69     $url = explode('?', $request->to_url());
     70     $curl = curl_init($url[0]);
     71     curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
     72     curl_setopt($curl, CURLOPT_FAILONERROR, false);
     73     curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
     74 
     75     // Return request headers in the response.
     76     curl_setopt($curl, CURLINFO_HEADER_OUT, $returnRequestHeaders);
     77 
     78     // Return response headers in the response?
     79     if ($returnResponseHeaders) {
     80       curl_setopt($curl, CURLOPT_HEADER, true);
     81     }
     82 
     83     $headers = array($request->to_header());
     84     if (is_array($extraHeaders)) {
     85       $headers = array_merge($headers, $extraHeaders);
     86     }
     87     curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
     88 
     89     // Execute the request.  If an error occurs fill the response body with it.
     90     $response = curl_exec($curl);
     91     if (!$response) {
     92       $response = curl_error($curl);
     93     }
     94 
     95     // Add server's response headers to our response body
     96     $response = curl_getinfo($curl, CURLINFO_HEADER_OUT) . $response;
     97 
     98     curl_close($curl);
     99 
    100     return $response;
    101   }
    102 
    103   public function checkLicense($userId) {
    104     $url = self::LICENSE_SERVER_HOST . '/chromewebstore/v1/licenses/' .
    105            self::APP_ID . '/' . urlencode($userId);
    106 
    107     $request = OAuthRequest::from_consumer_and_token(
    108         $this->consumer, $this->token, 'GET', $url, array());
    109 
    110     $request->sign_request($this->signatureMethod, $this->consumer,
    111                            $this->token);
    112 
    113     return $this->send_signed_get($request);
    114   }
    115 }
    116 
    117 try {
    118   $openid = new LightOpenID();
    119   $userId = $openid->identity;
    120   if (!isset($_GET['openid_mode'])) {
    121     // This section performs the OpenID dance with the normal redirect. Use it
    122     // if you want an alternative to the popup UI.
    123     if (isset($_GET['login'])) {
    124       $openid->identity = 'https://www.google.com/accounts/o8/id';
    125       $openid->required = array('namePerson/first', 'namePerson/last',
    126                                 'contact/email');
    127       header('Location: ' . $openid->authUrl());
    128     }
    129   } else if ($_GET['openid_mode'] == 'cancel') {
    130     echo 'User has canceled authentication!';
    131   } else {
    132     $userId = $openid->validate() ? $openid->identity : '';
    133     $_SESSION['userId'] = $userId;
    134     $attributes = $openid->getAttributes();
    135     $_SESSION['attributes'] = $attributes;
    136   }
    137 } catch(ErrorException $e) {
    138   echo $e->getMessage();
    139   exit;
    140 }
    141 
    142 if (isset($_REQUEST['popup']) && !isset($_SESSION['redirect_to'])) {
    143   $_SESSION['redirect_to'] = $selfUrl;
    144   echo '<script type = "text/javascript">window.close();</script>';
    145   exit;
    146 } else if (isset($_SESSION['redirect_to'])) {
    147   $redirect = $_SESSION['redirect_to'];
    148   unset($_SESSION['redirect_to']);
    149   header('Location: ' . $redirect);
    150 } else if (isset($_REQUEST['queryLicenseServer'])) {
    151   $ls = new LicenseServerClient();
    152   echo $ls->checkLicense($_REQUEST['user_id']);
    153   exit;
    154 } else if (isset($_GET['logout'])) {
    155   unset($_SESSION['attributes']);
    156   unset($_SESSION['userId']);
    157   header('Location: ' . $selfUrl);
    158 }
    159 ?>
    160 
    161 <!DOCTYPE html>
    162 <html>
    163   <head>
    164   <meta charset="utf-8" />
    165   <link href="main.css" type="text/css" rel="stylesheet" />
    166   <script type="text/javascript" src="popuplib.js"></script>
    167   <script type="text/html" id="ls_tmpl">
    168     <div id="access-level">
    169       <% if (result.toLowerCase() == 'yes') { %>
    170         This user has <span class="<%= accessLevel.toLowerCase() %>"><%= accessLevel %></span> access to this application ( appId: <%= appId %> )
    171       <% } else { %>
    172         This user has <span class="<%= result.toLowerCase() %>"><%= result %></span> access to this application ( appId: <%= appId %> )
    173       <% } %>
    174     </div>
    175   </script>
    176   </head>
    177   <body>
    178     <nav>
    179       <?php if (!isset($_SESSION['userId'])): ?>
    180         <a href="javascript:" onclick="openPopup(450, 500, this);">Sign in</a>
    181       <?php else: ?>
    182         <span>Welcome <?php echo @$_SESSION['attributes']['namePerson/first'] ?> <?php echo @$_SESSION['attributes']['namePerson/last'] ?> ( <?php echo $_SESSION['attributes']['contact/email'] ?> )</span>
    183         <a href="?logout">Sign out</a>
    184       <?php endif; ?>
    185     </nav>
    186     <?php if (isset($_SESSION['attributes'])): ?>
    187       <div id="container">
    188         <form action="<?php echo "$selfUrl?queryLicenseServer" ?>" onsubmit="return queryLicenseServer(this);">
    189           <input type="hidden" id="user_id" name="user_id" value="<?php echo $_SESSION['userId'] ?>" />
    190           <input type="submit" value="Check user's access" />
    191         </form>
    192         <div id="license-server-response"></div>
    193       </div>
    194     <?php endif; ?>
    195     <script>
    196       // Simple JavaScript Templating
    197       // John Resig - http://ejohn.org/ - MIT Licensed
    198       (function(){
    199         var cache = {};
    200 
    201         this.tmpl = function tmpl(str, data){
    202           // Figure out if we're getting a template, or if we need to
    203           // load the template - and be sure to cache the result.
    204           var fn = !/\W/.test(str) ?
    205             cache[str] = cache[str] ||
    206               tmpl(document.getElementById(str).innerHTML) :
    207 
    208             // Generate a reusable function that will serve as a template
    209             // generator (and which will be cached).
    210             new Function("obj",
    211               "var p=[],print=function(){p.push.apply(p,arguments);};" +
    212 
    213               // Introduce the data as local variables using with(){}
    214               "with(obj){p.push('" +
    215 
    216               // Convert the template into pure JavaScript
    217               str
    218                 .replace(/[\r\t\n]/g, " ")
    219                 .split("<%").join("\t")
    220                 .replace(/((^|%>)[^\t]*)'/g, "$1\r")
    221                 .replace(/\t=(.*?)%>/g, "',$1,'")
    222                 .split("\t").join("');")
    223                 .split("%>").join("p.push('")
    224                 .split("\r").join("\\'")
    225             + "');}return p.join('');");
    226 
    227           // Provide some basic currying to the user
    228           return data ? fn( data ) : fn;
    229         };
    230       })();
    231 
    232       function queryLicenseServer(form) {
    233         var userId = form.user_id.value;
    234 
    235         if (!userId) {
    236           alert('No OpenID specified!');
    237           return false;
    238         }
    239 
    240         var req = new XMLHttpRequest();
    241         req.onreadystatechange = function(e) {
    242           if (this.readyState == 4) {
    243             var resp = JSON.parse(this.responseText);
    244             var el = document.getElementById('license-server-response');
    245             if (resp.error) {
    246               el.innerHTML = ['<div class="error">Error ', resp.error.code,
    247                               ': ', resp.error.message, '</div>'].join('');
    248             } else {
    249               el.innerHTML = tmpl('ls_tmpl', resp);
    250             }
    251           }
    252         };
    253         var url =
    254             [form.action, '&user_id=', encodeURIComponent(userId)].join('');
    255         req.open('GET', url, true);
    256         req.send(null);
    257 
    258         return false;
    259       }
    260 
    261       function openPopup(w, h, link) {
    262         var extensions = {
    263           'openid.ns.ext1': 'http://openid.net/srv/ax/1.0',
    264           'openid.ext1.mode': 'fetch_request',
    265           'openid.ext1.type.email': 'http://axschema.org/contact/email',
    266           'openid.ext1.type.first': 'http://axschema.org/namePerson/first',
    267           'openid.ext1.type.last': 'http://axschema.org/namePerson/last',
    268           'openid.ext1.required': 'email,first,last',
    269           'openid.ui.icon': 'true'
    270         };
    271 
    272         var googleOpener = popupManager.createPopupOpener({
    273           opEndpoint: 'https://www.google.com/accounts/o8/ud',
    274           returnToUrl: '<?php echo "$selfUrl?popup=true" ?>',
    275           onCloseHandler: function() {
    276             window.location = '<?php echo $selfUrl ?>';
    277           },
    278           shouldEncodeUrls: false,
    279           extensions: extensions
    280         });
    281         link.parentNode.appendChild(
    282             document.createTextNode('Authenticating...'));
    283         link.parentNode.removeChild(link);
    284         googleOpener.popup(w, h);
    285       }
    286     </script>
    287   </body>
    288 </html>
    289