Ignacio Blanco, OpenSocial Developer
August 2008
Overview
If you have ever tried to enhance an OpenSocial application using one of the Google data APIs, you may have noticed that not all API features are available since you don't have authorization to access private feeds or modify any data.
As an example, the Google Calendar data API provides access to a user's calendar data but only for calendars that are public. In order to access private calendars and edit or create events, you will need to authenticate with the Google data service.
AuthSub Authentication
The Google data APIs allow web-based applications to authenticate using a method called AuthSub. The process is illustrated below and involves two stages: obtaining an authentication token (steps 1-4) and using the token to authenticate with the Google data service (steps 5-6).
- The web application (an OpenSocial app, in this case) sends a HTTP request to the Google Accounts page.
- The user is prompted to enter their username and password (or select an account that is already authenticated).
- The user enters their credentials or selects previously authenticated account.
- The user is redirected back to the application with a 'token' URL parameter.
- The application invokes the Google data service (Google Calendar for example) using the token for authentication.
- The Google data service responds.
The above flow is almost valid for OpenSocial applications. The only limitation is that the applications run inside an IFrame on the container's site. This means that during step 4, when the application is invoked again, the redirect will go to the IFrame itself and not to the container URL, causing this approach to fail.
We can obtain an authentication token from the Google Accounts Authentication service by sending a request to the appropriate URL. In this case, we make the AuthSubRequest to the following URL:
https://www.google.com/accounts/AuthSubRequest?scope=[OUR_SCOPE]&next=[OUR_APPLICATION_URL]
Here we can specify the 'scope' parameter to specify the Google data API we want to access as well as the 'next' parameter, which allows us to tell the AuthSub service where to redirect the user after authentication to display our app.
For Google Calendar, we will use the following values:
- scope - The scope for the Calendar data API:
http://www.google.com/calendar/feeds/
- next - The URL of the canvas page of our app (note that this string needs to be URL encoded):
- For orkut:
http://sandbox.orkut.com/Application.aspx?uid=16106640322329497443&appId=112386189666&bpc=1
- For hi5:
http://201697359.hi5.com/friend/apps/entry/blanconet.googlepages.com/authSub.xml?view=canvas&from=devhome
- For orkut:
In the JavaScript of our OpenSocial app, we set the current
window.location
to the AuthSubRequest URL.
var scope = 'http://www.google.com/calendar/feeds/';
var next = escape(window.location);
window.location = 'https://www.google.com/accounts/AuthSubRequest?scope=' + scope + '&next=' + next;
This will cause the Google Authentication page to load in the IFrame. When the user grants access, the IFrame's location will be redirected to the 'next' URL which we've set above to point back to the application's canvas page.
Note that any application state will be lost during the redirection to the authentication page. We can save it beforehand using the OpenSocial Persistence API, or add it to the 'next' parameter sent to the AuthSub service.
Now we have another issue to work through---because we did not use the
login
method, the Google data JavaScript client library won't work without one more step. The JavaScript client library documentation explains that the authentication token is stored in a cookie in order to sign the requests. The
login
method sets the cookie when the user grants access and the
GoogleService
class uses this cookie when invoking the requests. Since we are not using the
login
method from the API, we have to set the cookie ourselves. The format is: g314-[SCOPE]=[TOKEN]:
var token = getUrlParam('token'); //getUrlParam is a method that takes all URL parameters from the window.location
document.cookie = 'g314-http%3A%2F%2Fwww.google.com%2Fcalendar%2Ffeeds%2F=' + token;
We're all set. Now we can continue using the Google data API to access the private feeds, allowing us to create and get data.
Requesting an AuthSub token
The following code snippet groups the excerpts shown above into a complete function that can be called from an application.
1. function authSubRequest() \{ 2. var scope = 'http://www.google.com/calendar/feeds/'; 3. var next = window.location // TODO add here + '&[[]SOME_EXTRA_URL_PARAMETER_OR_APP_STATE=' + VALUE; 4. var url = 'https://www.google.com/accounts/AuthSubRequest?'; 5. url += 'scope=' + scope; 6. url += '&session=1&secure=0&'; 7. url += 'next=' + escape(next); 8. // TODO add here the OpenSocial persistence API call to store application state 9. window.location = url; 10. \}
When invoking the code above, the browser will redirect the user away from our application and all the state data that isn't saved will be lost. To avoid this, we can choose between two different approaches:
- In line 3, we can add some application data to the 'next' URL parameter. This way, the data will be available as a URL parameter when the user is redirected back to our application from the Google authentication page.
- In line 8, we can use the OpenSocial Persistence API to store application state and retrieve it when coming from the Google authentication page.
Using the Google Calendar Javascript API
The following code snippet includes a function to interact with the Google Calendar service.
1. function create() {
2. var token = getUrlParam('token');
3. var calendarService = new google.gdata.calendar.CalendarService('authSubExample');
4. document.cookie = 'g314-http%3A%2F%2Fwww.google.com%2Fcalendar%2Ffeeds%2F=' +token;
5. // The default "private/full" feed is used to insert event to the primary calendar of the authenticated user
6. feedUri = 'http://www.google.com/calendar/feeds/' + getUrlParam('calendarId') + '/private/full';
7. var entry = new google.gdata.calendar.CalendarEventEntry();
8. entry.setTitle(google.gdata.Text.create(getUrlParam('eventTitle')));
9. var when = new google.gdata.When();
10. var startTime = new google.gdata.DateTime(new Date());
11. var endTime = new google.gdata.DateTime(new Date());
12. when.setStartTime(startTime);
13. when.setEndTime(endTime);
14. entry.addTime(when);
15. // The callback method that will be called after a successful insertion from insertEntry()
16. var callback = function(result) {
17. console.log('event created');
18. }
19. // Error handler will be invoked if there is an error from insertEntry()
20. var handleError = function(error) {
21. console.log(error);
22. }
23. calendarService.insertEntry(feedUri, entry, callback, handleError, google.gdata.calendar.CalendarEventEntry);
24. }
In line 4, we set the cookie that will be used by the Google Calendar client library. This is the step where we bypass the invocation of the login function:
scope = "http://www.google.com/calendar/feeds/";
var token = google.accounts.user.login(scope);
The rest of the code is fully related to the Google Calendar library: creating an event, building the feed URL and finally inserting the entry.
Utility methods
The following function is used along with the example code in order to get URL parameters values based on the name.
}
Note on the use of SessionAuthToken
It is important to note that the token we're using is a one-time use token. There is a way of exchanging this token for a long-lived session token, but it won't work in the context of an OpenSocial app. Obtaining a long-lived session token requires the application to use
gadgets.io.makeRequest
to send a request using the Authorization header, but this header is filtered by the OpenSocial containers.Conclusionh1. While the Google data APIs allow web applications to authenticate in order to fully access the underlying data, OpenSocial applications are a different kind of application since they are "contained" in a social network. In order to use the AuthSub authentication method, some extra code is needed, but in the end it's possible to use all of the provided features. And remember, although this article has referenced the Google Calendar data API, the same technique can be applied to other Google data services supported by the JavaScript client library.Author Bioh1. Ignacio is an Argentinean software engineer, technology enthusiast, and has been passing his days working around Google products both from the inside--one year working on the Google Checkout API (including a presentation at the 2007 JavaOne conference on Google Checkout with Patrick Chanezon) and a 3 month internship-and outside--posting in Google Checkout forums and participating in the OpenSocial trusted tester program. Ignacio has been involved in several projects that integrate with Google technology, such as helping in the creation of BuckDrop, working on the first version of OpenSocket with Dan Lester, and building BuyFast, an OpenSocial app for e-commerce. Since finishing his internship at Google, Ignacio has been working on an OpenSocial application that integrates with Google Maps and the Google Calendar API, which, incidentally, inspired him to write this article!Resources==