How to implementing log in to site with Facebook account in ASP.NET MVC

I have decided to enable users that already have a Facebook account (who hasn’t ?!) to log on my site with their Facebook credentials. Process has shown to be pretty easy but It took me some time to figure out few things. I have decided to implement so called ‘Single Sign-On’ and I pretty much followed guildlines from http://developers.facebook.com/docs/guides/web. So, not to loose your precious time, let us start from the beginning.

1. Register Your application/web site

First You need to have a Facebook account. Then You visit http://developers.facebook.com/setup/ to register Your web site. After filling out the form:

alt-txt

and pressing ‘Create application’ button You will receive Application ID and Secret numbers. As You can see from filled form Your web site can stay on localhost - it would work great anyway. You will get Your info in similar form as here:

alt-txt

2. Place login button

Now, You are ready to place Facebook login button on Your web site. I have placed it next to standard login button in LoginUserControl.ascx (Partial View):

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%
    if (Request.IsAuthenticated) {
%>
        Welcome <b><%: Page.User.Identity.Name %></b>!
        [ <%: Html.ActionLink("Log Off", "LogOff", "Account") %> ]
<%
    }
    else {
%>
        <div class="login-with-facebook">
            <fb:login-button ></fb:login-button>
        </div>
        [ <%: Html.ActionLink("Log On", "LogOn", "Account") %> ]
<%
    }

%>

3. Add Facebook namespace

As You can see Facebook uses it’s own html namespace. Due to that I have actually lost some time - as I couldn’t figure out why my log in button does not show up in IE…. You need to add fb namespace to Your site (preferably in Site.master):

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml"> 

This solves the issue.

4. Add JavaScript code

Now some js code is needed, place it at the bottom of the web site:

<div id='fb-root'></div>
<script>
window.fbAsyncInit = function () {
            FB.init({ appId: '<%= Settings.FacebookApplicationId %>', status: true, cookie: true,
                xfbml: true
            });
            FB.Event.subscribe('auth.sessionChange', function (response) {
                if (response.session) {
                    window.location = '<%= Url.Action("LoginWithFacebook", "account", new { returnUrl = Request.Url }) %>';
                } else {
                    window.location = '<%= Url.Action("logout", "account", new { returnUrl = Request.Url }) %>';
                }
            });
        };
        (function () {
            var e = document.createElement('script');
            e.type = 'text/javascript';
            e.src = document.location.protocol +
          '//connect.facebook.net/en_US/all.js';
            e.async = true;
            document.getElementById('fb-root').appendChild(e);
        } ());
</script>

Make sure You have placed ‘fb-root’ element or You manually added script reference to ‘connect.facebook.net/en_US/all.js’ script. Replace settings.facebookapplicationid with the one You received after registering app in facebook (Previous step) Previusly placed button () is initialized thanks to FB.init function. Event ‘auth.SessionChange’ is handled in such a manner that when user registers it redirects to /account/loginwithfacebook and when user log out it goes to /account/logout. As simple as that.

5. Server side code

Now we need to handle those actions. I will just describe log in, as log out is self explanatory.

[AcceptVerbs(HttpVerbs.Get)]
        public ActionResult LoginWithFacebook(string returnUrl)
        {
            var cookie = FacebookCookie.GetCookie(Settings.FacebookApplicationId, Settings.FacebookSecretKey);

            if (cookie != null) {

                WebClient client = new WebClient();

                var result = client.DownloadString("https://graph.facebook.com/me?access_token=" + cookie.AccessToken);

                JavaScriptSerializer js = new JavaScriptSerializer();

                var user = js.Deserialize<FacebookUserHelper>(result);

                var contextUser = UserService.CreateUserFromFacebook(user);

                FormsAuth.SetAuthCookie(contextUser.Name, false);

            }

            if (string.IsNullOrWhiteSpace(returnUrl))
                return RedirectToHomePage();
            else
                return Redirect(returnUrl);
        }

After a call to LoginWithFacebook is made code checks if there is a specific Facebook cookie and validates it (‘Settings.FacebookApplicationId’ and ‘Settings.FacebookSecretKey’ are the one obtained during registration period). If everything is okay we call ‘https://graph.facebook.com/me' passing authentication token received within a cookie to obtain user’s data, like name or email. Service returns Json data so we use JavaScriptSerializer to deserialize it. Next we just set authentication cookie. Validation of a cookie may look like:

         public bool ValidateFacebookCookie(string orgCookieValue, string facebookAppSecret) {
            var args = HttpUtility.ParseQueryString(orgCookieValue.Replace("\"", string.Empty));
            var cookieCheck = new StringBuilder();
            foreach (var key in args.AllKeys.Where(k => k != "sig")) {
                cookieCheck.AppendFormat("{0}={1}", key, args[key]);
            }
            cookieCheck.Append(facebookAppSecret);
            var md5Hash = cookieCheck.ToMD5Hash(Encoding.ASCII);
            StringBuilder signature = new StringBuilder();
            for (int i = 0; i < md5Hash.Length; i++) {
                signature.Append(md5Hash[i].ToString("X2"));
            }
            return string.Equals(args["sig"], signature.ToString(), StringComparison.InvariantCultureIgnoreCase);
        }

You can obtain facebok cookie value with:

       public static string GetFacebookCookieValue(string facebookAppId, string facebookAppSecret) {
            string name = string.Format("fbs_{0}", facebookAppId);
            if (!HttpContext.Current.Request.Cookies.AllKeys.Contains(name)) {
                return null;
            }
            return HttpContext.Current.Request.Cookies[name].Value;
        }

In case You need a complete code for Facebook cookie validation, You may find it using Googling or write me an email and I will send to anyone. You may check how it works by visiting CyprusQA web site. That is it, happy coding!

comments powered by Disqus