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 }

62 comments:

Kevin said...

Thank you very much for sharing code!!

I did have to change the following function to add access_type and approval_prompt inorder to have a refresh token returned.


function _formatOAuthReq($OAuthParams, $scope)
{
$uri = $OAuthParams['oauth_uri'];
$uri .= "?client_id=" . $OAuthParams['client_id'];
$uri .= "&redirect_uri=" . $OAuthParams['redirect_uri'];
$uri .= "&scope=" . $scope;
$uri .= "&response_type=code";
$uri .= "&access_type=offline";
$uri .= "&approval_prompt=force";

return $uri;
}

Robert Scavilla said...

Thanks Kevin - I updated the code with your changes.

rhobinz said...

function _get_refresh_token($params, $code)
{
$url = $params['oauth_token_uri'];

$fields = array(
'code' =>$code, //this should not be included
'client_id' => $params['client_id'],
'client_secret' => $params['client_secret'],
'refresh_token' => $token['refresh_token'],
'grant_type' => 'refresh_token'
);
$response = _do_post($url, $fields);
return $response;
}

parameter invalid: code

rhobinz said...

Do the refresh token expires? Or I can call the same refresh token to get another access token again?

Robert Scavilla said...

Hi rhobinz, I haven't tested this, however, According to Google, "Refresh tokens are valid until the user revokes access. I'll try to run a test if I have time over the next days. I'll let you know what I find.

...bob

Marko said...

A life saver! Thanks man!

Anonymous said...

Thank you! Worked like a charm!

Khaleel Shaik said...

Hi Robert,

This is excellent, I am looking for the same in JAVA with Servlets/JSP. Would you be able to make one for the JAVA guys?

Khaleel

Khaleel Shaik said...

Hi,

$response = curl_exec($ch); statement is not returning neither the results not the FALSE. Any idea? What am I missing?

--Khaleel

Khaleel Shaik said...

Hi,

I have added the following two options as well then I am getting proper token response in JSON format.

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);


--Khaleel

Robert Scavilla said...

Thanks for you notes Khaleel. I've note had any issues with the CURL response. I'm glad you posted a solutions. Could this be a JAVA specific issue?

An additional note; I store the access token in a cookie: setcookie($cookieName, $access_token, time() + $json_obj->expires_in, '/');

When my application accesses this cookie and it's expired, it will refresh the access token using the refresh token.

...bob

Anonymous said...

i love you

Robert Scavilla said...

:=)

Chidambaram Subramanian said...

Hi,

Thank you for sharing the code. Code works like chram.
I have an issue with getting user info (ex: email id, first name and last name).

Below is the code change i have done for it:
line 33: $OAuth_request = _formatOAuthReq($OAuth,
"https://www.google.com/m8/feeds/");
line 54: $post_string = "https://www.google.com/m8/feeds/";

Result:
After successfull granting the permission, i'm not getting any info printed on screen.

I guess this is releated to method "_get_wmt_sites_feed".
Please help me to reslove this issue.

Robert Scavilla said...

Hello Chidambaram, for the contact fee;

in the _get_wmt_sites_feed() function, if you set the $post_string to something like:
https://www.google.com/m8/feeds/contacts/liz%40gmail.com/full

and after $response = file_get_contents($post_string); add the following;
echo print_r($reponse);

// do not run _parse_wmt_response();

you should see the results for liz@gmail.com

Let me know the results,
...bob

Chidambaram Subramanian said...

Bob,

Thanks a lot for your reply.
I did as what you said and got all the contacts of user google account printed on screen.
But i wanted user info such as email, fname,lname and user id. Below is the step i followed after your suggestion:

Defined Scope as :
$OAuth_request = _formatOAuthReq($OAuth, "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile");

And

$post_string = "https://www.googleapis.com/oauth2/v2/userinfo";

Hope this helps other guys.

Again, thanks bob. :)

Anonymous said...

please help, i am not getting the access token? what to do?

Anonymous said...

got the error
file_get_contents() [function.file-get-contents]: Unable to find the wrapper "https" - did you forget to enable it when you configured PHP?

for this line
$response = file_get_contents($post_string);

and also the error
Trying to get property of non-object

for this line
foreach ($xml->entry as $entry)

any help? thanks!

Anonymous said...

from the above codes, how can i retrieve phone number,email,and others?

Robert Scavilla said...

I assume you are using XAMPP?? If so, try uncommenting ;extension=php_openssl.dll in /apache/bin/php.ini

...bob

Bhuvana said...

Hi Robert Scavilla,

Thanks for this post.
But I wanted to pass username/password(Resource Owner Password Credentials Grant) and get the access token. How can I achieve this.

Thanks,
Bhuvana

Unknown said...

I want the permission page to appear just once in my app i.e. when the user logs in for the first time.
Can anyone suggest me how to do it?

Robert Scavilla said...

Hello Umang, if you keep the client information the same and use the same call back url you will not get repeat request to authenticate your app. Please give me more details and I can help you further,
...bob

Unknown said...

Thanks bob,
I am sending this request
https://accounts.google.com/o/oauth2/auth?state=profile&
scope=https://www.googleapis.com/auth/plus.me&
redirect_uri='.$redirect_uri.'&
response_type=code&access_type=offline&
client_id='.$client_id.'

Over here i have kept the client_id and redirect_uri as constants from my google api console.
Still i keep getting authentication page again.

Unknown said...

Thank you for your Instructions, but i need a way to fetch the Crawler Errors (Feed). Did you have any idea or solution?

Thank you!

PHP Development Company said...

Nice php code examples.

Unknown said...

Silentium... really helped me out got most of my feeds working now.

Frank The Tank said...

Hi Robert

Thx for that, really helpful!

@Silvio Tischer:

To get the Crawl Errors you have to define another scope:

$OAuth_request = _formatOAuthReq($OAuth,"https://www.google.com/webmasters/tools/feeds/crawlissues");

or get the whole scope by:
https://www.google.com/webmasters/tools/feeds/

then encode your siteid, I did this with a little workaround, because "urlencode" did not what I want.

$site = urlencode(utf8_encode("http://www.YOURSITE_ID.com/"));
str_replace(".", "%2", $site);

$post_string = "https://www.google.com/webmasters/tools/feeds/".$site."/crawlissues/";
$post_string .= '?v=2';
$post_string .= '&oauth_token=' . $access_tokens['access_token'];

I hope this helps!
You also have to parse the crawl new, because its another structure.

I hope that helps!

Thx Robert again!
Best, Thomas

Robert Scavilla said...

Great post Frank, Thank you! I like the urlencode fix.
...bob

Frank The Tank said...
This comment has been removed by the author.
Frank The Tank said...

you're welcome!

and I got another issue which I want to share with you. When you parse the xml from googlw with SimpleXML, SimpleXML does not know how to deal with namespaces!

Check the google XML here:
https://developers.google.com/webmaster-tools/docs/2.0/developers_guide_protocol

for example there is an element called "wt:verified" and you will not get the value easily with SimpleXML, if you do not go over the namespace! The solution is that you have to deal with children("namespace_url"). An example for "verified" is mentioned here:

$title = $xml->children()->entry->children()->title;

$verified = $xml->children()->entry->children("http://schemas.google.com/webmasters/tools/2007")->verified;

For german speaking people, check this:

http://blog.gauner.org/blog/2007/08/30/xml-dokumente-mit-namespaces-in-php5simplexml-verarbeiten/

Chirag said...

Hi,

I am getting error when pass url using file_get_contents().
On my local system it is working fine but on server it is give me forbidden error.

It is giving error "failed to open stream: HTTP request failed! HTTP/1.0 403 Forbidden ".

Can you guide me?
Does under webmaster tools there is any API Limit?

Robert Scavilla said...

Chirag, This error is most likely because your PHP app failed to authenticate. Did you successfully get your Google API Key and did you setup the callbacks?

Anonymous said...

Dear, Thanks for the code, I got the token , added my site url but not showing any results, any hint?

Robert Scavilla said...

Anonymous, I need more information; did the $token_response array get populated?

Anonymous said...

Hi, Thanks for the example!
How to use it for Facebook?

MSR said...

I want to use this to make a GET request as I can only do a Sites List with a get request, is there a way to make this do that? I have it setup and working, but no results will return and the google documentation says it must be an authenticated GET request for this to work.

Anonymous said...

so has anyone managed to get this working?

Seeman words said...

Bingo super life saver! why google don't release proper client library for webmaster api.

Chirag said...

How to use auth token with New API version 3?
$post_string = "https://www.google.com/webmasters/tools/feeds/sites/";
$post_string .= '?v=2';
$post_string .= '&oauth_token=' . $access_tokens['access_token'];
Under New API this is not working I cannot able to pass like this.
$post_string = "https://www.googleapis.com/webmasters/v3/sites/";
$post_string .= '?oauth_token=' . $access_tokens['access_token'];
Any help would be nice.

Anonymous said...

Chirag, u need to change the code:
1) if ($AuthCode == NULL)
{ $OAuth_request = _formatOAuthReq($OAuth,
"https://www.googleapis.com/auth/webmasters"); //changing the scope
...

2) function _get_wmt_sites_feed($access_tokens) {
$post_string = "https://www.googleapis.com/webmasters/v3/sites?fields=siteEntry/siteUrl";
$post_string .= '&oauth_token=' . $access_tokens['access_token'];
$response = file_get_contents($post_string);
var_dump($response); //$response is in json-format, not in xml
...

ismavolk said...

Thanks!!!

Harilal K said...

Hi Robert,
Can you pl post a code to send email using "gmail oauth2 php"?
Googling did not lead me to anything. -thanks in advance

Harilal K said...

Hi Robert,
Can you pl post a code to send email using "gmail oauth2 php"?
Googling did not lead me to anything. -thanks in advance

Unknown said...

Hi,
I am using above script, I get error when fetch value of feed. Please help me

Robert Scavilla said...

Hi Ajit, the GWMTs API changed a few months back and I have not had a chance to update this script. The scope URL changed. you can try replacing www.google.com/webmasters/tools/feeds/
with
www.googleapis.com/auth/webmasters.readonly

I have not tried this yet so let me know the results.
...bob

Unknown said...

This is a brilliant blog! I'm very happy with the comments!..
Webmaster Forum

Krunal Patel said...

how to print contact result ?

Krunal Patel said...

Please help me, How to print all contact after google api working successful.

Unknown said...

OUNNAS ABD EL KADER

Unknown said...

google.com/+abdelkaderounnasURL

Unknown said...

plus.google.com/+abdelkaderounnasURL

Unknown said...

ABDELKADEROUNNAS@GMAIL.COM

Unknown said...

google.com/+abdelkaderounnas@gmail.comurl

Unknown said...

plus.google.com/+abdelkaderounnas@gmail.comurl

Unknown said...

ounnas abd el kader youtube

Unknown said...

ounnas abd el kader linkedin

Unknown said...

ounnas abd el kader faecbook

Unknown said...

ounnas abd el kader twitter

Unknown said...

ounnas abd el kader google+1

Unknown said...

ounnas abd el kader google+

Unknown said...

Thank Yo For Update

http://mp3base.co

Post a Comment