Tuesday, January 25, 2011

Under the covers of OAuth 2.0 at Facebook

For the past three years, the Facebook Platform has been built on top of a session-based authentication system that many developers found complex. In order to make any API calls, you have to understand the details of signature algorithms. It’s a common source of problems for new developers using the Facebook Platform. We have been searching for a way to make it simpler.

The OAuth community has faced similar issues. If a new developer just wants to start using the Twitter API, suddenly they have to understand things like HMAC-SHA256 and how to sign their base string. That is just overkill for a simple web API.

Because of this complexity, I am really excited about the next version of OAuth. OAuth 2.0 largely solves the performance and usability issues from OAuth 1.0a. It relies on SSL instead of signatures as the default way to interact with applications, which means you can play with it in your browser. While there are drawbacks to exclusive reliance on SSL, it is so simple to get started that it can’t be beat for most developers. OAuth 2.0 also splits out flows for different security and performance contexts (i.e., desktop apps vs. web apps vs. mobile apps). That means that when a developer starts coding, they can see exactly what they need and start running.

How did we get here? Last fall, a small group wrote a proposal called OAuth WRAP, which introduced the core innovations of what would become OAuth 2.0 - using SSL and multiple flows. After Bret Taylor implemented OAuth WRAP in Friendfeed, we realized that it lived up to the promise. In the time since, we have been working with Yahoo, Twitter, Google, Microsoft, and many community members within the IETF to produce a new draft.

At f8, Facebook shipped a new Graph API, which relies entirely on OAuth 2.0. In this post, I’ll go into detail about exactly what we shipped and our plans for the future.

The access token is king

The OAuth 2.0 spec is divided into two sections:

* First, you get an access token
* Second, you use the token to access protected resources.

Using a token is the easy part, so we’ll start there.

To use an access token, just append it to the end of a protected resource with the oauth_token parameter. (To alleviate some developer confusion, Facebook also accepts access_token as the parameter name.)

All calls are required to go over HTTPS.

The new Graph API demonstrates how easy it is to get started. You can fetch data about any object on Facebook by hitting https://graph.facebook.com/. For instance, this is my profile without an access token - all you can see are a few public fields:

http://graph.facebook.com/lukeshepard

{ "id": "2901279", "name": "Luke Shepard", "first_name": "Luke", "last_name": "Shepard", "link": "http://www.facebook.com/luke.shepard" }

Now, if you append an access token (notice the protocol switches to https), you can see much more information about me, such as my employer:

https://graph.facebook.com/lukeshepard?access_token=....

{ "id": "2901279", "name": "Luke Shepard", "first_name": "Luke", "last_name": "Shepard", "link": "http://www.facebook.com/luke.shepard", "work": [ { "employer": { "id": 109719699048814, "name": "Facebook" }, "location": { "id": 104022926303756, "name": "Palo Alto, California" }, "position": { "id": 109981385691387, "name": "Software Development Engineer" }, "start_date": "2007-05" }, ....

The new Graph API only accepts OAuth tokens. All existing API calls are backwards compatible with our existing auth system, but they do accept an OAuth token as well. So the Graph API is a carrot to encourage OAuth adoption.

Getting an Access Token

It used to be that people just accessed services from their desktop or laptop computers, but in the past few years, that has changed. Now, people use laptops, all sorts of web browsers, mobile phones, devices connected to TVs, etc. One of the most common criticisms of OAuth 1.0a is that there is only one way to do things - the new version of OAuth 2.0 addresses that by allowing for multiple types of flows to get a token.

In this initial launch, Facebook supports three ways of getting a token.

Web server flow

The web server flow is intended for use by server-side developers that don't really like JavaScript. The whole flow works by redirecting the user to the authorization server (Facebook) and back to your site. It is baked into the Facebook docs as the default auth flow (if you don't pass "type" param, then this flow is assumed).

You must pre-register a "Connect URL" with the domain and path of their site. When the user tries to authorize, Facebook checks the "redirect_uri" to make sure it begins with the URL registered for the given "client_id".

You can also pass an optional "display" parameter to customize, primarily for mobile devices. Values accepted are "page" (default), "popup", "wap", and "touch" (the last two for mobile sites).

The Web Server flow.

The Web Server flow.

User-agent flow

While the code for web applications typically lives on the web server, far away from the user, sometimes it lives on the user’s machine. The main examples are desktop apps (Tweetdeck) and JavaScript-based applications (StreamDiff). Because the code actually runs on the client device, it can’t really rely on embedded secret keys for security - in JavaScript, anyone can look at the source and trivially extract the secret. So, we need something else.

The user agent flow is created for applications that cannot embed a secret key. The access token is just returned directly in the redirect response instead of requiring an extra server call. Security is handled in two ways:
* Facebook makes sure that the access token is not sent to a random webserver by validating the redirect_uri matches a pre-registered URL.
* The access token never goes across the wire in the clear. Even if redirect_uri is an HTTP url, the token itself is returned after the fragment (#) and so the browser will never send it to the server.

Facebook encourages the User Agent flow for use in Desktop applications. We plan to incorporate it into the the JavaScript client library

The User Agent Flow

The User Agent Flow

Client Credentials flow

This is perhaps the simplest flow - just exchange your client_id and secret for an access token, no user involved. We support this for accessing application-only resources. In particular, it’s required to use our new subscriptions API, modify developer settings, etc.

Session Exchange flow

Finally, for backwards compatibility, there is an endpoint where developers can exchange existing session keys for access tokens. We considered using the “Assertion Flow” for this, but we didn’t because a) it uses a bunch of parameters and is optimized for SAML tokens, and b) this is just a migration strategy and not really core to the spec.

Open Areas

The OAuth 2.0 spec is in a draft state, which means it’s still subject to change - especially around the edges. Since it’s much easier to add a feature later than to remove or change one, we have tried to implement the stable core while postponing controversial or unstable features. In particular, the following areas are really interesting:

Identity. As David Recordon posted, we would love to “get OAuth 2.0 to the point – fairly quickly – where we can start to architect the next version of OpenID on top of it.” Since Facebook provides both identity as well as authorization, it’s critical that we get this piece right in order to have a complete solution.

Signatures. Most of the benefit of OAuth 2.0 is its lack of signatures - however, there are some use cases where it is difficult or nonperformant to make SSL requests, and in those situations we want to use signatures. Signatures are also required for identity - the server needs to be able to verify that a given message came from Facebook without making another HTTP request. We will eventually add support for signatures.

Immediate mode. In order to do single sign in with JavaScript, we need the ability to check if the user is currently logged in with an iframe. Today, Facebook uses an endpoint called login_status.php to handle this, but we plan to use the immediate mode in the future.

Device flow. I am intrigued by the Netflix-style device flow, and I think it’s important for someone to implement it and try it out in the wild.

Refresh tokens. Most of the access tokens issued by Facebook are short-lived - they last for an hour or at most a day. In the OAuth 2.0 spec, the official way to handle long lived tokens is by issuing a “refresh token”, which is then exchanged repeatedly for new, short term access tokens. In the Facebook API, the developer just asks for the “offline_access” extended permission, and then their access token just lasts forever (or until the user revokes it). We may look into using refresh tokens in the future.

Error formats. The error messages are currently JSON encoded, but they should be form-encoded - we are planning to fix that.

Client state parameter. The “state” parameter was removed and then added back in, but we don’t support it. I believe that client state should be tracked in the redirect_uri, as it offers more protection.

Display parameter. We use a “display” parameter to support mobile flows, even though the spec does not officially include it (yet). We hope to see it included in a future draft.

What comes next?

The OAuth 2.0 draft is still a work in progress. As we work through the open issues, the spec will evolve. I’m looking forward to other companies shipping their own endpoints, and eventually building OAuth 2.0 into the fundamental fabric of how the Internet works. Please leave a comment or join the mailing list to get involved!

(reference from http://www.sociallipstick.com)

No comments:

Post a Comment