Using the Draft OAuth Assertion Grant with Google+

2014-09-10 14:38:35 -0400

The IETF has been working on a new OAuth standard for "assertions" which enables OAuth to work with other types of authentication systems. This can be used to allow users to authenticate with your API through Google+ or other third-party identity providers.

For example, let's say you are developing a single-page Javascript app or a mobile app that uses both Google's APIs as well as your own APIs. You'd like to have users authenticate with Google to obtain access to Google's APIs, but then you'd also like your app to authenticate with your server to gain access to some additional resources. You'd like to not reinvent the wheel and use OAuth for your own API. You also implicitly trust Google to verify the user's identity, so you don't want the user to need to go through another OAuth flow just to use your API.

Assertion grants allow you to do this in a standards-compliant way. This is a draft standard that was just submitted in July of 2014, but for this simple use-case, it is already fairly usable.

How Google+ handles sign in for "combination" apps (with both a client and a server)

Google has some great documentation on how to authenticate both a client and a server, which is worth reading if you plan on implementing this. The gist of it is that first the client authenticates with Google through a OAuth popup or redirect. This gives the client both an access token and an access code. The code is then passed to the server to authenticate the backend.

This "passing the code to the backend step" is what OAuth assertion grants enable in a standards-compliant way.

OAuth Assertion Grants

The IETF Assertion Grant spec defines a way to define new grant types that are assertions of identity from third parties. An assertion grant looks like this (from the example in the spec):
{
   "grant_type": "urn:ietf:params:oauth:grant-type:saml2-bearer",
   "assertion": "PHNhbWxwOl...[omitted for brevity]...ZT4",
   "scope": ""
}
Assertions are very similar to Resource Owner Password Credential grants in that they are passed as HTTP POSTs directly to the /token endpoint. The "grant_type" for an assertion must be a absolute URI that defines the assertion type, the "assertion" is a Base64-encoded string (using URL-safe encoding) that contains the actual asserrtion, and the "scope" is the same as other OAuth grant types.

An OAuth Assertion Grant for Google+

Since Google has not defined an assertion grant format for Google+ identity, I've decided to make one up! You can feel free to steal this format for your own apps.
{
   "grant_type": "urn:googlepluscode",
   "assertion": "(see below)",
   "scope": "(specific to your app)"
}
For my Google+ assertion grant, I've just chose "urn:googlepluscode" as the URL. This is arbitrary, but Google would need to standardize this so we currently don't have a better option. For the assertion itself, I use a Base64-encoded, url-safe version of this JSON:
{
   "code": "(access code provided by the front-end when it authenticates with Google)",
   "google_plus_user_id": "(Google+ user ID)"
}

Verifying the Google+ assertion grant

When the backend receives the Google+ assertion grant, it should do these steps to verify it:
  1. Convert the access code into an access token
  2. Call the /oauth/tokeninfo endpoint with the access token from the previous step
  3. In the response from the tokeninfo endpoint, confirm these things:
    1. The user_id matches the google_plus_user_id in the assertion
    2. The issued_to from the tokeninfo response matches the client_id of your application (both the front-end and back-end share the same client_id.
Stay tuned for a future post on how to implement this with RailsĀ and Doorkeeper!