A critical vulnerability was discovered in React Server Components (Next.js). Our systems remain protected but we advise to update packages to newest version. Learn More

Magnus Rahl
Feb 15, 2011
  5922
(0 votes)

Single Sign-on using Facebook javascript library, Part 1

The RelatePlus templates come with a ready-to-use Open ID integration, but I find users are more likely to have a Facebook account which they use to log in to various web applications outside of Facebook. Facebook offers several different integration points depending on how deep integration you are looking for. The most simple cases just use javascript and ready-to-use “controls” to enable the infamous “like” button and its cousins.

In this article I describe how you can use the relatively simple javascript approach together with an http module to enable users to sign in to your site with their facebook account, while still creating a asp.net membership account and logging the user in locally in to your site.

Step 1: The client javascript and login button

This isn’t specific to neither EPiServer nor ASP.NET so I won’t go over the details. There is some documentation over at the Facebook API documentation pages. To quckly sum it up:

First I generated an application ID in Facebook. You must have this to use the Facebook API. Then I created a User Control containing the client script and stuck this control in the master page, first inside the body tag. So it loads on every page. The markup is pretty much copy-and paste from the Facebook API docs:

   1: <div id="fb-root"></div>
   2: <script type='text/javascript'>
   1:  
   2:     window.fbAsyncInit = function () {
   3:         FB.init(
   4:         {
   5:             appId: 'APP_ID_GOES_HERE',
   6:             status: true,
   7:             cookie: true,
   8:             xfbml: true
   9:         });
  10:         FB.Event.subscribe('auth.sessionChange', function (response) {
  11:             if (response.session) {
  12:                 // The user has logged in, reload to integrate user
  13:                 window.location = window.location;
  14:  
  15:             } else {
  16:                 // The user has logged out, and the cookie has been cleared
  17:             }
  18:         });
  19:     };
  20:     (function () {
  21:         var divId = 'fb-root';
  22:         var e = document.createElement('script'); e.async = true;
  23:         e.src = document.location.protocol +
  24:         '//connect.facebook.net/sv_SE/all.js';
  25:         document.getElementById(divId).appendChild(e);
  26:     } ());
</script>

Finally I used XFBML to add a Facebook login button on my login page, complete with the requested extended permission to read the user’s email:

   1: <fb:login-button perms="email" />

Simple as that. The users can now click the login button, grant my application access to their basic account details and email, and we’re go for the client side. Using the javascript API I can now call Facebook services using the user’s access token to pull or push Facebook data and to load Facebook controls. But this far, the EPiServer site has no clue about the user.

Step 2: Integrate the user using a http module

By creating an http module which run on every (managed) request, I can use the great Facebook C# SDK to pick up the Facebook session whenever a user is logged in using the client script. Using the session data I check if there is a local membership user corresponding to the logged in Facebook user. If not I create one using the Facebook user ID as name. For future extendability I also save the Facebook user ID in the profile, so I add a field for this to my web.config profile section:

   1: <profile enabled="true" defaultProvider="SqlProfile" automaticSaveEnabled="true">
   2:   <properties>
   3:     ...
   4:     <add name="FacebookId" type="System.Int64"/>
   5:   </properties>
   6:   ...
   7: </profile>

I also save the user’s email address (requested in the extended permission), first and last name. Finally, I set the forms auth cookie to log the user in and refresh the page for the login to take effect. Here’s the code for the http module:

   1: using System;
   2: using System.Web;
   3: using System.Web.Security;
   4: using System.Web.Profile;
   5: using EPiServer.Personalization;
   6:  
   7: namespace ACME.Web.Templates.HttpModules
   8: {
   9:     public class FacebookIntegrationModule : IHttpModule
  10:     {
  11:         public void Dispose() { }
  12:  
  13:         public void Init(HttpApplication context)
  14:         {
  15:             context.AuthenticateRequest += AuthenticateRequest;
  16:         }
  17:  
  18:         private void AuthenticateRequest(object sender, EventArgs e)
  19:         {
  20:             var application = sender as HttpApplication;
  21:  
  22:             if (application == null || application.Request.IsAuthenticated) return;
  23:  

24: var facebookApp = new Facebook.FacebookApp();// Construct fbsession from cookie

25: UpdateUser(application, facebookApp;

  26:         }
  27:  
  28:         private bool UpdateUser(HttpApplication application, Facebook.FacebookApp facebookApp)
  29:         {
  30:             if (facebookApp.Session == null || facebookApp.Session.UserId < 1) return false;
  31:  
  32:             // Read user data
  33:             var userId = facebookApp.Session.UserId;
  34:             var username = userId.ToString();
  35:             dynamic me = facebookApp.Get("me");
  36:             string email = me.email;
  37:             string firstname = me.first_name;
  38:             string lastname = me.last_name;
  39:  
  40:             // Get user, create if not previously exiting
  41:             var user = Membership.GetUser(username);
  42:             if (user == null)
  43:             {
  44:                 // Password won't be used with FB login, so just set a new random one
  45:                 var password = Membership.GeneratePassword(10, 0);
  46:                 user = Membership.CreateUser(username, password, email ?? username + "@acme.web");
  47:             }
  48:  
  49:             var profile = EPiServerProfile.Get(username);
  50:  
  51:             // Make user the ID is set in a profile property
  52:             var savedId = GetFacebookId(profile);
  53:             if (savedId != facebookApp.Session.UserId)
  54:             {
  55:                 SetFacebookId(profile, userId);
  56:             }
  57:  
  58:             if (email != null)
  59:             {
  60:                 // UPdate email both in membership and profile
  61:                 if (user.Email != email)
  62:                 {
  63:                     user.Email = email;
  64:                     Membership.UpdateUser(user);
  65:                 }
  66:  
  67:                 if (profile.Email != email)
  68:                 {
  69:                     profile.Email = email;
  70:                 }
  71:             }
  72:  
  73:             if (profile.FirstName != firstname)
  74:             {
  75:                 profile.FirstName = firstname;
  76:             }
  77:  
  78:             if (profile.LastName != lastname)
  79:             {
  80:                 profile.LastName = lastname;
  81:             }
  82:  
  83:             profile.Save();
  84:  
  85:             FormsAuthentication.SetAuthCookie(user.UserName, false);
  86:  
  87:             return true;
  88:         }
  89:  
  90:         private static long GetFacebookId(ProfileBase profile)
  91:         {
  92:             var value = profile["FacebookId"];
  93:             return Convert.ToInt64(value);
  94:         }
  95:  
  96:         private static void SetFacebookId(ProfileBase profile, long id)
  97:         {
  98:             profile["FacebookId"] = id;
  99:         }
 100:     }
 101: }

As you can see this is C# 4.0 code using dynamics, but it is possible to use the Facebook library the same way (but with different syntax) in .NET 3.5. The http module must then be added to the system.webServer handlers element in web.config:

   1: <system.webServer>
   2:     <modules runAllManagedModulesForAllRequests="true">
   3:       ...
   4:       <add name="FacebookIntegrationModule" type="ACME.Web.Templates.HttpModules.FacebookIntegrationModule, ACME.Web" preCondition="managedHandler"/>
   5:     </modules>
   6:     ...
   7: </system.webServer>

Once a user has granted your application access, the user won’t be prompted again unless you change the requested permissions. If the user is already logged in to Facebook she won’t even have to sign in, the login will be automatic as soon as your page is visited!

Step 3: Fix the bugs :)

Make sure you test your solution with the browsers you are targeting. I also advice you to think about the javascript refreshing of the site when the session becomes available. As I will describe in a separate article I had problems with one specific Safari browser which got stuck in an infinite loop because it couldn’t persist the session and therefore continued to fire the sessionChanged event.

Source code

The code in this example is manually edited to enable a quick overview in few files. I copied and pasted code from several classes so it is likely to contain syntax errors etc. These edited files are available for download in the code section

Feb 15, 2011

Comments

Please login to comment.
Latest blogs
A day in the life of an Optimizely OMVP: Learning Optimizely Just Got Easier: Introducing the Optimizely Learning Centre

On the back of my last post about the Opti Graph Learning Centre, I am now happy to announce a revamped interactive learning platform that makes...

Graham Carr | Jan 31, 2026

Scheduled job for deleting content types and all related content

In my previous blog post which was about getting an overview of your sites content https://world.optimizely.com/blogs/Per-Nergard/Dates/2026/1/sche...

Per Nergård (MVP) | Jan 30, 2026

Working With Applications in Optimizely CMS 13

💡 Note:  The following content has been written based on Optimizely CMS 13 Preview 2 and may not accurately reflect the final release version. As...

Mark Stott | Jan 30, 2026

Experimentation at Speed Using Optimizely Opal and Web Experimentation

If you are working in experimentation, you will know that speed matters. The quicker you can go from idea to implementation, the faster you can...

Minesh Shah (Netcel) | Jan 30, 2026