My favorites | English | Sign in

Faster apps faster - GWT 2.0 with Speed Tracer New!

Google Data Protocol

OAuth in the Google Data Protocol Client Libraries

This document describes how to use the Google Data API client libraries to connect to Google's OAuth Authentication for Web Applications.

The OAuth interface allows a web-based application to access a Google service on behalf of a user. To maintain a high level of security, OAuth enables the application to get an access token without ever handling the user's account login information.

The Google Data API client libraries provide methods to help you use OAuth in your web application. Specifically, there are methods for constructing the acquiring a request token, authorizing the request token, and exchanging the authorized request token for an access token. The libraries also handle the necessary signing algorithms when making requests to a Google Data service.

Contents

Audience

This document is intended for programmers who want their web-based applications to access Google services on behalf of users, using the Google Data APIs client libraries.

This document assumes that you are familiar with the OAuth interface and the general process for incorporating OAuth into your web application. For a complete description of OAuth's protocol, see OAuth Authentication for Web Applications or the official specification at oauth.net.

Using OAuth and Google Data APIs without the client libraries

If you want your web application to interact with a Google Data service using OAuth as an authorization method, then everything you need to know is in OAuth Authentication for Web Applications. There's no need to use the Google Data APIs client libraries if you don't want to.

Here's an outline of how your application might authenticate a user using OAuth:

  1. Your application makes a signed request to fetch an initial OAuth request token from the OAuthRequestToken endpoint.
  2. Your application redirects the user to the appropriate OAuthAuthorizeToken URL to authorize the request token.
  3. Upon granting access, the user is redirected back to your application (the oauth_callback url)
  4. Your application sends a signed request to upgrade the authorized request token to an access token using the OAuthGetAccessToken endpoint.

The Google Data APIs client libraries simplify this authorization process by handling various details for you. This document explains how.

Registering your web application

OAuth requires that all API calls be digitally signed. Google supports the HMAC-SHA1 and RSA-SHA1 signature methods. In order to sign requests, your application first needs to register with Google. Once you've registered, Google will provide you with a consumer key (and secret for use with HMAC-SHA1), and a place to upload a public certificate.

1. Registering your domain

Please follow the steps outlined in Registration for Web-Based Applications.

2. Creating a private key / public certificate pair (optional)

If you choose to use RSA-SHA1 as the oauth_signature_method, you'll need to create a self-signing RSA private key and public certificate pair. See Generating a self-signing private key and public certificate (below) for examples on how to do that.

Working with OAuth and the Google Data APIs: client library examples

The following sections show examples of using the Google Data APIs client library methods to follow the steps outlined in the "Working With OAuth" section of the OAuth documentation.

Determining the scope of your data access

Each Google service defines a scope value which determines (and possibly narrows) a token's access to the user's data. See the FAQ for the list of available scope values. To simplify these examples, we'll be talking to the Documents List Data API. It is also assumed that your example application is hosted at example.com.

To interact with the Documents List API, set the scope to http://docs.google.com/feeds/.

Note: Always set the scope value to the broadest URL possible unless you have need for a finer restriction. For example, a narrower scope like http://docs.google.com/feeds/documents/private/full will restrict the token's access to the documents/private/full feed. Using http://docs.google.com/feeds/ will allow access to all of DocLists's feeds: http://docs.google.com/feeds/*.

Multi-scoped tokens

To create tokens that access multiple Google Data APIs, seperate each scope with a url-encoded space (%20). The example below creates a token which will have access to both a user's Google Documents and Google Calendar data.

scope=http://www.google.com/calendar/feeds/%20http://docs.google.com/feeds/

Fetching a request token

Using HMAC-SHA1 as the signature method:

import com.google.gdata.client.docs.*;
import com.google.gdata.client.authn.oauth.*;

String CONSUMER_KEY = "example.com";
String CONSUMER_SECRET = "abc123doremi";

GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
oauthParameters.setScope("http://docs.google.com/feeds/");

OAuthHmacSha1Signer signer = new OAuthHmacSha1Signer();

GoogleOAuthHelper oauthHelper = new GoogleOAuthHelper(signer);
oauthHelper.getUnauthorizedRequestToken(oauthParameters);

Using RSA-SHA1 as the signature method:

import com.google.gdata.client.docs.*;
import com.google.gdata.client.authn.oauth.*;

String CONSUMER_KEY = "example.com";

GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
oauthParameters.setScope("http://docs.google.com/feeds/");

PrivateKey privKey = getPrivateKey("/path/to/your/rsakey.pk8");
OAuthRsaSha1Signer signer = new OAuthRsaSha1Signer(privKey);

GoogleOAuthHelper oauthHelper = new GoogleOAuthHelper(signer);
oauthHelper.getUnauthorizedRequestToken(oauthParameters);

...

public static PrivateKey getPrivateKey(String privKeyFileName) {
  FileInputStream fis = new FileInputStream(new File(privKeyFileName));
  DataInputStream dis  = new DataInputStream(fis);

  byte[] privKeyBytes = new byte[(int)privKeyFile.length()];
  dis.read(privKeyBytes);
  dis.close();
  fis.close();

  String BEGIN = "-----BEGIN PRIVATE KEY-----";
  String END = "-----END PRIVATE KEY-----";
  String str = new String(privKeyBytes);
  if (str.contains(BEGIN) && str.contains(END)) {
    str = str.substring(BEGIN.length(), str.lastIndexOf(END));
  }

  KeyFactory fac = KeyFactory.getInstance("RSA");
  EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(Base64.decode(str));
  return fac.generatePrivate(privKeySpec);
}

Using HMAC-SHA1 as the signature method:

import gdata.auth
import gdata.docs.service

CONSUMER_KEY = 'example.com'
CONSUMER_SECRET = 'abc123doremi'
SIG_METHOD = gdata.auth.OAuthSignatureMethod.HMAC_SHA1

client = gdata.docs.service.DocsService(source='yourCompany-YourAppName-v1')
client.SetOAuthInputParameters(SIG_METHOD, CONSUMER_KEY, consumer_secret=CONSUMER_SECRET)

req_token = client.FetchOAuthRequestToken()
client.SetOAuthToken(req_token)

Using RSA-SHA1 as the signature method:

import gdata.auth
import gdata.docs.service

CONSUMER_KEY = 'example.com'
SIG_METHOD = gdata.auth.OAuthSignatureMethod.RSA_SHA1

f = open('/path/to/yourRSAPrivateKey.pem')
RSA_KEY = f.read()
f.close()

client = gdata.docs.service.DocsService(source='yourCompany-YourAppName-v1')
client.SetOAuthInputParameters(SIG_METHOD, CONSUMER_KEY, rsa_key=RSA_KEY)

SCOPES = ['http://docs.google.com/feeds/', 'http://www.google.com/calendar/feeds/']  # example of a multi-scoped token
req_token = client.FetchOAuthRequestToken(scopes=SCOPES)
client.SetOAuthToken(req_token)

Authorizing a request token

To authorize a request token, your application must redirect the user to the OAuthAuthorizeToken URL, which prompts them to log into their Google account. For more information on the OAuthAuthorizeToken URL, see the full OAuth Authentication for Web Applications.

To construct the OAuthAuthorizeToken URL in your application, use the following for each client library. Note, these samples build on the previous examples.

For HMAC-SHA1, you need some way to persist the token secret in order to create an OAuth token object coming back from the approval page. To do that, set a session variable or cookie. However, the simplest solution is to include the token secret as a query parameter in your oauth_callback URL.

String oauth_callback = "http://www.example.com/UpgradeToken.jsp";
oauth_callback.append("?oauth_token_secret=").append(
    java.net.URLEncoder.encode(oauthParameters.getOAuthTokenSecret(), "UTF-8"));

oauthParameters.setOAuthCallback(oauth_callback);

String approvalPageUrl = oauthHelper.createUserAuthorizationUrl(oauthParameters);
System.out.println(approvalPageUrl);

For RSA-SHA1, the oauth_token_secret is unused so there's no need to persist the token secret.

oauthParameters.setOAuthCallback("http://www.example.com/UpgradeToken.jsp");

String approvalPageUrl = oauthHelper.createUserAuthorizationUrl(oauthParameters);
System.out.println(approvalPageUrl);

For HMAC-SHA1, you need some way to persist the token secret in order to create an OAuthToken object coming back from the approval page. To do that, set a session variable or cookie. However, the simplest solution is to include the token secret as a query parameter in your oauth_callback URL.

# req_token is from previous call to client.FetchOAuthRequestToken()
oauth_callback_url = '%s?oauth_token_secret=%s' % (self.request.uri, req_token.secret)
approval_page_url = client.GenerateOAuthAuthorizationURL(callback_url=oauth_callback_url)

For RSA-SHA1, the oauth_token_secret is unused so there's no need to persist the token secret.

approval_page_url = client.GenerateOAuthAuthorizationURL(callback_url=self.request.uri)

After constructing the approval page URL, your application can use it in a variety of ways to send the user to the OAuthAuthorizeToken handler. The most common approach is to redirect the user or display a link to that page.

Extracting the token from the callback URL

When Google redirects back to your application, the oauth_token is appended to the "oauth_callback_url" URL as a query parameter. Your application should then extract the token value from its URL query parameter and re-establish the oauth parameters.

The client libraries provide convenience methods for extracting the oauth_token. These samples build on the previous examples.

If you've chosen to persist the token secret in the callback URL (when using HMAC-SHA1):

GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);

OAuthHmacSha1Signer signer = new OAuthHmacSha1Signer();

GoogleOAuthHelper oauthHelper = new GoogleOAuthHelper(signer);
oauthHelper.getOAuthParametersFromCallback(request.getQueryString(), oauthParameters);

The only difference with RSA-SHA1 is the signing method:

GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);

PrivateKey privKey = getPrivateKey("/path/to/your/rsakey.pk8");
OAuthRsaSha1Signer signer = new OAuthRsaSha1Signer(privKey);

GoogleOAuthHelper oauthHelper = new GoogleOAuthHelper(signer);
oauthHelper.getOAuthParametersFromCallback(request.getQueryString(), oauthParameters);

If you've chosen to persist the token secret in the callback URL (when using HMAC-SHA1), make sure to set the secret on the token:

oauth_token = gdata.auth.OAuthTokenFromUrl(self.request.uri)
if oauth_token:
  oauth_token.secret = cgi.escape(self.request.get('oauth_token_secret'))
  oauth_token.oauth_input_params = gdata.auth.OAuthInputParams(
      gdata.auth.OAuthSignatureMethod.HMAC_SHA1, CONSUMER_KEY, consumer_secret=CONSUMER_SECRET)
 client.SetOAuthToken(oauth_token)
else:
  print 'No oauth_token found in the URL'

The process is similar for RSA-SHA1, but without the token secret:

oauth_token = gdata.auth.OAuthTokenFromUrl(self.request.uri)
if oauth_token:
  oauth_token.oauth_input_params = gdata.auth.OAuthInputParams(
      gdata.auth.OAuthSignatureMethod.RSA_SHA1, CONSUMER_KEY, rsa_key=RSA_KEY)
 client.SetOAuthToken(oauth_token)
else:
  print 'No oauth_token found in the URL'

Upgrading to an access token

The last step in the OAuth token dance is to upgrade the authorized request token to a long-lived access token using the OAuthGetAccessToken URL, as described in the full OAuth Authentication for Web Applications documentation.

Here are some examples using each of the client libraries:

String accessToken = oauthHelper.getAccessToken(oauthParameters);
// You can also pull the OAuth token string from the oauthParameters:
// String accessToken = oauthParameters.getOAuthToken();
System.out.println("OAuth Access Token: " + accessToken);

String accessTokenSecret = oauthParameters.getOAuthTokenSecret();
System.out.println("OAuth Access Token's Secret: " + accessTokenSecret);
access_token = client.UpgradeToOAuthAccessToken()  # calls SetOAuthToken() for you

Note: If you're using HMAC-SHA1, be sure to save the access token's token secret alongside the token value in your database, otherwise you won't be able to properly reconstruct the oauth parameters for later use.

Using an access token

After you've obtained an access token, use the standard Google Data APIs client library calls to interact with the service. The library will take care of signing the requests and including the correct Authorization header for you. Typically, you'll be recalling the user's token from a cookie or database. These examples demonstrate how to reconstruct the oauth parameters and make a client library call.

If you're using HMAC-SHA1:

  GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
  oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
  oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
  oauthParameters.setOAuthToken(ACCESS_TOKEN);
  oauthParameters.setOAuthTokenSecret(TOKEN_SECRET);

  OAuthHmacSha1Signer signer = new OAuthHmacSha1Signer();

  DocsService client = new DocsService("yourCompany-YourAppName-v1");
  client.setOAuthCredentials(oauthParameters, signer);

  URL feedUrl = new URL("http://docs.google.com/feeds/documents/private/full");
  DocumentListFeed resultFeed = client.getFeed(feedUrl, DocumentListFeed.class);
  for (DocumentListEntry entry : resultFeed.getEntries()) {
    System.out.println(entry.getTitle().getPlainText());
  }
  

The difference with RSA-SHA1 is that you don't need to set the access token's secret and constructing the signer object is different:

  GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
  oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
  oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
  oauthParameters.setOAuthToken(ACCESS_TOKEN);

  PrivateKey privKey = getPrivateKey("/path/to/your/rsakey.pk8");  // See above for the defintion of getPrivateKey()
  OAuthRsaSha1Signer signer = new OAuthRsaSha1Signer(privKey);

  DocsService client = new DocsService("yourCompany-YourAppName-v1");
  client.setOAuthCredentials(oauthParameters, signer);

  URL feedUrl = new URL("http://docs.google.com/feeds/documents/private/full");
  DocumentListFeed resultFeed = client.getFeed(feedUrl, DocumentListFeed.class);
  for (DocumentListEntry entry : resultFeed.getEntries()) {
    System.out.println(entry.getTitle().getPlainText());
  }
  

Assuming HMAC-SHA1:

client = gdata.docs.service.DocsService(source='yourCompany-YourAppName-v1')
client.SetOAuthInputParameters(SIG_METHOD, CONSUMER_KEY, consumer_secret=CONSUMER_SECRET)

# the token key and secret should be recalled from your database
client.SetOAuthToken(gdata.auth.OAuthToken(key=TOKEN, secret=TOKEN_SECRET))

feed = client.GetDocumentListFeed()
for entry in feed.entry:
  print entry.title.text

Additional Resources and Samples

Back to top

2 Legged OAuth

2 legged OAuth allows domain administrators to create or customize third-party applications (controlled by their organization) to access their users' Google Data without their direct involvement or authorization. Specifically, an access token is not required as per the normal authorization flow (also referred to as 3-legged OAuth).

The following client library samples demonstrate how to setup your client to use 2 Legged OAuth using HMAC-SHA1.

import com.google.gdata.client.docs.*;
import com.google.gdata.client.authn.oauth.*;

String CONSUMER_KEY = "example.com";
String CONSUMER_SECRET = "abc123doremi";

GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
OAuthHmacSha1Signer signer = new OAuthHmacSha1Signer();

DocsService client = new DocsService("yourCompany-YourAppName-v1");
client.setOAuthCredentials(oauthParameters, signer);

// Retrieve user's list of Google Docs
String user = "any.user@" + CONSUMER_KEY;
URL feedUrl = new URL("http://docs.google.com/feeds/documents/private/full" +
                      "?xoauth_requestor_id=" + user);

DocumentListFeed resultFeed = client.getFeed(feedUrl, DocumentListFeed.class);
for (DocumentListEntry entry : resultFeed.getEntries()) {
  System.out.println(entry.getTitle().getPlainText());
}
using Google.GData.Client;
using Google.GData.Documents;

// Create an OAuth factory to use
GOAuthRequestFactory requestFactory = new GOAuthRequestFactory("writely", "yourCompany-YourAppName-v1");
requestFactory.ConsumerKey = "example.com";
requestFactory.ConsumerSecret = "abc123doremi";

String user = "any.user";  // any.user@example.com

DocumentsService client = new DocumentsService("yourCompany-YourAppName-v1");
client.RequestFactory = requestFactory;

DocumentsListQuery query = new DocumentsListQuery();
query.Uri = new OAuthUri("http://docs.google.com/feeds/documents/private/full", user, requestFactory.ConsumerKey);

DocumentsFeed feed = client.Query(query);

foreach (DocumentEntry entry in feed.Entries)
{
  Console.WriteLine(entry.Title.Text);
}
import gdata.auth
import gdata.docs.service

CONSUMER_KEY = 'example.com'
CONSUMER_SECRET = 'abc123doremi'
SIG_METHOD = gdata.auth.OAuthSignatureMethod.HMAC_SHA1

requestor_id = 'any.user@example.com'

client = gdata.docs.service.DocsService(source='yourCompany-YourAppName-v1')
client.SetOAuthInputParameters(SIG_METHOD, CONSUMER_KEY, consumer_secret=CONSUMER_SECRET,
                               two_legged_oauth=True, requestor_id=requestor_id)

# Retrieve user's list of Google Docs
feed = client.GetDocumentListFeed()
for entry in feed.entry:
  print entry.title.text

# Change to another user on your domain
client.GetOAuthInputParameters().requestor_id = 'another.user@example.com'

Additional Resources and Samples

Generating a self-signing private key and public certificate

The private key is used to generate a signature, which must be included with each request. The public key embedded in the certificate is used by Google to verify the signature. The public key must be a 1024-bit RSA key encoded in an X.509 certificate in PEM format. The certificate should be sent to Google at time of registration.

The following sections provide examples of how to generate keys and certificates using two particular tools: the OpenSSL utility and Java's keytool utility.

These examples are not specific to the Google Data APIs; you can use the same utilities to generate keys for any purpose.

The examples assume that your company is named My_Company, and is located in Mountain View, California, US, with domain name example.com.

Generating keys using OpenSSL

To create a pair of RSA keys and the corresponding certificate, you could use the following command:

# Generate the RSA keys and certificate
openssl req -x509 -nodes -days 365 -newkey rsa:1024 -sha1 -subj \
  '/C=US/ST=CA/L=Mountain View/CN=www.example.com' -keyout \
  myrsakey.pem -out /tmp/myrsacert.pem

Warning: Including the -nodes parameter creates a private key without a password to protect it. However, you should consider omitting this parameter for added security.

The -sha1 parameter specifies that the key will be used to generate SHA1 signatures.

The -subj parameter specifies the identity of the application that the certificate represents.

The -keyout parameter specifies the file that will contain the keys. This file contains sensitive information and should be protected and not shared with anyone.

The -out parameter specifies the file that will contain the certificate in PEM format (which can be sent to Google while registering).

Generating keys for the .NET client

The .NET framework doesn't understand keys or certificates stored in the PEM format. Therefore, an additional step is needed once you have created the .pem file:

openssl pkcs12 -export -in test_cert.pem -inkey myrsacert.pem -out myrsacert.pfx -name "Testing Certificate"

This step generates a PFX file from your private key and certificate. This file can be imported into the .NET client library to digitally sign requests made to the Google Data APIs.

Generating keys for the Java client

The Java client accepts private keys in the PKCS#8 format. After generating a key/cert using the directions above, create a .pk8 file from your generated .pem file:

openssl pkcs8 -in myrsakey.pem -topk8 -nocrypt -out myrsakey.pk8

Alternatively, you can use the Java key store and the keytool utility to create a pair of RSA keys and the corresponding certificate. Use the following command:

# Generate the RSA keys and certificate
keytool -genkey -v -alias Example -keystore ./Example.jks\
  -keyalg RSA -sigalg SHA1withRSA\
  -dname "CN=www.example.com, OU=Engineering, O=My_Company, L=Mountain  View, ST=CA, C=US"\
  -storepass changeme -keypass changeme

Warning: "changeme" is not a good password; this is just an example.

The -dname parameter specifies the identity of the application that the certificate represents. The -storepass parameter specifies the password to protect the keystore. The -keypass parameter specifies the password to protect the private key.

To write the certificate to a file that can be used in the ManageDomains tool, use the following command:

# Output the public certificate to a file
keytool -export -rfc -keystore ./Example.jks -storepass changeme \
  -alias Example -file mycert.pem

Back to top