2-legged OAuth for the OpenSocial REST API

With version .8 of the OpenSocial standard, support was added for REST APIs.  The authentication mechanism chosen for those APIs was OAuth.  However, there are two flavors of OAuth, and this document attempts to explain the different use cases that they support in OpenSocial, as well as why the "2-legged" variant of OAuth is likely to be more important for most OpenSocial containers to support first.

For the details of the specification, see section "4. Request Authentication and Authorization Context" of http://www.opensocial.org/Technical-Resources/opensocial-spec-v08/restful-api-specification

2-legged or 3-legged
The OAuth term is most frequently used when referring to 3-legged OAuth.  This was a standardized version of a protocol that many vendors like Yahoo, Google, AOL, etc. had deployed which involved an end-user going through a "dance" where they start on the OAuth consumer site, and are redirected to the OAuth provider site where they are asked to approve access by the consumer site to their data.  If their approval is given, they are then bounced back to the OAuth consumer site.  While this "dance" has enabled a whole net set of applications and data-interoperability, the user experience can be confusing for an end-user (sort of like ballroom dancing with two partners at the same time).

2-legged OAuth is a term that is used to refer to another variant of OAuth which does not require this dance.  This variant is also called Signed Fetch or Phone Home, especially in the OpenSocial community.  In fact, the vast majority of actual REST API calls made on the Internet using OAuth are made using 2-legged OAuth, not 3-legged OAuth.  The reason for that is that this is the feature that allows a gadget to make a connection back to the home server's of the developers who created the gadget.  The following blog post contains an early description of this gadget feature:
This Signed Fetch feature of OpenSocial allows a gadget to "phone home" to its home servers.  The reason the OAuth dance is avoided is because those home servers do not have any pre-existing information about the end-user, so there is no need for the end-user to give approval to access that non-existing data.

Use Cases for OpenSocial REST API
There are two primary use cases for an OpenSocial container to expose a REST API
  • A gadget's home servers want to make a server-server call to the container to get more information about one of the end-users of that gadget, for example to check for new friends of that end-user.  This is referred to as Reverse Phone Home
  • A 3rd party site which DOES NOT have a gadget wants to get the end-users permission to access their data at the social network, for example to download their friend list, or to get permission to post to their activity stream
The second scenario does requires 3-legged OAuth, however the first scenario is the much more pressing reason for most containers to expose a REST API.  While it is theoretically possible to use 3-legged OAuth to support the Reverse Phone Home scenario, we will explain in the following sections why it is unnecessary, and would overly complicate the user experience.

The following diagram may have orient the reader to the data flows, click the picture to see a larger version.

Reverse Phone Home
When a user adds a gadget to their profile, they are generally presented with some options to control what level of access that gadget has to their OpenSocial information such as friend list, activity stream, etc.  Whatever UI flow is used to capture those permissions preferences can also be used to allow the user to decide what permissions the gadget's home servers should have to that data.  A container could even choose to default to giving the gadget's home servers the same permission as the gadget.  If the user later changes these permissions, or deletes the gadget using the UI flow that they use to manage gadgets today, those changes will similarly apply to the gadget's home servers.  This combined UI enables the end-user to avoid having to go through a second UI flow in an OAuth dance to decide what permissions the gadget's home servers should have.  More importantly, it offers a consistent way to edit or delete those permissions, because there is no existing standard UI flow for an end-user to manage those permissions outside the gadget framework.  For example, what if a user deleted a gadget, but forgot to go to a different UI to delete that gadget's home server's access to the end-user's data?

Now that we have avoided the OAuth dance, the gadget's home servers can make a REST API to the OpenSocial container to access the end-users data.  However, that creates two security/identity challenges:
  1. How does the container verify that the request did in fact come from the home server's of the gadget
  2. How does the home server identity which user's data it wants from the OpenSocial container
For the first problem, the container needs to issue a shared secret to the gadget developer.  The gadget developer can then use that shared secret to sign all the requests from their server to verify their identity.  One way for the container to do this is to provide a gadget registration site for developers where they can login to the container's developer site, and provide the URL of their gadget.  The container can then display a one-time code that the gadget developer would add somewhere in their gadget spec.  The container could then check the gadget for that code, and if found, it would remember that the currently logged in user was the developer of that gadget.  The container could then issue an OAuth Consumer Key & Consumer Key Secret to the gadget developer.  For a good example of how this is done, look at MySpace's OpenSocial registration process.

Note: Previously we mentioned the Phone Home feature of OpenSocial.  Those calls also need to be digitally signed by the container who is sending them, so that the gadget's home servers can verify their authenticity.  A container could choose to a shared secret to sign the phone home requests, and then ask the developer to use the same shared secret to sign the reverse phone home requests.

The second problem is identifying the user.  Fortunately, there is already a common user identifer that is shared by the container and the gadget's home servers.  Whenever the gadget makes a phone home call to its home servers, the container adds the IDs of the gadget owner and viewer.  The gadget's home servers can then store data locally about one of those user IDs.  If it wants to make a REST API call for more data about the user, it can simply pass the same user ID.

OpenSocial App Data
Another advantage that 2-legged OAuth has in this scenario is that when a REST call is received by the OpenSocial container, it can easily map it to the gadget which is associated with the requesting server.  If the server is asking for access to OpenSocial App Data, then the container needs to know which gadget's AppData to return, and this is made simpler by having the direct mapping between the home servers and gadget.  In addition, if the home servers want to add information to the user's activity steram, or send a message to a user, then the container can attribute the source of that post/message to the gadget.

Comparing URL Parameters:
To help explain the reverse phone home feature, the following table compares the URL parameters used for phone home and reverse phone home:

  Phone Home
Reverse Phone Home
 oauth_consumer_key Container Name, i.e. orkut.com
opensocial_appid from phone home request
 oauth_signaturegadget specific shared secret
gadget specific shared secret
 opensocial_appidgadget ID assigned by container
 opensocial_owneriduser ID assigned by container
 opensocial_vieweriduser ID assigned by container
 xoauth_requestor_id N/Aopensocial_ownerid from phone home request

Note that the oauth_signature can be done in both cases with the same shared secret (though some containers may prefer to sign phone home requests using a public/private key).  The container will assign the oauth_consumer_key that should be used in reverse phone home calls, but a simple option for the container would be to use the same appid that they send on phone home calls.

It is then the responsibility of the gadget's home servers to remember user IDs that it has seen in phone home calls, and when it wants to get more data about that user via a REST API call it should put that ID in the xoauth_requestor_id field that was specific in the .8 spec.

Accepting both 3 legged & 2 legged
While the section above describe how an OpenSocial container could accept 2-legged OAuth calls to its REST API, it can also accept 3-legged OAuth calls.  The same API endpoint can be used for both types of calls, it just needs to detect whether an OAuth token is passed, in which case 3-legged OAuth is being used.  In that case, the ID of the user's whose data is being requested would be extracted from the OAuth token, but the identity of the server's making the call would still be extracted from the oauth_consumer_key after validating the signature.