ASP.NET FaceBook Login UserControl

With more than 500 million active users and 50% of active users logging on to Facebook in any given day, it makes sense to use Facebook's free controls to allow your site's users to log in with their facebook credentials. In addition, if you remember the recent Sony fiasco, it may be comforting to know that with Facebook login you do not need to store any users' password information - they supply that when they log in via Facebook.

I've done some research into the use of the Facebook FBML API and have tried several implementations of facebook log ins. One, that I found on codeplex.com seemed to be simple enough but it had some deficiencies that needed to be corrected, and would be much better suited as a UserControl that you could just drop onto any page. The original example used two separate pages laden with code, one for the login and the other for the required callback. By consolidating everything into an ASCX UserControl, I've been able to remove the need for anything but a single page. Drop the control on the page, have one simple JSON utility class in your project, and you're done.

One of the problems I found, which was not addressed at all in the original code, is that Internet Explorer is very finicky in recognizing the "fb" namespace. This can be solved by modifying the page's HTML Declaration as follows:

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

Note the addition of the xmlns:fb atttribute, which makes IE correctly recognize the Facebook markup language namespace.

With these fixes in place we are not quite all the way there. We still need to set up a test app that will work with all three major browsers - IE, Firefox, and Chrome.

You have to go to your FaceBook account, and choose "Developers" from the very bottom of the page:

http://developers.facebook.com/?ref=pf

Choose "My Apps" from the top menu.  Choose "Set up new App". Your configuration should look like the following in the "Web Site" section:


Note that Internet Explorer will not process callbacks correctly unless you use IIS for your local project and enter 127.0.0.1 plus the Virtual Directory name (not "localhost"). Note also that for the testing app, I've left the domain blank. This setup will now allow you to test locally with all your favorite browsers, and Internet Explorer will finally cooperate.

In your Solution, you use IIS and conform to the same usage with "127.0.0.1" to make IE happy with callbacks:



To use the control, we have a login page with an instance of the control:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="FacebookLogin.Login" %>
<%@ Register src="FBLogin.ascx" tagname="FBLogin" tagprefix="uc1" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>    
        <uc1:FBLogin ID="FBLogin1" runat="server" />    
    </div>
    </form>
</body>
</html>

The Control markup is reasonably simple:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="FBLogin.ascx.cs" Inherits="FacebookLogin.FBLogin" %>
<fb:login-button perms="email" onlogin="document.location.href = 'Login.aspx'">Login with Facebook</fb:login-button>
<%--
To make this control work you must  put your facebook-app-key  in the "facebookAppKey" element in AppSettings in web.config  
    --%>
    <style>
     .Yellowborder
   {
   BORDER-RIGHT: #ffcc00 1px solid; BORDER-TOP: #ffcc00 1px solid;
   FONT-WEIGHT: normal; FONT-SIZE: 11px; BORDER-LEFT: #ffcc00 1px solid;
     COLOR: #000000; BORDER-BOTTOM: #ffcc00 1px solid;
      FONT-FAMILY: Verdana, Helvetica, sans-serif;
       BACKGROUND-COLOR: white;  width:210px; overflow:hidden;
   }     
    </style>
    
<div id="fb-root"></div>    
<script type="text/javascript">
     window.fbAsyncInit = function () {
         FB.init({ appId: '<%= FacebookLogin.FBLogin.FaceBookAppKey %>', status: true, cookie: true, xfbml: true });
     };
     (function () {
         var e = document.createElement('script'); e.async = true;
         e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
         document.getElementById('fb-root').appendChild(e);
     } ());

</script>
      <asp:label runat="server" id=lblMessage text="" CssClass="Yellowborder"></asp:label>


We have a div "fb-root" that holds the injected async script from Facebook. We have a script tag that performs the Facebook fbAsyncInit function and injects the script. Note also that the onlogin="document.location.href = 'Login.aspx'" in the control markup is how we tell facebook where to send the response. In this case, it's the same page that the control is on.

In the codebehind is where all the cool stuff happens:

using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace FacebookLogin
{
    public partial class FBLogin : System.Web.UI.UserControl
    {
         public static string FaceBookAppKey = ConfigurationManager.AppSettings["facebookAppKey"];

        protected void Page_Load(object sender, EventArgs e)
         {
             if(String.IsNullOrEmpty( FaceBookAppKey ))
                throw new InvalidOperationException("You must have a valid Facebook App Key in AppSettings element \"facebookAppKey\"");

             if (Request.Cookies["fbs_" + FaceBookAppKey] == null)
            {
                lblMessage.Text = "Not logged in.";
                 return; // No cookie returned from Facebook!!
            }

            string cookie = Request.Cookies["fbs_" + FaceBookAppKey].Value;
                cookie = cookie.Replace("\"", ""); //fix Facebook bug...
                NameValueCollection facebookValues = HttpUtility.ParseQueryString(cookie);

                  // send an http-request to facebook using the token from the cookie
                 //and parse the JSON response
                string json = GetFacebookUserJSON(facebookValues["uid"], facebookValues["access_token"]);
                Hashtable jsonHash = (Hashtable) JSON.JsonDecode(json);

                 //ok, let's actually read some data from FB response
                string facebookName = jsonHash["name"] as string;
                string firstName = jsonHash["first_name"] as string;
                string lastName = jsonHash["last_name"] as string;
                string facebookId = jsonHash["id"] as string;
                string email = jsonHash["email"] as string;
                     //We explicitly requested email (see fb-button)
            lblMessage.Text = "Welcome, " + firstName + " " + lastName + " [" + email + "]";

             // Can store name, email etc. in db here, get user profile, store info in Session, etc.
        }

         /// <summary>
        /// sends http-request to Facebook and returns the response string
        /// </summary>
        private static string GetFacebookUserJSON(string userid, string access_token)
        {
            string url = string.Format("https://graph.facebook.com/{0}?access_token={1}&fields=email,first_name,last_name", userid, access_token);
            WebClient wc = new WebClient();
            string s = wc.DownloadString(url);
             wc.Dispose();
             return s;
        }
    }
}

From top to bottom, we make sure the user has an AppKey in their web.config. Then we look for the facebook cookie names for our AppKey. If there is no cookie, the user is not logged in so we do nothing. If the cookie is present, that means Facebook has returned user information from a log-in. We fix up the cookie, get the NameValueCollection of all the facebook values, and get the Facebook JSON string with a WebClient call. The JSON operations are all done with the included JSON utility class. You could also do this with JSON.NET, but since all this code worked fine in the original offering, I decided there was no need to change it.

Finally, we get the user's facebook name, first name, lastname, Facebook ID, and email and display some user info in the label to show the user that they are logged in. It is at this point where you would store information in your database, do a profile lookup, or store information in session (or you could do a lookup and even redirect to a required "profile page" to capture addition user information and store it in your user tables in your database). You could even add FormsAuthentication by calling the FormsAuthentication SetAuthTicket and adding it to the Response.Cookies collection, and redirecting to a "logged in" page.

Once you are happy with your local testing, you would go back to your Facebook "My Apps" section and either create a new app with the domain of your deployed solution, or modify your "test" app with a new public URL and domain, and change the App ID in web.config if appropriate. To add this all to an existing web application, you only need add the UserControl with it's codebehind class, the JSON.cs utility class, and add the required appSettings element with your Application ID to your web.config file. Drop the control on a MasterPage or any other page where you want users to be able to log in via Facebook, and you're on your way to Facebook nirvana!

You can download the complete Visual Studio 2010 solution here. Note that this implementation is designed to work under IIS using the 127.0.0.1 address described above. If you choose to test locally using IIS Express or the Visual Studio WebDev server, you do so at your own peril. And, don't forget to set up an app at facebook and get your very own Application ID -- you'll need to fill it in in web.config AppSettings in order for this demo to work.

By Peter Bromberg   Popularity  (8534 Views)