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.
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
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 )
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
21 * Eric Bidelman <ericbidelman@chromium.org>
26 require_once 'lib/oauth/OAuth.php';
27 require_once 'lib/lightopenid/openid.php';
29 // Full URL of the current application is running under.
30 $scheme = (!isset($_SERVER['HTTPS']) ||
$_SERVER['HTTPS'] != 'on') ?
'http' :
32 $selfUrl = "$scheme://{$_SERVER['HTTP_HOST']}{$_SERVER['PHP_SELF']}";
36 * Wrapper class to make calls to the Chrome Web Store License Server.
38 class LicenseServerClient
{
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]';
48 public $signatureMethod;
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();
58 * Makes an HTTP GET request to the specified URL.
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.
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);
75 // Return request headers in the response.
76 curl_setopt($curl, CURLINFO_HEADER_OUT
, $returnRequestHeaders);
78 // Return response headers in the response?
79 if ($returnResponseHeaders) {
80 curl_setopt($curl, CURLOPT_HEADER
, true);
83 $headers = array($request->to_header());
84 if (is_array($extraHeaders)) {
85 $headers = array_merge($headers, $extraHeaders);
87 curl_setopt($curl, CURLOPT_HTTPHEADER
, $headers);
89 // Execute the request. If an error occurs fill the response body with it.
90 $response = curl_exec($curl);
92 $response = curl_error($curl);
95 // Add server's response headers to our response body
96 $response = curl_getinfo($curl, CURLINFO_HEADER_OUT
) . $response;
103 public function checkLicense($userId) {
104 $url = self
::LICENSE_SERVER_HOST
. '/chromewebstore/v1/licenses/' .
105 self
::APP_ID
. '/' . urlencode($userId);
107 $request = OAuthRequest
::from_consumer_and_token(
108 $this->consumer
, $this->token
, 'GET', $url, array());
110 $request->sign_request($this->signatureMethod
, $this->consumer
,
113 return $this->send_signed_get($request);
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',
127 header('Location: ' . $openid->authUrl());
129 } else if ($_GET['openid_mode'] == 'cancel') {
130 echo 'User has canceled authentication!';
132 $userId = $openid->validate() ?
$openid->identity
: '';
133 $_SESSION['userId'] = $userId;
134 $attributes = $openid->getAttributes();
135 $_SESSION['attributes'] = $attributes;
137 } catch(ErrorException
$e) {
138 echo $e->getMessage();
142 if (isset($_REQUEST['popup']) && !isset($_SESSION['redirect_to'])) {
143 $_SESSION['redirect_to'] = $selfUrl;
144 echo '<script type = "text/javascript">window.close();</script>';
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']);
154 } else if (isset($_GET['logout'])) {
155 unset($_SESSION['attributes']);
156 unset($_SESSION['userId']);
157 header('Location: ' . $selfUrl);
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 %
> )
172 This user has
<span
class="<%= result.toLowerCase() %>"><%
= result %
></span
> access to this
application ( appId
: <%
= appId %
> )
179 <?php
if (!isset($_SESSION['userId'])): ?
>
180 <a href
="javascript:" onclick
="openPopup(450, 500, this);">Sign in
</a
>
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
>
186 <?php
if (isset($_SESSION['attributes'])): ?
>
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" />
192 <div id
="license-server-response"></div
>
196 // Simple JavaScript Templating
197 // John Resig - http://ejohn.org/ - MIT Licensed
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
) :
208 // Generate a reusable function that will serve as a template
209 // generator (and which will be cached).
211 "var p=[],print=function(){p.push.apply(p,arguments);};" +
213 // Introduce the data as local variables using with(){}
214 "with(obj){p.push('" +
216 // Convert the template into pure JavaScript
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('');");
227 // Provide some basic currying to the user
228 return data ? fn( data ) : fn;
232 function queryLicenseServer(form) {
233 var userId = form.user_id.value;
236 alert('No OpenID specified
!');
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
');
246 el.innerHTML = ['<div
class="error">Error
', resp.error.code,
247 ': ', resp.error.message, '</div
>'].join('');
249 el.innerHTML = tmpl('ls_tmpl
', resp);
254 [form.action, '&user_id
=', encodeURIComponent(userId)].join('');
255 req.open('GET
', url, true);
261 function openPopup(w, h, link) {
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'
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 ?>';
278 shouldEncodeUrls
: false,
279 extensions
: extensions
281 link
.parentNode
.appendChild(
282 document
.createTextNode('Authenticating...'));
283 link
.parentNode
.removeChild(link
);
284 googleOpener
.popup(w
, h
);