This is an archive of blog posts from a "long time ago". If you find this useful, let me know and I'll revive it.

code blog foo - tag line bar

Facebook Platform's OAuth 2.0 Protocol and ASP.NET MVC

The Facebook Platform allows external websites the ability to leverage user accounts that exist in Facebook...click here for an explanation of single sign-on.

What's Covered

After figuring out the protocol and what Facebook expected and returned, the implementation of the protocol in ASP.NET MVC was a piece of cake (for the most part...). This post will show you:

Registering Your Website With Facebook

Here are the steps for setting up for your app to communicate with Facebook's Single Sign-on Protocol. First thing you have to do is register with Facebook (as a person...company pages cannot have apps...).

After you yourself have registered and have exposed yourself to the Facebook conspiracy (I kid, I kid), you then have to add the Developer App to your profile.

After you have authorized the Developer App, you can add your website (click the "Set Up New Application" button and set up your app as follows).

Things you have to set up.

Basic Information (make sure to make note of your Application ID, API Key, and your Secret Key):

Authentication Information:

Connect Information: (your Connect URL is what Facebook will use to ensure a connection is coming in from you and not some 3rd party)

Also, make sure you keep your connect url as ALL LOWERCASE. The connect url is case sensitive so any reference to this connect url must be IDENTICAL to what you've provided to Facebook.

Authenticating With Facebook

Now comes the fun part. Facebook has provided documentation of the [authentication process] (which wouldn't hurt to read). Here is a summary of what you have to do to have a successful "handshake" with Facebook:

  1. Place a "Sign In With Facebook" link on your authentication page.
  2. The user will click the link which will redirect them to Facebook for authentication.
  3. After authenticating, the user will be required to Authorize your App.
  4. After authorization, Facebook will redirect back to your website, with a token representing the authenticated Facebook session.
  5. Taking this token, you will need to request an access token from Facebook's Graph API Service.
  6. If token exchange is successful, you will be given an access token that you can then use with the Graph API.
  7. Using the Graph API, you then can query for the user's holy grail...the UserId...
  8. You can save this UserId to your database and "go to town" relating "stuff" to it!

Here is some ascii art to explain:

     +--------+                                  +---------------+
     |        |--(A)-- Authorization Request --->|   Resource    |
     |        |                                  |     Owner     |
     |        |<-(B)------ Access Grant ---------|               |
     |        |                                  +---------------+
     |        |
     |        |         Client Credentials &     +---------------+
     |        |--(C)------ Access Grant -------->| Authorization |
     | Client |                                  |     Server    |
     |        |<-(D)------ Access Token ---------|               |
     |        |      (w/ Optional Refresh Token) +---------------+
     |        |
     |        |                                  +---------------+
     |        |--(E)------ Access Token -------->|    Resource   |
     |        |                                  |     Server    |
     |        |<-(F)---- Protected Resource -----|               |
     +--------+                                  +---------------+

The Code

Alright. You've registered your website with Facebook. And you understand (at least at a high level) what is involved with performing a successful handshake. Now it's time to implement that handshake in ASP.NET MVC.

Step 1: Create a Controller Action that Redirects to Facebook

Per the Facebook documentation, you have to redirect to: https://graph.facebook.com/oauth/authorize to get things started. There are 3 query string parameters you have to include with this URL:

Here is the controller action for redirecting to Facebook:

[HttpGet]
[ActionName("FacebookLogin")]
public ActionResult FacebookLogin()
{
     //redirect to https://graph.facebook.com/oauth/authorize giving Facebook my application id, the request type and the redirect url
     return new RedirectResult("https://graph.facebook.com/oauth/authorize? type=web_server& client_id=114756055226487& redirect_uri=http://www.examplewebsite.com/facebook/handshake/");
}

Here is the html for the action link.

<% string facebookLoginUrl = Url.RouteUrl(new { controller = "Authentication", action = "FacebookLogin" }); %>
<a href="<%= facebookLoginUrl %>">log in with Facebook</a>

Step 2: Handle the redirect from Facebook

The redirect will take the user to a Facebook login screen. If the login is successful (and if the user authorizes your application), Facebook will redirect back to the url that was provided in the redirect_uri query string parameter with an access code for the Facebook session. For this example the Facebook redirect url to something like:

http://www.examplewebsite.com/facebook/handshake/?code=2.DQUGad7_kFVGqKTeGUqQTQ__.3600.1273809600-1756053625|dil1rmAUjgbViM_GQutw-PEgPIg.

Alright. Let's keep the end goal in mind and work backwards. What we're trying to get from Facebook is a UserId. The UserId will always be the same for a given user. And of course, if we have the UserId, we can start relating stuff to it (such as profile information). Here is the workflow (working backwards) for getting the UserId:

The workflow make sense? Good. Here is the code:

//this is the statically typed representation of the JSON object that will get returned from: https://graph.facebook.com/me
public class FacebookUser
{
    public long id { get; set; } //yes. the user id is of type long...dont use int
    public string first_name { get; set; }
    public string last_name { get; set; }
    public string name { get; set; }
    public string email { get; set; }

}

//this controller action will be called when Facebook redirects
[HttpGet]
[ActionName("handshake")]
public ActionResult Handshake(string code)
{
         //after authentication, Facebook will redirect to this controller action with a QueryString parameter called "code" (this is Facebook's Session key)

         //example uri: http://www.examplewebsite.com/facebook/handshake/?code=2.DQUGad7_kFVGqKTeGUqQTQ__.3600.1273809600-1756053625|dil1rmAUjgbViM_GQutw-PEgPIg.

        //this is your Facebook App ID
        string clientId = "114756055226487";
        
        //this is your Secret Key
        string clientSecret = "YOURSECRETKEY";

        //we have to request an access token from the following Uri
        string url = "https://graph.facebook.com/oauth/access_token?client_id={0}&redirect_uri={1}&client_secret={2}&code={3}";

        //your redirect uri must be EXACTLY the same Uri that caused the initial authentication handshake
        string redirectUri = "http://www.examplewebsite.com/facebook/handshake/"; 

        //Create a webrequest to perform the request against the Uri
        WebRequest request = WebRequest.Create(string.Format(url, clientId, redirectUri, clientSecret, code));
        
        //read out the response as a utf-8 encoding and parse out the access_token
        WebResponse response = request.GetResponse();
        Stream stream = response.GetResponseStream();
        Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
        StreamReader streamReader = new StreamReader(stream, encode);
        string accessToken = streamReader.ReadToEnd().Replace("access_token=", "");
        streamReader.Close();
        response.Close();

        //set the access token to some session variable so it can be used through out the session
        Session["FacebookAccessToken"] = accessToken;

        //now that we have an access token, query the Graph Api for the JSON representation of the User
        string url = "https://graph.facebook.com/me?access_token={0}";

        //create the request to https://graph.facebook.com/me
        request = WebRequest.Create(string.Format(url, accessToken));

        //Get the response
        response = request.GetResponse();

        //Get the response stream
        Stream stream = response.GetResponseStream();

        //Take our statically typed representation of the JSON User and deserialize the response stream
        //using the DataContractJsonSerializer
        DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(FacebookUser));
        FacebookUser user = new FacebookUser();
        user = dataContractJsonSerializer.ReadObject(stream) as FacebookUser;

        //close the stream
        response.Close();

        //capture the UserId
        Session["FacebookUserId"] = user.id;

        //Set the forms authentication auth cookie
        FormsAuthentication.SetAuthCookie(user.email, false);

        //redirect to home page so that user can start using your application      
        return RedirectToAction("Home", "User");
}

Whew. Great. User is authenticated. Now you can use any resource provided by [Facebook's Graph Api]. All entities returned for the Graph Api are JSON objects...so all you have to do is create a static representation of the JSON entity in C# and use the DataContractJsonSerializer to deserialize the response stream (as demonstrated above).

Logging Out of Facebook Programmatically

OAuth is a great authentication protocol.....too bad Facebook hasn't provided any restful way to logout and purge any Facebook session cookies...so we have two options.

  1. Logout via javascript using Facebooks Javascript SDK.
  2. Redirect to Facebook's logout page (this approach is not documented and Facebook may change it without giving the developer community notice).

Logging Out with the Javascript Facebook SDK

Here is how you log out of Facebook. Remember, even if you do FormsAuthentication.Logout(), the user is still logged into Facebook (so you must sign out of Facebook also to ensure user privacy).

Since this is Javascript based, you need to have a controller action that just redirects to a view page:

[ActionName("FacebookLogout")]
[HttpGet]
public ActionResult FacebookLogout()
{
    FormsAuthentication.SignOut();
    return View();
}

This is what the view code will look like (it's very....hackish...unfortunately...but each piece of html is needed....maybe one day Facebook will stream line this...):

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    FacebookLogout
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <!-- this is a message to show while the javascript executes -->
    Logging you out of Facebook....

    <!-- reference to google's jquery ui library -->
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>

    <!-- this div element is required...I have no idea why...but when i remove it, the log out doesn't occur -->
    <div id="fb-root">
    </div>

    <!-- pull down Facebook's Javascript SDK -->
    <script src="http://connect.facebook.net/en_US/all.js"></script>

    <!-- execute the following script immediately when the page loads -->
    <script>
        // initialize the library with your Facebook API key
        FB.init({ apiKey: 'f3ff8e4ed6becc81176d0b2517c9c44c' });

        // fetch the status so that we can log out,  once the login status is fetched, call handleSessionResponse
        FB.getLoginStatus(handleSessionResponse);

        // handle a session response from any of the auth related calls
        function handleSessionResponse(response) {
            // if we dont have a session (which means the user has been logged out, redirect the user)
            if (!response.session) {
                window.location = "http://www.examplewebsite.com/Authentication/Login/";
                return;
            }

            //if we do have a non-null response.session, call FB.logout(),
            //the JS method will log the user out
            //of Facebook and remove any authorization cookies
            FB.logout(handleSessionResponse);
        }
    </script>

</asp:Content>

Logging Out of Facebook Using a Redirect

Alternately (if you dont like the Javascript approach). You can log out of Facebook by redirecting to their logout.php page. Please keep in mind that this approach isn't documented. I used Fiddler2 to see how the Javascript SDK did it's log out magic....so just keep in mind that Facebook can change this anytime without prior notice.

To log out, just redirect to logout.php with your Api Key and the Original Session Key that was provided in the first handshake...the session code/key...not the Graph API Access Token:

[ActionName("FacebookLogout")]
[HttpGet]
public ActionResult FacebookLogout()
{
    string url = "http://www.facebook.com/logout.php?api_key=f3ff8e4ed6becc81176d0b2517c9c44c&session_key={0}";
    
    //the Facebook session token is passed in, this was the token that was given to you immediately after Facebook authenticated the user
    return new RedirectResult(string.Format(url, Session["FacebookSessionToken"]));
}

Summary

That's really all there is to Facebook's Single Sign-On. If you have any questions, don't hesitate to email me (my email address is located at the bottom of the "about" page).


Written: 5/8/2010