Demystifying Modern Social APIs: Social Sign-In

Andrew Dodson

This article will give you insights into the inner workings of the proprietary JavaScript libraries that many of us include in our Web projects. Social sharing buttons and federated authentication, which are found in the likes of the Live Connect JavaScript API and Facebook JavaScript SDK, are just two examples you may have come across.

In this article, you’ll learn about the OAuth 2.0 approach to user authentication, using XMLHttpRequest 2 for cross-origin resource sharing (CORS) and also REST. At the end, I’ll demonstrate a working app that allows users to connect to and manipulate their SkyDrive photos in the browser.

Getting Started

About two years ago, I was asked to add Windows Live and Facebook Connect buttons to a Web site, just like the two shown in Figure 1.

Social Sign-in Buttons
Figure 1. Social Sign-in Buttons

Adding these buttons to a Web page required two libraries, one from each of the providers, plus a little JavaScript to wire them up. Both libraries had some magic that made them work, although I doubted that all of the 200 KB of JavaScript I wrote was being used. Before I was asked to implement a third service, I opened up Fiddler and started to inspect what was going over the wire. After a little poking around, I found my way to the docs, and before I knew it I had the premise for an insightful article. So, get a cup of tea and a biscuit and enjoy the read.

A Glossary of Terms

When we talk of wiring up a Web app with other Web services, it’s useful to first familiarize yourself with the characters.

The app (otherwise known as the client) is your Web application or a Web site you use. The user is the end user who uses your app. The provider is the Web service that your app is going to connect to—for example, Windows Live or Facebook. The authorization server is the provider’s user-login service.

The Technologies

Two common industry standards are used to authenticate users and securely sign subsequent API requests: OAuth 1.0 and OAuth 2.0. The implementations of these underlying technologies do not differ, but the URLs and nuances between providers do. As such, many providers have their own JavaScript library to support their API, outlined in Table 1.

Provider OAuth Version
Windows Live API 2
Facebook Graph 2
Google API 2
Twitter 1.0a
Yahoo 1.0a
LinkedIn 1.0a
Dropbox 1.0

Table 1.API Technologies Used by Popular Social Sites

This article focuses on OAuth 2.0—and don’t be confused by the name. OAuth 2.0 and OAuth 1.0 are vastly different protocols. Furthermore, OAuth 1.0 has been deprecated by many Web services in favor of OAuth 2.0.

OAuth2: Authentication

Here’s how OAuth.net describes OAuth2: “An open protocol to allow secure authorization in a simple and standard method from web, mobile and desktop applications. . . . OAuth is a simple way to publish and interact with protected data. It’s also a safer and more secure way for people to give you access. We’ve kept it simple to save you time.”

I think of OAuth2 as an authentication mechanism that lets an app obtain an access token for a user, based on the provider’s Web service. The app can then use this access token to query or modify the provider’s data on behalf of the user.

Initiate OAuth 2

Initiating the authentication process begins with opening a new browser window to a special URL on the provider’s Web site. Here the user is prompted to sign in and agree to share certain functionalities with the application. The process is illustrated in Figure 2, where the provider is https://a.com and the client is  http://b.com/. Look at the URLs in the address bar, where you should see access_token in the final window. Figure 3 shows an example of a sign-in window from Windows Live. In the figure, the application adodson.com is asking for access to SkyDrive photos and documents.

OAuth2 Flow
Figure 2.OAuth2 Flow

OAuth 2 Consent Screen Hosted by Windows Live
Figure 3.OAuth 2 Consent Screen Hosted by Windows Live

The URL in Figure 3 is:

https://oauth.live.com/authorize?client_id=00001111000&scope=wl.photos&response_type=
  token&redirect_uri=http://b.com/redirect.html

This special URL is made up of an initial path for the authorization page and four required key-value parameters:

  • A client_id provisioned by the provider when the app owner registers the app. (Register yours for Windows Live athttps://manage.dev.live.com/.)
  • The scope, which is a comma-separated list of strings that denote which services the app can access. I maintain a list of possible scopes for various providers at http://adodson.com/hello.js/#ScopeandPermissions.
  • The response_type=token attribute, which translates to “Hey, return the access token immediately.”
  • The redirect_uri attribute, which is the address for where to redirect the window after the user has signed in or cancelled. This URL must belong to the same origin as the client_id when it was provisioned.

There’s also an optional state parameter, which is a string that, when it’s included, is simply returned in the response from the authentication provider.

Receiving the Access Token

After the user has authenticated and consented to share with the app, the browser window is redirected to the page defined in the redirect_uri parameter.  For example:

http://adodson.com/graffiti/redirect.html#access_token=EwA4Aq1DBAAUlbRWyAJjK5w968Ru3Cyt

Appended to the URL location hash (#) are some credentials:

  • access_token A unique string that can be used to query the provider’s API.
  • expires_in A number (in seconds) that access_token is valid for.
  • state The string that can be optionally passed into the state parameter and returned.

The credentials can be read relatively easily using the window.location object. For example, the access token can be extracted like so:

	var access_token =
	  (window.location.hash||window.location.search).match(/access_token=([^&]+)/);

After obtaining the access token, the next step is to use it.

OAuth2 History

OAuth 2.0 was devised in 2010 by some smart people at Microsoft and Facebook as a means to securely share data services with other applications on behalf of a user. It does this in a manner that need not rely on a server or complicated cryptic algorithms beyond SSL.

Since its inception, OAuth2 has become the de facto method with which third-party apps authenticate their users via Windows Live or Facebook and then tap into and share data with these megalithic data warehouses. The standard has since proliferated through Google’s services, LinkedIn and SalesForce, and Twitter has tweeted its interest. As you can see, OAuth2.0 comes highly endorsed.

Native Apps

An alternative parameter for response_type=token is response_type=code.  Using “code” prompts the provider to return a short-lived authorization code instead of an access token. The code is used in conjunction with the client secret (allocated at the time of registering the app), and the application must then make a server-to-server call to obtain the access token. This approach gets around domain restrictions that are imposed on redirect_uri, yet it ensures it’s the same app. Using “code” is therefore required when working with native apps that are domainless. Using the server-side authentication flow is different from the pure client flow described in this article, but it is still part of OAuth2. You can read about it in greater detail at IETF-OAuth2.

Cross-Origin Resource Sharing (CORS)

The application, having successfully obtained the access token, is now capable of making signed HTTP requests to the provider’s API.

Accessing resources on one domain from another is known as cross-origin resource sharing, or CORS. Doing this is not as simple as accessing content from the same domain. Consideration must be taken to comply with the same-origin policy imposed by the browser. Such a policy applies conditions on scripts seeking to access content outside their current browser window’s domain name and port number. If the conditions are not met, the browser will throw a SecurityError exception.

XHR2

The new incarnation of the JavaScript API, XMLHttpRequest 2 (XHR2), supports the ability to use CORS. There are two parts to enabling this capability: in the client, the request must use the XHR2 interface, and the server must respond with an Access-Control-Allow-Origin header.

Client JavaScript

The following code illustrates an HTTP request in the client using XHR2:

	var xhr = new XMLHttpRequest();
	xhr.onload = function(e){
	  // contains the data
	  console.log(xhr.response);
	};
	xhr.open('GET', “http://anotherdomain.com”);
	xhr.send( null );

Access-Control HTTP Headers

The provider responds with an Access-Control-Allow-Origin header, satisfying the security policy in the user’s browser. For example, an HTTP request URL to the Windows Live API might create the following HTTP request and response:

	REQUEST
	GET https://apis.live.net/v5.0/me?access_token=EwA4Aq1DBAAUlbRWyAJjK5w968Ru3Cy
	...
	RESPONSE
	HTTP/1.1 200 OK
	Access-Control-Allow-Origin: *
	...
	{
	  "id": "ab56a3585e01b6db",
	  "name": "Drew Dodson",
	  "first_name": "Drew",
	  "last_name": "Dodson",
	  "link": "http://profile.live.com/cid-ab56a3585e01b6db/",
	  "gender": "male",
	  "locale": "en_GB",
	  "updated_time": "2012-11-05T07:11:20+0000"
	}

The browser security policy does not reject this CORS request because the provider allowed it by providing the HTTP header Access-Control-Allow-Origin: *. The asterisk (*) wildcard character indicates that all HTTP requests from any Web application are allowed to read the response data from this Web service.

All the social sign-in providers that I’ve looked at—for example, the Live Connect API and Facebook’s Graph API—do, of course, return this header in their responses.

XHR 2 Browser Support

Not all popular browsers support the now standard XMLHttpRequest with CORS headers. But they all support JSONP! JSONP simply gets round the security issues of Cross Domain by calling the API via the ‘src’ attribute of an embedded script tag.

All good APIs like SkyDrive API will ‘pad’ their Javascript Object response with a function call if the  “callback” parameter is provided in the URL.

First though we can feature detect by snooping for a property of the new XHR interface, like in the example below.

	If( “withCredentials” in new XMLHttpRequest() ){
	   // browser supports XHR2
	   // use the above method
	}
	else {
	   // Use JSONP, add an additional parameter to the URL saying return a callback
	   jQuery.getJSON(url + '&callback=?', onsuccess);
	}

The above code relies on jQuery’s getJSON method as fallback, and does a great job too.

REST: Representational State Transfer

Up to this point you’ve learned about authenticating users via the industry standard OAuth2 and cross-origin resource sharing with XMLHttpRequest and Access-Control headers. Next, I’ll cover what are essentially access to and interaction with servers and data sets on the Web.

In the code in the previous section, you saw a simple HTTP request and response. This is not unlike how HTML pages and their assets are served. However, when performed within an application to interoperate with Web services, we instead refer to this mechanism as a Representational State Transfer, or REST.

To sign a REST request with an access token, merely include the token within the query string parameters, as in this example:

https://apis.live.net/v5.0/me?access_token=EwA4Aq1DBAAUlbRWyAJjK5w968Ru3C

Connecting the Dot Coms

Now, with the technology and terminology covered so far, let’s crack on with a demonstration of an app that puts all this theory to the test. A short while ago, I created a photo-editing app called Graffiti (seeFigure 4). I felt it was a perfect contender for a social makeover so that users can load their photos from SkyDrive onto the canvas element and manipulate their online photos in the browser. You can see the demo at http://adodson.com/graffiti/ and also check out the code athttps://github.com/MrSwitch/graffiti/.

In the app, I’ve re-created some of the functions in the SkyDrive JavaScript SDK, such as WL.login, WL.filePicker and WL.api(). If you are not familiar with these methods, don’t worry, because I’ll explain what they do as we go.

Graffiti App with Photos from a SkyDrive Album
Figure 4.Graffiti App with Photos from a SkyDrive Album

Essentially, the new functionality includes these items:

  • getToken() Authenticates a user and stores the user’s access token for interacting with SkyDrive. This is akin to the WL.login() function.
  • httpRequest() For querying the SkyDrive API and getting results so that a navigation can be built, such as inFigure 4. This is akin to WL.api and WL.filePicker.

Let’s look at each in more detail.

getToken: Authenticate

The Graffiti’s authentication process is designed to work on demand. When a user’s action requires a signed API request, the authentication process begins.

In the SkyDrive API, the authentication handler is WL.login. The following code includes a custom function (getToken) that re-creates this method. It’s applied throughout the Graffiti app code and precedes any API requests, just like its counterpart. You can see a typical invocation illustrated here:

	btn.onclick = function(){
	  getToken("wl.skydrive", function(token){
	    // … do stuff, make an API call with the token
	  });
	}

The getToken function, shown in the following code, keeps track of the stored token and triggers the authentication flow when authorization is required. The received tokens are persisted for subsequent calls via the new HTML5 localStorage feature, which is available in modern browsers and allows developers to read and write persisted information (in this case our auth-token data) via key-value pairs.

Initially no tokens exist, so window.authCallback is assigned to the callback and is invoked when the access token is available. The window.open method creates a popup to the provider’s authorization page. Replace the text “WINDOWS_CLIENT_ID” with your app ID.

	function getToken(scope, callback){
	  // Do we already have credentials?
	  var token = localStorage.getItem("access_token"),
	    expires = localStorage.getItem("access_token_expires"),
	    scopes = localStorage.getItem("access_scopes") || '';
	  // Is this the first sign-in or has the token expired?
	  if(!(token&&(scopes.indexOf(scope)>-1)&&expires>((new Date()).getTime()/1000))){
	    // Save the callback for execution
	    window.authCallback = callback;
	    // else open the sign-in window
	    var win = window.open( 'https://oauth.live.com/authorize'+
	      '?client_id='+WINDOWS_CLIENT_ID+
	      '&scope='+scope+
	      '&state='+scope+
	      '&response_type=token'+
	      '&redirect_uri='+encodeURIComponent
	         (window.location.href.replace(//[^/]*?$/,'/redirect.html')),
	         'auth', 'width=500,height=550,resizeable') ;
	    return;
	  }
	  // otherwise let’s just execute the callback and return the current token.
	  callback(token);
	}

The getTokenfunction doesn’t work all on its own. After the user has consented, the pop-up browser window is returned to the redirect.html page with the new access token in the path. This HTML document is shown in the following code.

	<!DOCTYPE html>
	<script>
	  var access_token =
	    (window.location.hash||window.location.search).match(/access_token=([^&amp;]+)/);
	  var expires_in =
	    (window.location.hash||window.location.search).match(/expires_in=([^&amp;]+)/);
	  var state = (window.location.hash||window.location.search).match(/state=([^&amp;]+)/);
	  if(access_token){
	    // Save the first match
	    access_token = decodeURIComponent(access_token[1]);
	    expires_in = parseInt(expires_in[1],10) + ((new Date()).getTime()/1000);
	    state = state ? state[1] : null;
	    window.opener.saveToken( access_token, expires_in, state );
	    window.close();
	  }
	</script>

The full Web address to the redirect.html page contains the access token, state and expiry arguments. The script in the redirect.html page (shown earlier) extracts the arguments from the window.location.hash object using a regular expression before passing those back to the parent window object (window.opener) by calling a custom function, saveToken.Finally, this script executes window.close() to remove the pop-up window because it’s no longer needed. Here’s the code for saveToken:

	function saveToken(token, expires, state){
	  localStorage.setItem("access_token", token );
	  localStorage.setItem("access_token_expires", expires );
	  // Save the scopes
	  if((localStorage.getItem("access_scopes") || '').indexOf(state)===-1){
	  state += "," + localStorage.getItem("access_scopes") || '';
	  localStorage.setItem("access_scopes", state );
	  }
	  window.authCallback(token);
	}

The saveToken function stores the access_token credentials in localStorage. Finally, the callback saved at window.authCallback is triggered.

Pretty neat, huh? This lengthy code replaces the WL.login function of the Live Connect JavaScript API. The OAuth2 flow is a little intense and confusing at first, but I think that once you see it in action, you’ll appreciate it better.

Next, let’s re-create the way we query the SkyDrive API.

httpRequest: Query SkyDrive

The Graffiti app also requires that a user be able to query SkyDrive and pick a file to draw onto the canvas. The WL.filpicker method is the SkyDrive JavaScript API’s equivalent. However, the filePicker is a UI method, whereas a REST call to SkyDrive is typically handled by the WL.api method. (Figure 4 illustrates Graffiti’s filePicker-esq UI.)

I created two functions to separate the HTTP request process from the UI. In the following code, the function httpRequest emulates the WL.api(‘get’,..) method:

	function httpRequest(url, callback){
	  // IE10, FF, Chrome
	  if('withCredentials' in new XMLHttpRequest()){
	    var r = new XMLHttpRequest();
	    // xhr.responseType = "json";
	    // is not supported in any of the vendors yet.
	    r.onload = function(e){
	      callback(JSON.parse(r.responseText});
	    }
	    r.open("GET", url);
	    r.send( null );
	  }
	  else{
	    // Else add the callback on to the URL
	    jsonp(url+"&amp;callback=?", callback);
	  }
	}

The httpRequest function initially tests for the presence of XHR2 by detecting whether the property withCredentials exists within an instance of the XHR API. The fallback for browsers that don’t support XHR2 cross-origin capabilities is JSONP (check out jQuery.getJSON).

The xhr.onload handler converts the response string to a JavaScript Object and passes it as the first parameter to the callback handler. The httpRequest function is easily initiated.

	httpRequest(“https://apis.live.net/v5.0/me?access_token=EwA4Aq1DBAAUlbRWyAJjK5w968Ru3Cy”,
	  callback);

The function that calls httpRequest and subsequently puts the thumbnail images on the screen is createAlbumView, and it’s this method that re-creates the WL.filePicker-like functionality, for example:

	createAlbumView("me/albums", "SkyDrive Albums");

Here’s the code for createAlbumView:

	function createAlbumView(path, name){
	  // Get access_token from OAuth2
	  getToken("wl.skydrive", function(token){
	    // Make httpRequest
	    // Retrieve all items from path defined in arguments
	    httpRequest('https://apis.live.net/v5.0/'+path+'?access_token='+token, function(r){
	      // Create container
	      // …
	      // Loop through the results
	      for(var i=0;i&lt;r.data.length;i++){
	        // Create thumbnail and insert into container
	        createThumbnail(r.data[i], container);
	      }
	    });
	  });
	}

When provided with a path name of an album (such as “me/albums”), createAlbumView populates the navigation screen with the items found at that address. While the initial list of albums is available at “me/albums”, createAlbumView is recursive. Items it finds that are albums form the new path, and hence make the whole of SkyDrive navigable. The following code shows how the item exposes its type and the different way it is handled by the app:

	function thumbnail_click (item){
	  if( item.type === "photo" ){
	    applyRemoteDataUrlToCanvas( item.source );
	  }
	  else if(item.type === "album"){
	    createAlbumView(item.id+'/files', item.name);
	  }
	}

Items that are Images are returned straight into Graffiti’s canvas element.

Signing Out

This article has aimed to demystify magic that is packaged up in the proprietary JavaScript libraries. You’ve seen three functions that imitate those from the SkyDrive’s JavaScript API.

  • getToken emulates WL.login
  • httpRequest emulates WL.api(‘get’,…)
  • createAlbumView emulates WL.filePicker()

Using the SkyDrive JavaScript SDK was just an example. Facebook Connect JavaScript SDK and others work in a very similar way. Perhaps now you can see these libraries for what they are; a collection of adoptive technologies and clever tricks.

This story isn’t over. There are more ways that XMLHttpRequest can be leveraged. In Part 2, I’ll introduce those and illustrate them by extending the Graffiti app to edit albums, uploading the Graffiti art work to SkyDrive and sharing information on the users’ activity feed. Magnifico!

Until then, if you would like to support a project that aggregates many social APIs on the Web, please take a look at http://adodson.com/hello.js/ and share your thoughts on the GitHub page.

Thanks for reading.

References

This article is part of the HTML5 tech series from the Internet Explorer team. Try-out the concepts in this article with 3 months of free BrowserStack cross-browser testing @ http://modern.IE.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.alexfraundorf.com Alex Fraundorf

    Andrew,
    Excellent article. Thank you very much for sharing it along with your HelloJS library.
    I am looking forward to integrating it in some future projects.
    Alex