Saturday, June 18, 2011

Using OAuth 2.0 to Access Google APIs - PHP Code Example


Written by Robert Scavilla

Updated: December 19, 2011

This document outlines how to use OAuth 2.0 validation to access Google API feed endpoints. The Webmaster Tools API is used here only as an example of how to interact with a Google API feed.  The example code in this document is written in PHP.

OAuth 2.0

Google is now supporting OAuth 2.0 for API security validation. To summarize the draft specification of the OAuth 2.0 protocol, OAuth enables clients to access protected resources by obtaining an access token as "a string representing an access authorization issued to the client" rather than using the resource owner's credentials.  Tokens are issued to clients by an authorization server with the approval of the resource owner. For more information on OAuth 2.0, please reference the Google documentation Using OAuth 2.0 to Access Google APIs.

Getting a Google API Key

Before you begin, you must register your application with Google and get an API key. This registration associates your Google account with your application. I won’t go into great detail here, but in short here are the steps to get a Google API key for your application:
  • If you don’t already have an API project, create one. (https://code.google.com/apis/console)
  • Under the Project menu (top left) select API Access
  • Click on Create an OAuth 2.0 client ID…
  • You will be prompted to fill in your branding information. The name of your application (Product name) and the Product Logo will be displayed to the user during the authentication process.
  • When you click next, you will be prompted to fill in the Client ID settings. Under Application type, be sure Web application is selected. Next to Your site or hostname click on (more options).  The dialog box will look like this:


  • Authorized Redirect URIs: During the authentication process, the OAuth2 server will prompt the user to Allow or disallow your application to have access to the Google account. You must register at least one URL here to which you want the OAuth2 server to send the permission response. You can register multiple redirect URLs in this box.
  • Enter your root domain in the Authorized Javascript Origins – this field is not used in this example.
  • Click on the Create client ID button. You will then be taken to your API Access page. You will see four fields in the Client ID for web applications box:
Client ID: xxxxxxxx.apps.googleusercontent.com
Client secret: xcfrtgd_dfLcHxx6
Redirect URIs: https://www.example.com/oauth2callback
JavaScript origins: https://www.example.com


Ask permission to access the Google Account

The first step in OAuth2.0 Server side authentication is for the application to send a request to the Google OAuth2 server with the Client ID information and the redirect URL. The OAuth2 server prompts the user to allow or disallow your application to access the Google account.


If the user clicks on the No thanks button, the redirect URL is called with the URI parameter:
?error=access_denied

If the user clicks on the Allow Access button, the redirect URL is called with the URI parameter:
?code=4/FXxxxxxxxxxxxxx_LcSwvVZGk  .

The value of the code parameter is the authorization code. This authorization code needs to be swapped for an access token, but first, here is the code to get the authorization code.


 1 <?php
 2 $OAuth = array(
 3     'oauth_uri' => 'https://accounts.google.com/o/oauth2/auth',
 4     'client_id' => 'insert your Client ID here',
 5     'redirect_uri' => 'http://example.com/OAuth2' // replace with your redirect uri
 6 );
 7 $title = 'No Code';
 8 $AuthCode = 'Null';
 9
10 // see if error parameter exisits
11 $error = _get_url_param($_SERVER['REQUEST_URI'], 'error');
12 if ($error != NULL)
13 {   // this means the user denied api access to GWMTs
14     $title = $error;
15 }
16 else
17 {   // does the code parameter exist?
18     $AuthCode = _get_url_param($_SERVER['REQUEST_URI'], 'code');
19     if ($AuthCode == NULL)
20     {   // get authorization code
21         $OAuth_request = _formatOAuthReq($OAuth,
22                         "https://www.google.com/webmasters/tools/feeds/sites/");
23
24         header('Location: ' . $OAuth_request);
25         exit; // the redirect will come back to this page and $code will have a value
26     }
27     else
28     {
29         $title = 'Got Authorization Code';
30     }
31 }
32
33 function _formatOAuthReq($OAuthParams, $scope)
34 {
35     $uri = $OAuthParams['oauth_uri'];
36     $uri .= "?client_id=" . $OAuthParams['client_id'];
37     $uri .= "&redirect_uri=" . $OAuthParams['redirect_uri'];
38     $uri .= "&scope=" . $scope;
39     $uri .= "&response_type=code";
40
41     return $uri;
42 }
43
44 function _get_url_param($url, $name)
45 {
46     parse_str(parse_url($url, PHP_URL_QUERY), $params);
47     return isset($params[$name]) ? $params[$name] : null;
48 }
49 ?>
50 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
51 <html>
52     <head>
53         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
54         <title><?= $title; ?></title>
55     </head>
56     <body>
57         <h1>OAuth2 Authorization Code</h1>
58         <p>Code = <?= $AuthCode; ?></p>
59
60     </body>
61 </html>
62
On line 22 in the source code listing above is the URL of the Google Webmaster Tools 'sites' feed. This feed is referred to as the 'scope' of the authorization meaning that the authorization code is only valid for this feed. Each feed requires its own authorization code. You can consult the Google Webmaster Tools API documentation if you require more information on this feed.

Requesting the Access Token

Now that we have the authorization code we need to exchange it for an access token and refresh token. This requires POSTing parameters to the OAuth 2.0 server. To do the POST, I am using the PHP curl library. Changes from the first listing include:  the $OAuth array defined on line 2 of the source listing was modified, the $token array was added, and there were two additional functions added to format and POST the request. Here is the PHP code:


  1 <?php
  2 $OAuth = array(
  3     'oauth_uri' => 'https://accounts.google.com/o/oauth2/auth',
  4     'client_id' => 'insert your Client ID here',
  5     'client_secret' => 'insert your client secret here',
  6     'redirect_uri' => 'http://example.com/OAuth2', // replace with your redirect uri
  7     'oauth_token_uri' => 'https://accounts.google.com/o/oauth2/token'
  8 
  9 );
 10 $token = array(
 11     'access_token' => '',
 12     'token_type' => '',
 13     'expires_in' => '',
 14     'refresh_token' => ''
 15 );
 16 
 17 $title = 'No Code';
 18 $AuthCode = 'Null';
 19 
 20 // see if error parameter exisits
 21 $error = _get_url_param($_SERVER['REQUEST_URI'], 'error');
 22 if ($error != NULL)
 23 {   // this means the user denied api access to GWMTs
 24     $title = $error;
 25 }
 26 else
 27 {   // does the code parameter exist?
 28     $AuthCode = _get_url_param($_SERVER['REQUEST_URI'], 'code');
 29     if ($AuthCode == NULL)
 30     {   // get authorization code
 31         $OAuth_request = _formatOAuthReq($OAuth,
 32                         "https://www.google.com/webmasters/tools/feeds/sites/");
 33 
 34         header('Location: ' . $OAuth_request);
 35         exit; // the redirect will come back to this page and $code will have a value
 36     }
 37     else
 38     {
 39         $title = 'Got Authorization Code';
 40         // now exchange Authorization code for access token and refresh token
 41         $token_response = _get_auth_token($OAuth, $AuthCode);
 42         $json_obj = json_decode($token_response);
 43         $token['access_token'] = $json_obj->access_token;
 44         $token['token_type'] = $json_obj->token_type;
 45         $token['expires_in'] = $json_obj->expires_in;
 46         $token['refresh_token'] = $json_obj->refresh_token;
 47         echo 'access_token = ' . $json_obj->access_token;
 48     }
 49 }
 50 
 51 function _get_auth_token($params, $code)
 52 {
 53     $url = $params['oauth_token_uri'];
 54 
 55     $fields = array(
 56         'code' => $code,
 57         'client_id' => $params['client_id'],
 58         'client_secret' => $params['client_secret'],
 59         'redirect_uri' => $params['redirect_uri'],
 60         'grant_type' => 'authorization_code'
 61     );
 62     $response = _do_post($url, $fields);
 63     return $response;
 64 }
 65 
 66 function _do_post($url, $fields)
 67 {
 68     $fields_string = '';
 69 
 70     foreach ($fields as $key => $value)
 71     {
 72         $fields_string .= $key . '=' . $value . '&';
 73     }
 74     $fields_string = rtrim($fields_string, '&');
 75 
 76     $ch = curl_init();
 77     curl_setopt($ch, CURLOPT_URL, $url);
 78     curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 79     curl_setopt($ch, CURLOPT_POST, count($fields));
 80     curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
 81     $response = curl_exec($ch);
 82     curl_close($ch);
 83 
 84     return $response;
 85 }
The response from the POST is a JSON array which is decoded on line 42 and stored in the $token array. The access token has a life of 1 hour.

Using the access token to request data from the Google Webmaster Tools 'sites' feed

Now your server app is fully authorized and we are ready to request data from a feed. Since the scope of the authorization is for the Webmaster Tools sites feed, we can only request services from that feed. The following code retrieves the list of servers registered in your Google Webmaster Account. The request is sent to the sites feed and the response is in xml format. The _parse_wmt_sites_response function parses the title field for each entry and builds an HTML list of the servers. The title field contains the URL of the site.

 36     else
 37     {
 38         $title = 'Got Authorization Code';
 39         // now exchange Authorization code for access token and refresh token
 40         $token_response = _get_auth_token($OAuth, $AuthCode);
 41         $json_obj = json_decode($token_response);
 42         $token['access_token'] = $json_obj->access_token;
 43         $token['token_type'] = $json_obj->token_type;
 44         $token['expires_in'] = $json_obj->expires_in;
 45         $token['refresh_token'] = $json_obj->refresh_token;
 46 
 47         $sites = _get_wmt_sites_feed($token);
 48     }
 49 }
 50 
 51 // Return the list of sites registered in your Google Webmaster Tools
 52 function _get_wmt_sites_feed($access_tokens)
 53 {
 54     $post_string = "https://www.google.com/webmasters/tools/feeds/sites/";
 55     $post_string .= '?v=2';
 56     $post_string .= '&oauth_token=' . $access_tokens['access_token'];
 57 
 58     $response = file_get_contents($post_string);
 59     return _parse_wmt_sites_response($response);
 60 }
 61 
 62 function _parse_wmt_sites_response($response)
 63 {
 64     $xml = simplexml_load_string($response);
 65     $response = '<br />';
 66     foreach ($xml->entry as $entry)
 67     {
 68         foreach ($entry->title as $title)
 69         {
 70             $response .= "<p><a href=\"$title\" target=\"_blank\">$title</a></p>";
 71         }
 72     }
 73     return $response;
 74 }

Access token expiration

When the access token expires an API call will return a HTTP 401 Unauthorized. To get a refresh token you can POST at any time to the token endpoint with the refresh token and the 'grant_type' parameter set to 'refresh_token'.


Get Refresh Token
 90 function _get_refresh_token($params, $code)
 91 {
 92     $url = $params['oauth_token_uri'];
 93 
 94     $fields = array(
 95         'code' => $code,
 96         'client_id' => $params['client_id'],
 97         'client_secret' => $params['client_secret'],
 98         'refresh_token' => $token['refresh_token'],
 99         'grant_type' => 'refresh_token'
100     );
101     $response = _do_post($url, $fields);
102     return $response;
103 }