Chapter 4. OAuth-Enabled APIs

JavaScript plugins, like the comment plugin from Chapter 3, allow you to move beyond the official social plugins, but the depth of integration that they provide is limited. As you move on to deeper integration, some processing must be done server-side. To accommodate this, further examples are written in PHP. You will need to an environment capable of running PHP and a SQLite database.

The Google+ platform is not limited to PHP. You can find client libraries and starter projects for many popular languages including Java, Python, .NET, and Ruby. If Google does not supply an official client library for your language of choice, you may still use the REST APIs directly.

New Application: Baking Disasters 2.0

Baking Disasters is fun to publish as a static HTML blog, but as time passes visitors have started to express a desire to contribute their baking experiences. Being a social baker with a streak of PHP ability, this seems like the perfect opportunity to transform Baking Disasters into a social web application where everyone can contribute.

After one night of frenzied PHP hacking, Baking Disasters 2.0, as pictured in Figure 4-1, was born. It consists of an administration page for managing recipes and a public page for each recipe where visitors can publish their hilarious baking disasters.

The screens of Baking Disasters 2.0. Upper left, index page for recipes; lower left, an administrative console to add new recipes; right, detail pages for each recipe that include user contributed attempts and a form to submit a new attempt.

Figure 4-1. The screens of Baking Disasters 2.0. Upper left, index page for recipes; lower left, an administrative console to add new recipes; right, detail pages for each recipe that include user contributed attempts and a form to submit a new attempt.

Consisting of a couple hundred lines of PHP and a SQLite database, Baking Disasters 2.0 may not scale to millions of users, but it is a great starting point for further exploration of the Google+ Platform. Its simple architecture is described by Figure 4-2.

The Baking Disasters web application baseline architecture

Figure 4-2. The Baking Disasters web application baseline architecture

You can see the initial state of this application in action at http://bakingdisasters.com/app-initial. It’s read only, for reasons that will become apparent shortly.

Authentication Using Google+

Within hours of launch the site has been overrun with spam. Since visitors can post content without identifying themselves, the application is being abused. You must find a way to lock down Baking Disasters and protect it.

Fortunately, the Google+ platform provides APIs for identifying users. The REST API exposes public profile fields. It also allows visitors to share their identity with us via OAuth 2.0. This provides everything that you need to address the spam problem, without having to build your own user management infrastructure.

OAuth 2.0

The Google+ platform uses OAuth 2.0 to authenticate users, and to authorize your access to their private data. Scopes control which data is accessible. When the user grants access, Google will provide your application with tokens that can be used to access your user’s private data hosted by Google. Baking Disasters can use this to identify users as shown in Figure 4-3.

A sequence diagram, which illustrates the OAuth 2.0 flow for Baking Disasters.

Figure 4-3. A sequence diagram, which illustrates the OAuth 2.0 flow for Baking Disasters.

Once a user’s identity is known, you can use the REST APIs to fetch their public data. This includes their profile photo and their public activity on Google+. Additionally, their user ID makes a great user identifier in your application.

Accounts and API Keys

Your application must be configured on the API Console to use OAuth 2.0. Return to the Google API console to generate a client ID and secret. Use the client ID and secret to initiate the OAuth dance. They guarantee to the user that they are authorizing the correct application.

The client ID is publicly exposed during several steps of the authentication dance, but you should keep your client secret secure. If at any time your client secret is compromised you can return to this page to reset it.

Follow these steps to create your OAuth 2.0 credentials:

  1. Navigate to the API console on Google Developers: https://developers.google.com/console.

  2. Select your application from the drop down and click API Access in the menu.

  3. Click on the large blue button shown in Figure 4-4 to create an OAuth 2.0 client ID.

  4. Next, specify branding information for your application as shown in Figure 4-5. The product name and logo that you specify here are presented to your user on the OAuth grant screen.

  5. Now for the tricky part. The OAuth dance requires you to specify the destination page, as shown in Figure 4-6. Google will redirect users there, once they have granted you privileges.

  6. You should now see your client ID and secret, as shown in Figure 4-7.

The big blue button that you must click to create an OAuth 2.0 client ID

Figure 4-4. The big blue button that you must click to create an OAuth 2.0 client ID

Creating an API client ID for Baking Disasters

Figure 4-5. Creating an API client ID for Baking Disasters

Configuring the redirect URI

Figure 4-6. Configuring the redirect URI

The resulting client ID and secret

Figure 4-7. The resulting client ID and secret

A Little More About OAuth 2.0

OAuth 2.0 is designed to solve the general problem of accessing user data that exists on a third-party system. This is a big problem to solve. Baking Disasters uses the basic flow for web applications that are running on a server. This flow is only a tiny sliver of the much larger OAuth 2.0 specification.

Not every application runs in a web browser. Applications that run as native code on a mobile device, or as a command line script, are also valid clients. To accommodate these applications OAuth 2.0 provides a variety of flows, each with their own nuances.

OAuth 2.0 is big enough that any discussion here can’t do it justice. You can learn more about OAuth at Google here: http://code.google.com/apis/accounts/docs/OAuth2.html.

Starter Projects

Client libraries make development much faster in the long run. However, just like any other tool, they have a learning curve. It will take you some time to make the most of their time-saving features. To smooth out this potentially sharp learning curve, the Google+ platform provides starter projects for the most popular client libraries. They are listed in Table 4-1.

These aptly named starter projects provide you with a turnkey foundation for whatever you would like to write. All you need to do is download the starter project and add your application identifiers. Starting from working code makes further development much easier.

Baking Disasters is written in PHP, so the PHP starter project is a great place to start. Once it’s working you can merge them together to start your Google+ integration.

  1. Download the starter project from the downloads tab on http://code.google.com/p/google-plus-php-starter/. Unzip the archive to reveal that the project contains three files, including a readme with usage instructions, as shown in Figure 4-8.

  2. The readme opens with a description of the starter project prerequisites. Most PHP installations will include the cURL and JSON extensions. The only thing that you need is the PHP client library.

  3. Download the PHP client library from http://code.google.com/p/google-api-php-client/ and extract it in the starter project’s folder. You should end up with a directory structure that looks like Figure 4-9.

  4. The readme instructs you to set up an application on the API Console to create a Client ID, Client Secret, and API key. Use the values that you set up for your API project in the previous section, as shown in Figure 4-10.

  5. Use your favorite text editor to edit index.php. Scroll to about line 30 and paste these identifiers in the appropriate places, as shown in Example 4-1.

  6. Next, update the redirectURI field to match the value in the API console and the place you’ll be hosting this file. If you plan to run the starter project from a your workstation, this may be localhost.

The Google+ API PHP starter project’s archive contents and readme file

Figure 4-8. The Google+ API PHP starter project’s archive contents and readme file

The PHP starter project with the PHP API client library

Figure 4-9. The PHP starter project with the PHP API client library

The client ID, client secret and API key on the API console

Figure 4-10. The client ID, client secret and API key on the API console

Example 4-1. The fully configured PHP Google+ API starter project

$client = new apiClient();
$client->setApplicationName("Google+ PHP Starter Application");
// Visit https://code.google.com/apis/console to generate your
// oauth2_client_id, oauth2_client_secret, and to register your oauth2_redirect_uri.
$client->setClientId('116363269786.apps.googleusercontent.com');
$client->setClientSecret('EJlqDrWkEYmmYznlken2JW-B');
$client->
  setRedirectUri('http://bakingdisasters.com/web-app/php-starter');
$client->setDeveloperKey('AIzaSyDrH_5j2-cPK7EZRANWjA6_g0xCZRrxH-U');
$client->setScopes(array('https://www.googleapis.com/auth/plus.me'));
$plus = new apiPlusService($client);

if (isset($_REQUEST['logout'])) {
  unset($_SESSION['access_token']);
}

It’s ready to go. Deploy the starter project to your web server, and view it in a web browser. Figure 4-11 shows what happens next. You’ll be greeted by a page that asks you to log in. Clicking the big blue link redirects you to Google’s authentication service where you’ll be asked to grant the starter project permissions to know who you are on Google. Click the Allow access button.

Top: The starter project landing page; Bottom: The permission grant page

Figure 4-11. Top: The starter project landing page; Bottom: The permission grant page

Having been granted access, the starter project now completes the OAuth dance. It exchanges a code for an access token. This token is used to make API calls, which are shown in Figure 4-12.

The completed flow of the starter project displaying your name, profile icon, and recent activity

Figure 4-12. The completed flow of the starter project displaying your name, profile icon, and recent activity

With only a few minutes invested you now have a self contained web application capable of making API calls to the Google+ REST APIs. If you run into issues as you progress you can always return to this code as a sanity check.

Bringing it Together

You now have two functioning applications: Baking Disasters, and the PHP starter project. Combine them to add sign-in functionality to Baking Disasters. This consists mostly of strategic copy-and-paste from the starter project into the appropriate places in Baking Disasters.

Create new PHP files for the sign-in and sign-out operations and store the currently signed in state within PHP’s session. Add code that’s shared across multiple files, such as code to create or manage API clients, to util.php.

All of the pages in Baking Disasters require the use of an API client. Take the code from the top of the starter project and copy it into util.php, as shown in Example 4-2. Create a new function that returns a Google+ API client.

Example 4-2. A PHP function that creates a configured API client

require_once 'google-api-php-client/src/apiClient.php'; 1
require_once 'google-api-php-client/src/contrib/apiPlusService.php'; 2

session_start(); 3

date_default_timezone_set('America/Los_Angeles');

function init_api_client() 4
{
  global $app_base_path;

  $client = new apiClient();
  $client->setApplicationName("Baking Disasters");
  $client->setClientId('116363269786.apps.googleusercontent.com');
  $client->setClientSecret('EJlqDrWkEYmmYznlken2JW-B');
  $client->setRedirectUri($app_base_path . '/login.php');
  $client->setDeveloperKey('AIzaSyDrH_5j2-cPK7EZRANWjA6_g0xCZRrx');
  $client->setScopes(array('https://www.googleapis.com/auth/plus.me'));
  return $client;
}
1

Load the core PHP API client library.

2

Load the generated Google+ API client library extension.

3

Start the session so that you have a place to store access tokens for your users.

4

Create a new client library instance configured for your API project and server.

Next, copy the rest of the authentication logic into two PHP files: login.php and logout.php. With a bit of shuffling login.php looks Example 4-3.

Example 4-3. Initiate an OAuth 2.0 flow from login.php

<?php
include_once("util.php");
$client = init_api_client();
$auth_url = $client->createAuthUrl();
if(!isset($_GET['code'])) { 1
  header("location: " . $auth_url);
}
else { //if (isset($_GET['code'])) { 2
  $client->authenticate();
  $_SESSION['access_token'] = $client->getAccessToken();
  header('Location: '.$app_base_path);
}
1

If the code GET parameter is not set, initiate a new OAuth 2.0 flow.

2

If code is set, use it to complete the OAuth flow.

And logout.php looks like Example 4-4.

Example 4-4. Sign users out by deleting their access and refresh tokens from the session

<?php
include_once("util.php");
unset($_SESSION['access_token']);
header('Location: '.$app_base_path);

With these files created add sign-in and sign-out links to the header of every page with the code in Example 4-5.

Example 4-5. Sign-in and sign-out links for the header of each page

<body>
<header class="blog-header">
  <span class="login">
    <?php if(isset($_SESSION['access_token'])) { ?>
      <a href="logout.php">Logout</a>
    <?php } else { ?>
      <a href="login.php">Sign in with Google+</a>
    <?php } ?>
  </span>
  <a href="index.php"><img id="blog-logo" src="images/logo.png"/></a>
  <h1>Baking Disasters</h1>

This implementation allows users to sign in and out, but it can already benefit from some refactoring. Currently, signing in and out redirects users back to the index page. Use the session and referrer header to return them to the page where they started. Example 4-6 is an updated login.php.

Example 4-6. Redirecting the user to the page from which they initiated the sign in improves their experience

// If there's a code we need to swap it for an access token
else { //if (isset($_GET['code'])) {
  $client->authenticate();
  $_SESSION['access_token'] = $client->getAccessToken();

  if(isset($_SESSION['original_referrer'])) {
    header('Location: ' . $_SESSION['original_referrer']);
    unset($_SESSION['original_referrer']);
  } else {
    header('Location: '.$app_base_path);
  }
}

Example 4-7 shows the updated logout.php.

Example 4-7. Also redirect the user to the page from which they initiated the sign out

<?php
include_once("util.php");
unset($_SESSION['access_token']);
if(isset($_SERVER['HTTP_REFERER'])) {
  header('Location: '.$_SERVER['HTTP_REFERER']);
} else {
  header('Location: '.$app_base_path);
}

The page header determines if a user is currently signed in by checking the existence of an access token in the session. Checking the signed-in state is something that many parts of the Baking Disasters needs, so refactor this into a utility function. Add this function to util.php, as shown in Example 4-8.

Example 4-8. A function that checks the current user’s sign-in state

function is_logged_in()
{
  if (isset($_SESSION['access_token'])) {
    return true;
  } else {
    return false;
  }
}

And update the header of each page to use it, as shown in Example 4-9.

Example 4-9. Using the is_logged_in() function abstracts the session implementation out of your PHP pages

<body>
<header class="blog-header">
  <span class="login">
    <?php if(is_logged_in()) { ?>
      <a href="logout.php">Logout</a>
    <?php } else { ?>
      <a href="login.php">Sign in with Google+</a>
    <?php } ?>
  </span>
  <a href="index.php"><img id="blog-logo" src="images/logo.png"/></a>
  <h1>Baking Disasters</h1>

Locking Stuff Down

Now that users can sign in, you can restrict access to sensitive features such as the disaster submission form and the administration console.

Restricting the disaster submission form to currently signed in users is quite easy. Just add an is_logged_in check when you render the form in recipe.php, as shown in Example 4-10.

Example 4-10. Only display the report attempt form to signed in users

<section class="content attempt-form">
<?php if(is_logged_in()) { ?>
  <h2>Report Your Attempt</h2>

  <p>Have you attempted this recipe with disastrous results?
    Tell us about it!</p>

  <form method="post">
    <input type="hidden" name="recipe_id" value="<?= $_GET['id'] ?>"/>
    <label>Your Name: <input name="author_name"></label>
    <label>Description: <textarea name="description"></textarea></label>
    <label>Photo URL: <input type="text" name="photo_url"/></label>
    <input type="submit"/>
  </form>
<?php } else { ?>
  <p>Log in to tell us about your attempt!</p>
<?php } ?>
</section>

And when you insert into the database, as shown in Example 4-11.

Example 4-11. Only allow signed-in users to write to the database

<?php
include_once("util.php");
if ($_POST && is_logged_in()) {
  insert_attempt($_POST['recipe_id'], $_POST['author_name'],
                 $_POST['description'], $_POST['photo_url']);
  echo "<p class='notice'>Attempt inserted!</p>";
}
?>

Unauthenticated users are now asked to log in to share their attempts, as shown in Figure 4-13.

The logged-out view of the recipe page

Figure 4-13. The logged-out view of the recipe page

This plugs up the biggest opening for spam, but what if someone discovers admin.php? They could insert new recipes. To ensure that only the site administrator, Jenny Murphy, can add new recipes, check to see if the signed in user has the correct user ID. To make this comparison fetch the signed in user’s profile from the API.

The starter project shows us how to make this call. It fetches the current user’s profile just after it validates that the current user has an access token. This call is shown in Example 4-12.

Example 4-12. Fetch the signed-in user from the API

$me = $plus->people->get('me');

Wrap this behavior into a get_plus_profile function, as shown in Example 4-13.

Example 4-13. A function that fetches the signed-in user

function get_plus_profile()
{
  if (!is_logged_in()) {
    die("Expected to be logged in here");
  }

  $client = init_api_client();
  $client->setAccessToken($_SESSION['access_token']);
  $plus = new apiPlusService($client);
  $me = $plus->people->get('me');
  return $me;
}

And then use the code in Example 4-14 to verify that Jenny is the user viewing admin.php.

Example 4-14. Check the signed-in user’s profile ID to restrict access to admin.php

</header>
<?php
if(is_logged_in()) {
  $me = get_plus_profile();
  if($me['id'] == "102817283354809142195") {
    if ($_POST) {
      insert_recipe($_POST['name'], $_POST['description'],
                    $_POST['ingredients'], $_POST['directions'],
                    $_POST['photo_url']);
      echo "<p class='notice'>Recipe inserted.</p>";
    }
?>
<section class="content">
...
</section>
  <?php   } else { echo "Only Jenny Murphy can access this page.";  }}?>
<footer>

This prevents interlopers from adding recipes. Instead, they see the error message shown in Figure 4-14.

The logged-out view of the Baking Disasters administration console.

Figure 4-14. The logged-out view of the Baking Disasters administration console.

All writes to Baking Disasters are now protected, but there’s so much more that you can do with Google+.

A Preview of the Sign-in Button

OAuth 2.0 is an amazing standard. It gives your users a way to share their Google data and identity with your application. Unfortunately, to support OAuth, you had to add quite a bit of code to your application. OAuth is complex, but implementing it does not have to be.

The Google+ sign-in button, shown in Figure 4-15, aims to make implementing OAuth easier. It accomplishes this by providing you with a plugin just like the +1 button. When the user clicks the Sign In button they are taken through an OAuth flow and the code is returned to your application. This replaces the first half of your OAuth code with a single line of markup, shown in Example 4-15.

Baking Disasters refactored to use the sign-in button

Figure 4-15. Baking Disasters refactored to use the sign-in button

Example 4-15. Sign-in button markup

<g:plus action="connect"
    clientid="1234567890.apps.googleusercontent.com"
    callback="onSignInCallback"></g:plus>

The sign-in button is currently in developer preview. During this time, preview only works for developers who are enrolled in the preview. You can start experimenting with it, but you can’t release software that uses it until the developer preview has finished. You should also be prepared for breaking changes at any point during this developer preview.

The developer preview is tied to a Google account. When you are logged in to Google with a developer preview account, the sign-in button will render. Use this form to sign up: https://developers.google.com/+/history/preview/.

Google Developers provides theoretical explanation, API reference material, and starter projects that use the Sign In button: https://developers.google.com/+/history/.

Making Baking Disasters Social

Baking Disasters now requires authentication for the creation of data. Authentication is useful, but it’s neither very exciting nor does it add any new features to your site. Let’s knock it up a notch and use the APIs to leverage even more features of Google+.

Import Disasters from Google+

Baking Disasters would benefit a lot from importing content from Google+. If users could import attempts from their recent activity on Google+ they can leverage the Google+ mobile application. They can live-share their baking attempt on the Google+ mobile application. Once the smoke has cleared and they return to their laptops, they can import content at their convenience. This flow is described in Figure 4-16.

During the baking party the user takes photos and records activity on their cell phone; after the party ends, the user can import that activity from Google+ into Baking Disasters

Figure 4-16. During the baking party the user takes photos and records activity on their cell phone; after the party ends, the user can import that activity from Google+ into Baking Disasters

Importing activity is a bit more involved than the previous enhancements. It will require you to add a new page to provide an import interface and update the recipe page to render imported attempts.

To comply with the developer policies, which restrict the storage of Google+ user data, you must store references to the activities on Google+ instead of the whole activity. This requires refactoring of your database. It also means that you must handle activities that have been deleted. However, it pays off in the form of automatically handling updates and edits. The resulting flow is shown in Figure 4-17.

A sequence diagram describing what happens during the baking party, when the user imports activity into Baking Disasters, and when Baking Disasters renders the activity on the recipe page

Figure 4-17. A sequence diagram describing what happens during the baking party, when the user imports activity into Baking Disasters, and when Baking Disasters renders the activity on the recipe page

The database schema changes are simple, but they do require a breaking change. Example 4-16 shows the new table creation statement. You can either run this by hand using the SQLite command line interface or delete the database and start from scratch.

Example 4-16. A new table schema for baking attempts

sqlite_exec($db, 'create table attempts (recipe_id int,
  google_plus_activity_id text);');

Just as in the previous enhancements most of the heavy lifting will be done by functions in util.php. Add a function that fetches a page of public activities for the currently logged in user. You can model this code from the activity list in the starter project. The resulting code is shown in Example 4-17.

Example 4-17. Fetch the signed in user’s recent public activities from the API

function get_recent_activities()
{
  if (!is_logged_in()) {
    die("Expected to be logged in here");
  }

  $client = init_api_client();
  $client->setAccessToken($_SESSION['access_token']);
  $plus = new apiPlusService($client);

  $optional_parameters = array('maxResults' => 20);
  $activities =
      $plus->activities->listActivities('me', 'public', $optional_parameters);
  return $activities;
}

Having changed the way activities are stored, you also need to update the way that you recall them, as shown in Example 4-18. The database only stores the activity ID. The rest of the fields must come from an API call to fetch each activity.

The fetch is a great time to clean up any activities that were deleted from Google+. If the API returns a 404, you know that the activity is gone and you should clean up the reference to it.

Example 4-18. This code fetches the activities associated with each recorded attempt for a specific recipe ID. If an activity no longer exists, it removes it from the database

function list_attempts($recipe_id)
{
  $db = init_db();
  $recipe_id = sqlite_escape_string(strip_tags($recipe_id));

  $query = sqlite_query($db, "select * from attempts where recipe_id = '$recipe_id'"); 1
  $attempt_stubs = sqlite_fetch_all($query, SQLITE_ASSOC);

  $client = init_api_client();
  $plus = new apiPlusService($client);

  $attempts = Array();
  foreach ($attempt_stubs as $attempt_stub) {
    $google_plus_activity_id = $attempt_stub['google_plus_activity_id'];
    try {
      $activity = $plus->activities->get($google_plus_activity_id); 2

      $attempt = Array();
      $attempt['url'] = $activity['url'];
      $attempt['author'] = $activity['actor'];
      $attempt['description'] = $activity['object']['content'];
      if (count($activity['object']['attachments']) > 0) { 3
        $attempt['photo_url'] =
            $activity['object']['attachments'][0]['image']['url'];
      }
      array_push($attempts, $attempt);
    } catch (Exception $e) {
      if ($e->getCode() == 404) { 4
        // If it's a 404, it has been deleted by the user. Clean it up.
        sqlite_exec($db, "delete from attempts where
                          google_plus_activity_id='$google_plus_activity_id';");
      }
    }
  }
  return $attempts;
}
1

Fetch the list of activity IDs for this recipe from the database.

2

Fetch the full activity for each one using the Google+ API.

3

Assuming that the first attachment is an image, extract it from the API response.

4

Deleted activities return a 404. If you see one, remove the activity ID from your database.

This method returns a list of activities that is far more rich than the previous representation. All you need to do is to update the code that renders it in recipes.php, as shown in Example 4-19. You also can also take advantage of the profile icon that is returned with the attached user object.

Example 4-19. Display attempts from Google+ on the recipe page

<section class="content attempts">
  <?php foreach ($attempts as $attempt) { ?>
  <div class="attempt">
    <?php if(isset($attempt['photo_url'])) { ?>
    <img class="attempt-photo" src="<?= $attempt['photo_url'] ?>" />
    <?php } ?>

    <h3>
      <a href="<?= $attempt['author']['url']?>">
        <img src="<?= $attempt['author']['image']['url']?>"/></a>
      <?= $attempt['author']['displayName'] ?>'s Attempt
      <a class="import-link" href="<?= $attempt['url'] ?>">imported from Google+</a>
    </h3>

    <p>
      <?= str_replace("\n", "<br/>\n", stripslashes($attempt['description'])) ?>
    </p>
    <div style="clear:both;"></div>
  </div>
  <?php } ?>
</section>

Reload Baking Disasters and use the new import feature to pull in some of your recent baking attempts. The resulting import should look like Figure 4-18.

Two baking attempts imported from Google+

Figure 4-18. Two baking attempts imported from Google+

Your modest PHP web application is better than ever. You have used the Google+ APIs for a simple sign-in solution. Knowing who is signed in has provided the tools that you need to restrict administrative features to the correct users. You’ve also used the activity APIs to enable users import their recent adventures and associate them with a recipe. The way this integration was implemented has the added benefit of automatically updating the imported view when the canonical copy on Google+ is updated or deleted.

Additional endpoints exist for searching activities, listing people who took action on a specific activity and viewing comments. You can use these same techniques to add functionality that pulls data from these endpoints.

A Preview of the History API

The same developer preview for the sign-in button also includes a write API for Google+ called the history API. This API allows your application to write moments that represent your user’s activity to a private place on Google+. Later your user can share the moments that are important to them with the right people on Google+.

Signing up for this developer preview follows the same process as for the sign-in button. Complete the sign-up form and your account will be able to create API projects that use the history API and write moments to your Google history. You can also see these moments in a beta version of the Google+ history user interface at https://plus.google.com/history.

Each write to the history API has two parts: an HTTP POST and a publicly accessible target page with schema.org structured markup. The POST body, shown in Example 4-20, describes the type of activity that has occurred. It includes the URL of the entity that was the target of the activity and a JSON representation of any content created as the result of the activity.

Example 4-20. A CommentActivity

{
  "type": "http://schemas.google.com/CommentActivity", 1
  "target": { 2
    "url": "https://developers.google.com/+/plugins/snippet/examples/blog-entry"
  },
  "result": { 3
    "type": "http://schema.org/Comment",
    "url": "https://developers.google.com/+/plugins/snippet/examples/blog-entry#comment-1",
    "name": "This is amazing!",
    "text": "I can't wait to use it on my site :)"
  }
}
1

The type of activity that has been performed. This must be one of the types predefined by Google.

2

The publicly accessible URL of the entity upon which the activity occurred.

3

If the activity created content, a JSON representation of that data.

The target page, specified in the POST by target.url, is the second part of the moment write. It is fetched when the moment is written. This fetch extracts schema.org markup and includes it into the moment.

If you have already added schema.org markup to your pages for other social plugins like the +1 button, you will not need to make any additional changes to them.

Warning

The history API is currently in developer preview. Expect rapid changes. Expect these changes to break your code.

Please see the documentation to see the latest starter projects, reference docs, and implementation advice.

The developer preview of the history API provides a rare opportunity. Google is very interested in feedback from developers as they experiment with the APIs. For example, there is a pre-populated form for requesting new moment types: https://developers.google.com/+/history/api/moments#request_a_new_type. If you request a moment type or change to the API during the developer preview it is much more likely to be incorporated into the released API.

Best Practices

For the sake of simplicity and ease of understanding the example code presented so far has cut some corners. For example, code has been copied and pasted in many places and it is not as efficient as it could be. Here are some tips to help guide you through efficient usage of the API.

Cache

The code presented above creates client libraries and often uses them only once. Cache client libraries. They can be reused. Leverage caches for API requests too. Most of the client libraries take care of this for you, but you should still make sure that the client library is configured to cached data appropriately for your project. For example, the PHP client library provides four different cache implementations, one of which may work better for you.

Be paranoid

Don’t trust anyone when it comes to cross site scripting. Be very cautious when rendering input that comes from a user, even if it’s coming via a Google API. Escape everything. The implementation of these practices will differ a bit on your language and platform.

Get Developing with Google+ now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.