Code

Facebook authentication in your AngularJS web app

, 21 Comments

Recently I already wrote about how to handle user authentication in a web app built over AngularJS, and I suggest you to read that article before continuing with the reading of this.

This time, even moved by a comment to my previous post, I would like to make a step further, trying to implement a login system based on the Facebook’s authentication system.
Spoiler: at the end you’ll find that all this is more easy of how you can think.

Authentication with Facebook

When I started to work on this little experiment I was not a big expert in the matter of Facebook’s api (and even now I’m really far from that); in this case I think that the smarter thing you can do is start reading the Facebook’s document about Facebook’s authentication system.
You’ll discover that Facebook makes available different ways to authenticate the users of your website, or application, on the basis of the device they are using (iOS, or Android), and even for a web app is possible to choose between two different flows: with or without JavaScript SDK.
I choose to follow the login flow with Javascript SDK.

Initialize the javascript SDK

I think the ideal is to init the javascript SDK as soon as possible, for this reason I will put the initialization code inside the run method. Moreover this will be the place from which load the SDK itself:


app.run(['$rootScope', '$window', 'srvAuth', 
	function($rootScope, $window, sAuth) {

	$rootScope.user = {};

	$window.fbAsyncInit = function() {
		// Executed when the SDK is loaded

		FB.init({ 

			/* 
			 The app id of the web app;
			 To register a new app visit Facebook App Dashboard
			 ( https://developers.facebook.com/apps/ ) 
			*/

			appId: '***************', 

			/* 
			 Adding a Channel File improves the performance 
			 of the javascript SDK, by addressing issues 
			 with cross-domain communication in certain browsers. 
			*/

			channelUrl: 'app/channel.html', 

			/* 
			 Set if you want to check the authentication status
			 at the start up of the app 
			*/

			status: true, 

			/* 
			 Enable cookies to allow the server to access 
			 the session 
			*/

			cookie: true, 

			/* Parse XFBML */

			xfbml: true 
		});

		sAuth.watchAuthenticationStatusChange();

	};

	// Are you familiar to IIFE ( http://bit.ly/iifewdb ) ?

	(function(d){
		// load the Facebook javascript SDK

		var js, 
		id = 'facebook-jssdk', 
		ref = d.getElementsByTagName('script')[0];

		if (d.getElementById(id)) {
			return;
		}

		js = d.createElement('script'); 
		js.id = id; 
		js.async = true;
		js.src = "//connect.facebook.net/en_US/all.js";

		ref.parentNode.insertBefore(js, ref);

	}(document));

}]);

Just a couple of notes before to move to the next paragraph:

  • What is srvAuth? Is a custom AngularJS service that I use to wrap all the authentication related stuff.
  • What is the channel file? It helps dealing with cross-domain communication in certain browsers. The contents of the channel.html file should be just a single line:
    
    <script src="http://connect.facebook.net/en_US/all.js"></script>
    
    

To end with the initialization tasks, it’s also a good practice to add the fb-root div, that is the place where the Javascript SDK attaches itself to; however if you skip this step the fb-root div will be created automatically.
Accomplish this task is so simple that we can do it ourselves; just write the following snippet wherever you want in the body of your page.


<div id="fb-root"></div>

The authentication service

If you have read even my previous post about user’s authentication, you won’t be surprised by the fact that I put the logic inherent the authentication in a specific purpose service.
The service I created is pretty simple: just three methods, and a single property that stores the user’s information retrieved from Facebook.
You already saw the first method because it’s executed from the previous snippet:


watchLoginChange = function() {

	var _self = this;

	FB.Event.subscribe('auth.authResponseChange', function(response) {

		if (response.status === 'connected') {
			
			/* 
			 The user is already logged, 
			 is possible retrieve his personal info
			*/
			_self.getUserInfo();

			/*
			 This is also the point where you should create a 
			 session for the current user.
			 For this purpose you can use the data inside the 
			 response.authResponse object.
			*/

		} 
		else {

			/*
			 The user is not logged to the app, or into Facebook:
 			 destroy the session on the server.
			*/
			 
		}

	});

}

This method uses the Facebook api to register a callback function that will be executed each time a change to the user authentication status occurs.
When the user is logged to Facebook, and also to the web app we receive the connected response status; then it’s possible to retrieve the user personal information from Facebook (using another method offered by the service).
Else if the user is not logged to Facebook, or did not granted the web app yet, we have not the permissions to call the Facebook api to retrieve personal information, so in this case we have also to destroy the user’s session on the server, created at the moment of the login.

How to retrieve user’s personal information from Facebook? Continue the analysis of the service with the getUserInfo method:


getUserInfo = function() {

	var _self = this;

	FB.api('/me', function(response) {

		$rootScope.$apply(function() { 

			$rootScope.user = _self.user = response; 

		});

	});

}

This methods simply wraps a call to the Facebook’s service that responds with the user’s profile information. The most important thing to note here is the use of the $apply AngularJS method. Infact, because the answer from Facebook is asynchronous, and so it comes from outside the framework world, we need to include it in AngularJS with the $apply method.

Finally, the last method of the service provide a way to logout the user from the web application (and from Facebook). However this operation won’t revokes the permission accorded to the app; so when the user will logged to Facebook in the future, he will logged to the app as well.


logout = function() {

	var _self = this;

	FB.logout(function(response) {

		$rootScope.$apply(function() { 

			$rootScope.user = _self.user = {}; 

		});	

	});

}

The Facebook login button

Finally, is the time to introduce the real star of this post: the Facebook login button… and the good news is that you’ve not more to do that include the following snippet in your web app.


<fb:login-button show-faces="true" max-rows="1" 
	size="large"></fb:login-button>

You can put this snippet everywhere you want; you can even use the ngInclude directive to load it from an external template.
That’s all.
However probably, sooner or later, you would like to customize the look of the login button.

A word on security

I admit, I simply ignored this topic for all the post.
This is because I already wrote enough on this topic in my previous post about the authentication in an AngularJS web app.
Everything I wrote for a generic authentication system is still valid for the login system based on Facebook’s authentication system that I described in this post.
In extreme synthesis: don’t trust the front-end, but repeat each check on the back-end.

Bruno

I'm Bruno Scopelliti, web developer from Bologna, Italy. I use this space to share my experiences, experiments and thoughts. I'm also on Google+, Twitter, and LinkedIn.

More Posts

Standard
  • kojilab

    This is awesome. Thanks so much. I never thought about FB.Event in the first place.

  • Neo Asterix

    your code is identical to this: https://github.com/Terumi/AngularJS-Facebook-Login/blob/master/js/app.js it would have been fair if you credited him.

    • http://brunoscopelliti.com/ Bruno Scopelliti

      Hi Neo,

      I didn’t know about that repository until now…
      I checked it right now, and the code is the same only for part relative to the initialization of the javascript SDK. Curious?

      This is dued to the fact that both me, and both Terumi read documentation, the Facebook documentation… (check the point number 3, https://developers.facebook.com/docs/facebook-login/getting-started-web/).

      The point of this post is how to use the facebook login in ANGULARJS… If you read it a more deeper, you’ll find that my implementation of the Facebook login system in an AngularJS web app is really different from the one proposed by Terumi (I let to you find the differences).

      I try to be more original as I can when I wrote a post on this blog… When I will use the work of someone else, I will give him the credit for sure.

      • Neo Asterix

        If you didn’t use his code you don’t have to credit of course, forget my post then, you must admit that those parts are identical but of course you can ended up there starting from the same point.

  • xowap

    Well, that’s nice but here you don’t really deal with the fact that when you’re doing a Facebook application, you usually are required to ask the user for more permissions than the “basic” permissions he gives you by default.

    Also, if you want to be able to call the FB global, Facebook has to be loaded at the time, otherwise you would be thanked with an error.

    This is why I started (but did not complete yet) the following library: http://hyperthese.net/post/frenetic-bunny/

    In case anyone’s interested, in addition to what I explain in my blog post, there is now an helper to go through paginated OpenGraph queries, but I still have to document it.

    • xowap

      And I forgot to mention: I use this inside an angular.js app, although there is no Angular integration yet. I’d be curious to see what can be done though :)

    • http://brunoscopelliti.com/ Bruno Scopelliti

      Hi xowap,
      Thanks for your feedback…

      You’re right in the post there is no mention about the way to ask the user more grants than the basic permission…
      This is why, I completely embraced the Facebook philosophy, that is ask a user a grant only when you really need to use that grant… and as first step in this post I want to introduce a login system based on the Facebook authentication system.

      However it’s really easy extend the approach that I illustrated to request more permissions.
      Basing on the Facebook documentation ( https://developers.facebook.com/docs/reference/plugins/login/ ) it’s possible to add to the login button a ‘scope’ attribute, that accepts as value a comma separated list of permissions.
      Something like:

      If this is not enough I can even extend the service that I wrote with a further method that wraps the Facebook api FB.login.

      FB.login(function(response) { /* handle response */ }, {scope: ‘grant_1,grant_2′});

      • xowap

        Well, now that you speak of the “login” button, I’d just say that it actually sucks and I’ve never used it in any FB app (and since I worked for a Facebook marketing company, I’ve made dozens of them).

        To re-iterate my arguments from a slightly different angle, the first thing I’d say about your solution is that you use the ugly default-style Facebook loader, that I have totally replaced by some much cleaner code.

        Another thing is that you never actually know when Facebook is loaded, if it loads at all. In Frenetic Bunny, I provide a way to do that by giving the user a promise that becomes true when Facebook is actually loaded — namely $.fb.ready().

        And one last thing is that the FB.login() method is boring as shit and can really be a pain in the ass. So I provided a wrapper around that that, once again, provides you with a promise that the permissions you asked for are actually granted. Because seeing that the user is currently connected doesn’t mean at all that he gave you those permissions, it just means that he installed the app (permission ”).

        What I’m suggesting is that it might be cool to either replicate Frenetic Bunny’s features, either wrap it to make it more Angular-ish, because I think that my way of seeing things saved me quite a lot of time when building Facebook applications, in comparison to the raw API. Just a suggestion ;)

        Something else that might be interesting to dig in, but that I have no idea on how to do it: Facebook OpenGraph queries return paginated results. It would be cool to have something able to feed an infinite scrolling from an API request.

        • http://brunoscopelliti.com/ Bruno Scopelliti

          I agree with you about the fact that the FB.login method is not brilliant… Even if I’ve not a great experience working on Facebook app, I already discovered that when you use it to request a grant, it responds with the connected status, even when the user didn’t granted anything…
          To workaround this problem I used FB.api(‘/me/permissions’, function(r) {}), wrapped inside another method of the service… I used 2 callbacks for each case (granted, or not granted), but your implementation with promise is much more elegant, so when I’ll find the time I’ll give it a try.

          • xowap

            Well, I’m just curious to see if you find some way to integrate that in an angularish way :)

  • Urigo

    How about a Github repo for the code?
    That way we can contribute and comment…

    • http://brunoscopelliti.com/ Bruno Scopelliti

      Hi urigo…
      Thanks for the suggestion…
      I will do it for sure, as soon as possible.

      • dmackerman

        Coming? :)

        • http://brunoscopelliti.com/ Bruno Scopelliti

          Sadly the demo I set up for this is gone missed… I am sorry.
          However starting from today, my code will be always hosted on Github

  • Ciul

    Hi, I wrote a github repository where I think I’m facing in a hopefully better way the asynchronous calls of Facebook load and methods calls.

    https://github.com/ciul/angularjs-facebook

  • Davor Peić

    Hi Bruno! Love your tutorials! I have one question, can I use this way of auth with FB for use in mobile app? (Appgyver Steroids/Phonegap) or you recommend use of maybe some other plugin for this? thanks

  • pasha

    Hi!) Are you “Hach” ? :D

  • DChang

    Hi Bruno. I hope you don’t mind – I made a Github repo for the principles covered here (and hopefully gave you sufficient credit): http://davidchang.github.io/angular-facebook-utils/

    Also thinking of adding a facebook-specific router directive that can handle sessions and public vs. private pages.

  • Gabo Lato

    Hi Bruno! I was trying your code and I don’t see where you defined the

    sAuth.watchAuthenticationStatusChange() function? Am I missing something?
    thanks in advance

  • lenin m

    cant find srvauth, please provide github ASAP. thank you!

  • Curt

    Has anyone been able to log in on IE?

    I added Bruno’s code into my AngularJS (version 1.2.6) app and it enables me to login great with Facebook on Chrome, but not IE9 (I have not tested on other versions of IE). Nothing happens when I run FB.getLoginStatus(). When I click on Facebook’s login button, I get Facebook’s dialog popup. After I enter my login info and click “Log In”, the popup goes blank and nothing happens after that (though, if I open Facebook.com in another tab, it has logged me in there).

    Has anyone got it to work on IE? Any suggestions on what I need to do?