Abdullah Diab’s Blog

Google Buzz, OAuth And Python

Google Buzz is a social networking and messaging tool from Google Inc. that’s integrated into GMail. Google Buzz was released in early February this year (9th Feb 2010), and since then it has emerged as an important social network for GMail users, and many people (including me) prefer it now to other social networking platforms such as Facebook.

Logos of Google Buzz, OAuth And Python
Logos of Google Buzz, OAuth And Python

In May Google revealed the Buzz API to the public so developers around the world could write applications to interact with Google Buzz to read and/or write.

Google uses OAuth for authentication and authorization to their services. OAuth is an open standard that allows users to share their content (or data generally) from one site to another (or to an application generally) without having to hand the other site their credentials. For more information about OAuth Open Standard go here

I was digging around to make a small application to notify me of new Buzzes or new comments on Buzzes, so I searched and read the API to interact with Google Buzz. In this post I’ll share my Python code that uses OAuth to authorize the application to use Google Buzz data, and soon I’ll share my application when it’s done 🙂

The Process

The process of authentication and authorization according to OAuth consists of three steps:

  1. Asking Google for a Request Token. The token is bound to a scope, this scope identifies the interests of your application with the user’s data.
<a id="GoogleBuzzScopes"></a>Google Buzz has two scopes; Full Access scope `https://www.googleapis.com/auth/buzz` and Read Only Scope `https://www.googleapis.com/auth/buzz.readonly`, you must select the scope that suits your needs.
  1. Asking the user to authorize your needs by redirecting him/her to a page in Google’s website to log in and authorize your application. As a result the user gets a verification code that must be delivered to your application so it can continue to step 3. There are three ways of getting the verification from the user listed here.
  2. Exchanging the Request Token for an Access Token that can be used later for authorizing your requests for data. Your application should save this token to use it later, this token has unlimited expiration time, so you don’t need to bother the user with authorization more than once (Unless the user revokes the access of your application from his/her account).

Prerequisites

  • My class depends on Python-OAuth2, so you need to download and install it first.
  • The code is written using Python 2.6.

General Parameters

All OAuth requests must have the following parameters:

  • oauth_version: Which defines the version of OAuth to use (since when writing this post there were only 1.0 and 1.0a I’ll be using 1.0).
  • oauth_nonce: A pseudo-random number.
  • oauth_timestamp: The timestamp when generating the request.
  • oauth_signature_method: The signature method used to sign the base string that identifies the request, which is either HMAC-SHA1 or RSA-SHA1 (It can be PLAINTEXT but Google doesn’t support it).
  • oauth_consumer_key and oauth_consumer_secret: The key and secret of your application, if you have registered your application then you’ll have a pair of key and secret for your application. If you don’t have a registered application you can use anonymous for both key and secret.
  • oauth_signature: Which is generated using the chosen signature method for the base string which identifies the request

So basically we’ll need to add those parameters to every request we make and we’ll have to sign the request. The former is done internally by calling __getParams, while the latter is done internally by calling __signRequest for each request.

Getting The Request Token

This request has the following parameters added to the general parameters:

  • oauth_callback: The URL that the user would be redirected to after authorizing your application, and when redirected the verification code is passed to this URL as a parameter along other token parameters. If you don’t have a URL or can’t redirect the user for some reasons you can use the special value oob (Out Of Bounds) so that Google redirects the user to a page in Google’s website that has the verification code inside it.
  • scope: The scope you selected for your application from here.
  • xoauth_displayname: The friendly name of your application that will Google will use when asking the user to authorize it.

This procedure is done by sending a POST request to the following URL https://www.google.com/accounts/OAuthGetRequestToken with the previous parameters while setting the ContentType header to application/x-www-form-urlencoded.

The response must be Request Token data with status code HTTP 200.

params = self.__getParams()
params.update({'oauth_callback' : self.callbackURL, 'scope' : self.defaultScope, 'xoauth_displayname' : self.applicationName})
oauthRequest = oauth.Request(method="POST", url=REQUEST_TOKEN_URL, parameters=params)
self.__signRequest(oauthRequest)
self.connection.request('POST', REQUEST_TOKEN_URL, body=oauthRequest.to_postdata(), headers={'Content-Type': 'application/x-www-form-urlencoded'})
resp = self.connection.getresponse()
if resp.status != 200:
	raise Exception, "Couldn't get a Request Token, status code: %d." % resp.status
self._token = oauth.Token.from_string(resp.read())
self.__destroyConnection()

This method __destroyConnection is used to destroy the connection because it becomes invalid after a few requests.

Redirecting The User To Authorization Page

Here we’ll have to know what is the URL of the page to redirect the user to, and that’s done by sending a GET request to the following URL https://www.google.com/buzz/api/auth/OAuthAuthorizeToken with the following parameters:

  • oauth_token: The key of the Request Token received in the previous step.
  • scope: Described earlier.
  • domain: The domain that your application uses, this is used for web applications, you must set it to anonymous for desktop applications.
  • xoauth_displayname: Described earlier.

The response would be a page with HTTP 302 status code, this page contains the URL to redirect the user to in the location header.

self.connection.request('GET', AUTHORIZE_TOKEN_URL + '?oauth_token=' + self.token.key + '&scope=' + self.defaultScope + '&domain=anonymous' + '&' + urllib.urlencode({'xoauth_displayname' : self.applicationName}).replace('+', '%20'))
resp = self.connection.getresponse()
if resp.status != 302:
	raise Exception, "Couldn't authorize the token, status code: %d." % resp.status
url = resp.getheader('location')
webbrowser.open(url)
self.__destroyConnection()

This method webbrowser.open is used to open the URL in the web browser of the user, it is -obviously- contained in the webbrowser Python standard module.

As a result of the authorization Google gives you (or the user) the verification code for the token, after acquiring it (automatically or by asking the user to give it to your application somehow) we give it to the token by calling setTokenVerifier method.

Exchanging The Request Token For The Access Token

This request has the following parameters added to the general parameters:

  • oauth_token: Described earlier.
  • oauth_verifier: The verification code acquired from the previous step.

This procedure is done by sending a POST request with the previous parameters to the following URL https://www.google.com/accounts/OAuthGetAccessToken.

The result is the Access Token data in a page with HTTP 200 status code.

params = self.__getParams()
params.update({
'oauth_token' : self.token.key,
'oauth_verifier' : self.token.verifier
})
oauthRequest = oauth.Request(method="POST", url=ACCESS_TOKEN_URL, parameters=params)
self.__signRequest(oauthRequest)
self.connection.request('POST', ACCESS_TOKEN_URL, body=oauthRequest.to_postdata(), headers={'Content-Type': 'application/x-www-form-urlencoded'})
resp = self.connection.getresponse()
if resp.status != 200:
        raise Exception, "Couldn't exchange the token with an Access Token, status code: %d.\nMessage: %s" % (resp.status, resp.read())
self._token = oauth.Token.from_string(resp.read())
self.__destroyConnection()

Authorizing Requests

Authorizing your requests for data is done by adding Authorization header to your request which includes the OAuth parameters along with the Access Token data, and by setting the ContentType header to either application/json or application/atom+xml:

params = self.__getParams()
params.update({
	'oauth_token' : self.token.key,
})
oauthRequest = oauth.Request(method=method, url=url, parameters=params)
self.__signRequest(oauthRequest)
if isJSON:
	headers = {'Content-Type': 'application/json'}
else:
	headers = {'Content-Type': 'application/atom+xml'}
headers.update(oauthRequest.to_header())
self.connection.request(method, url, body=body, headers=headers)
resp = self.connection.getresponse()
data = resp.read()
self.__destroyConnection()
return resp.status, data

Example On Using BuzzOAuth Class

This example uses the BuzzOAuth class to get the list of Buzzes for the user to read (The list of his/her friends’ Buzzes).

from BuzzOAuth import BuzzOAuth

boauth = BuzzOAuth()
boauth.requestToken()
boauth.authorizeToken()
boauth.setTokenVerifier(raw_input('Enter the verifier please: '))
boauth.convertToAccessToken()
boauth.saveTokenToFile()
status, data = boauth.request('GET', 'https://www.googleapis.com/buzz/v1/activities/@me/@consumption?alt=json')
print status
print ""
print data

The method request uses the Access Token to do the request passed to it and returns a tuple of the status code and data returned by the request.

In line 8 the call for method saveTokenToFile saves the Access Token to a binary file called token.tok by default, so that the application can later just use this token like this:

from BuzzOAuth import BuzzOAuth
		
boauth = BuzzOAuth()
boauth.loadTokenFromFile()
status, data = boauth.request('GET', 'https://www.googleapis.com/buzz/v1/activities/@me/@consumption?alt=json')
print status
print ""
print data

Download

You can download this class (BuzzOAuth.py) from here: http://www.box.net/shared/2m9srr12z7

License

This work is licensed under the GNU Public License (GPL). To view a copy of this license, visit http://www.gnu.org/copyleft/gpl.html.