ASP.NET FaceBook Login UserControl

By Peter Bromberg

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.

Popularity  (3784 Views)
Picture
Biography - Peter Bromberg
Peter Bromberg is a C# MVP, MCP, and .NET expert who has worked in banking, financial and telephony for over 20 years. Pete focuses exclusively on the .NET Platform, and currently develops SOA and other .NET applications for a Fortune 500 clientele. Peter enjoys producing digital photo collage with Maya,playing jazz flute, the beach, and fine wines. You can view Peter's UnBlog and IttyUrl sites. Follow Microsoft MVP
Create New Account
Article Discussion: ASP.NET FaceBook Login UserControl
Peter Bromberg posted at Tuesday, May 10, 2011 11:34 AM
Mitch replied to Peter Bromberg at Wednesday, May 11, 2011 4:52 PM
Great article! I got it running no problem but when I try to put a redirect or transfer in place to send user to another page, it doesn't go--just goes back to same page. Any thoughts?
Peter Bromberg replied to Mitch at Wednesday, May 11, 2011 4:52 PM
where are you putting your redirect or transfer? Can you post some sample code?

Mitch replied to Peter Bromberg at Wednesday, May 11, 2011 4:52 PM
I tried placing both a redirect and a transfer in the control code-behind right before your comment:

// Can store name, email etc...
Peter Bromberg replied to Mitch at Wednesday, May 11, 2011 4:52 PM
Have you tried putting a breakpoint on the line where you redirect? Does the debugger break on it?
Mitch replied to Peter Bromberg at Wednesday, May 11, 2011 4:52 PM
Couldn't seem to get breakpoint to work...

Here's your code from the Page_Load of the control with the redirect added. I guess I'm just wondering where you would put any code to execute once you're logged in?

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.
            Response.Redirect("http://www.yahoo.com");

Peter Bromberg replied to Mitch at Wednesday, May 11, 2011 4:52 PM
Do you see the comment near the end, 

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

That's where you would put code to execute if the person is logged in. Example:

if(email!=null)
{
// person is indeed logged in because they allowed us to capture their email

// save the person's email in our database here

Response.Redirect("LoggedInWelcomePage.aspx");

}
Mitch replied to Peter Bromberg at Wednesday, May 11, 2011 4:52 PM
Thanks for sticking with me on this...

I had the same thing, but this code never executes for some reason. The process just ends after updating the label text with name and email...
Peter Bromberg replied to Mitch at Wednesday, May 11, 2011 4:52 PM
Beats me. I do this all the time and it works just fine. Are you sure your code isn't throwing an exception? Try wrapping it all in a try / catch block and put an unhandled exception handler in your Application_Error handler in global.asax.cs

Mitch replied to Peter Bromberg at Wednesday, May 11, 2011 4:52 PM
Hey I figured it out! 

For anyone else struggling with this code, you need to specify the URL in the onlogin FB code at the top of the control:

<fb:login-button perms="email" onlogin="document.location.href = 'Welcome.aspx'">Login with Facebook</fb:login-button>
Peter Bromberg replied to Mitch at Wednesday, May 11, 2011 4:52 PM
I think the article states that quite clearly. But glad you got it fixed.
Jonathan replied to Peter Bromberg at Wednesday, May 11, 2011 4:52 PM
Hi Peter,

Thanks for the very clear article & nice code. It even ran first time- which is a minor miracle seeing come of the code coming from certain social networking sites......
My only problem is that when I deploy to my trusty 'tinterweb server, I get

405 - HTTP verb used to access this page is not allowed.

The page you are looking for cannot be displayed because an invalid method (HTTP verb) was used to attempt access.

Google reveals this to be either an update not applied to the server (but that was from 2008 or something), or due to code not implementing the GET properly following on from FB 'closing down' non iframe methods. I think that was around mid may this month- could you confirm that the code still works? I can't see an iframe anywhere- but I am going code blind now after 2 weeks of trying to get oauth to work!
Kindest & cheers, J.
Jonathan replied to Jonathan at Wednesday, May 11, 2011 4:52 PM

Oops- sorry- it suddenly started working! I think it may have been a FB propagation error.

Cheers, J.
selva kumar replied to Mitch at Wednesday, May 11, 2011 4:52 PM
please help me urgent sir i have developed in a website i have done but i have upload in my localhost server,the lohal host only view the design but database not access how i create local host password and username and server ip address:

my documents:
1.asp.net
2.C#
3.ms access database

please help for me urgent
selva kumar replied to selva kumar at Wednesday, May 11, 2011 4:52 PM
i want ms access connection string , next how i create username and password in ms access document please clear my error sir urgently