Skip to content
Share this..

Authenticate Facebook Users in MOdx, and Build a User Profile

2011 May 12
tags: modx
by Eddie

Although still very new, and with much to learn I am loving Modx as a tool to quickly build out robust sites.

Recently a client wanted to add Facebook Integration to their MODx site. I’ve used Facebook’s API before, and know it makes authentication pretty easy, but I had to figure out to insert that authorized user into Modx’s web context. And it was actually quite easy as well.

The user becomes a full blown member, and can be treated the same as any user that registered. (you can also treat them differently)

Read on to learn how two simple snippets allow Facebooks users to add themselves to your site in the provided member groups.

The extra also creates a full user profile in modx based on Facebook‘s info, complete with:

  • Name
  • Username
  • Email
  • Hometown
  • Photo

Finally, in case you want to add more social features using their Javascript API we make the appID and session available to the front end using placeholders.

Important:You Must Have Basic Login working using Login

This article assumes you already have basic authentication working for your users using Shaun and Jason’sLogin extra.   They also has great docs, so I’ll spare you.

But, I strongly suggest 3 things:

  1. A dedicated Login/Logout page
  2. A Login Link that shows to unknown users that simply links to the dedicated page <a href=”[[~29]]”>Login</a>
  3. A Logout link shown to authenticated users that links to the same page, but specifies logout.
    <a href=”[[~29? &service=`logout`]]”>Logout</a>

Shaun’s IF extra is a great way to manage that choir:

LoginChunk used on all pages

<div id="login-bar">
 
[[!If?
   &subject=`[[+modx.user.id]]`
   &operator=`empty`
 
   &then=`<!-- we dont know who the user is, show login and register links -->
      <a href="[[~29]]">Login</a>, 
    <a href="[[~27]]"/>Register</a>
    `
&else=`<!-- well hello old friend, show the known user his profile link, etc -->
	[[!Profile]]		
        Welcome Back, <a href="/users/my-profile.html">[[+modx.user.username]]</a>  | 
        <a href="[[~29? &service=`logout`]]" title="Logout">Logout</a>
`
]]
 
 
 
</div>

 
Once you’re all setup and working with that, read on.
 

Facebook API

You’ll want to visit Facebook’s developer site and get yourself and application ID.  They have great documentation, so I wont regurgitate it.

While you are there, be sure to grab the PHP Client they provide. ( you can save a step by using wget from your server to download the sdk tarball as well)

Now that facebook nows about your app, and you have an application ID and secret we can add our 2 snippets to MOdx, and upload the PHP Client and Certificate.
 
Note: Once you have created the two snippets below, you will need to provide them to both snippets as default properties.

Capture2 300x170 Authenticate Facebook Users in MOdx, and Build a User Profile

Edit the snippets, click "Properties" > "Unlock Default Properties" and then add the two values "appId" and "secret"

Upload the facebook client

I chose to put the php class under core/components/facebookLogin/ , and reccomend you do as well, because I plan to release all this as an extra very soon. (and that will keep the transition smooth)

You can use the file explorer to create the folder, but MOdx wont let you upload the PHP file. So you will need to ftp the file to the server or use wget right from the server. But in the end you need this, a directory with Facebook.php and fb_ca_chain_bundle.crt.  We will call the facebook.php file in later steps.

Capture 208x300 Authenticate Facebook Users in MOdx, and Build a User Profile

 

Creating the facebookLogin snippet for Modx

 
First add a new category named facebookLogin (again, keeps life easy), and write two new snippets, facebookLogin, and facebookLogout.

Capture1 Authenticate Facebook Users in MOdx, and Build a User Profile

The Login snippet outputs the Connect With Faceboook button, so you can just include it in the above code, right next to the login link.

Updated LoginChunk used on all pages

[[!If?
   &subject=`[[+modx.user.id]]`
   &operator=`empty`
 
   &then=`<!-- we dont know who the user is, show login and register links -->
      <a href="[[~29]]">Login</a>, 
    <a href="[[~27]]"/>Register</a>
    [[!facebookLogin? &userGroups=`Members`]]
    `
&else=`<!-- well hello old friend, show the known user his profile link, etc -->
	[[!Profile]]		
        Welcome Back, <a href="/users/my-profile.html">[[+modx.user.username]]</a>  | 
        <a href="[[~29? &service=`logout`]]" title="Logout">Logout</a>
`
]]

(Note: The extra will eventually use a chunk as a template to allow you to customize the text or image shown easily, this is more aligned with Modx’s recommended architecture as well, so bonus if you just implement that from the get go.)

/**
  *FacebookLogin Snippet for Modx
  * FacebookLogin is free software; you can redistribute it and/or modify it
 * under the terms of the <a href="www.gnu.org/licenses/gpl-2.0.html">GNU General Public License</a> as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option) any
 * later version.
 *
 * FacebookLogin is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the <a href="www.gnu.org/licenses/gpl-2.0.html">GNU General Public License</a> for more details.
 *
 * You should have received a copy of the <a href="www.gnu.org/licenses/gpl-2.0.html">GNU General Public License</a> along with
 * FacebookLogin; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
 
A substantial chunk of Jason Coward's and Shaun McCormick's Login snippet was used, and noted in the code below
 
*/
/**
 * Login
 *
 * Copyright 2010 by Jason Coward <jason@modx.com> and Shaun McCormick
 * <shaun@modx.com>
 *
 * Login is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option) any
 * later version.
 *
 * Login is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * Login; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */
require_once $modx->getOption('core_path').'components/facebookLogin/facebook.php';
 
//what permissions do we want
$par = array();
$par['req_perms'] = "email,user_hometown,user_website";
 
 
//
// Where do these come from? Add them as Properties to the snippet!
//
$appId= $modx->getOption('appId',$scriptProperties,'');
$secret= $modx->getOption('secret',$scriptProperties,'');
 
if(empty($appId) || empty($secret)){
$output='No AppID or Secret Provided, please obtain from developer.facebook.com';
return $output;
}
 
 
// Create our Application instance.
$facebook = new Facebook(array(
  'appId' => $appId,
  'secret' => $secret,
  'cookie' => true,
));
 
$session = $facebook->getSession();
 
 
/*
* Make the appID and current session available to the front end 
* (in case you want to add more social features using Javascript, specify those in the init() call)
*/
$modx->toPlaceholder('facebook_session',json_encode($session));
$modx->toPlaceholder('facebook_app_id',$facebook->getAppId());
 
$output="";
 
$me = null;
// Session based API call.
if ($session) {
  try {
    $uid = $facebook->getUser();
    $me = $facebook->api('/me');
 
  } catch (FacebookApiException $e) {
    error_log($e);
  }
}
 
// login or logout url will be needed depending on current user state.
if ($me) {
//die(print_r($me));
 
	$contexts = empty($contexts) ? array($modx->context->get('key')) : explode(',', $contexts);
	    foreach (array_keys($contexts) as $ctxKey) {
		$contexts[$ctxKey] = trim($contexts[$ctxKey]);
	    }
 
	$user = $modx->getObject('modUser',  array('remote_key:=' => $me['id'], 'remote_key:!=' => null));
 
	if(empty($user)){
	    	//their new!
		//facebook may pass multiple hometowns back
		if(!empty($me['hometown'])){
			if(is_array($me['hometown'])){
			   $homet=$me['hometown'][0];
			}else{
       			   $homet=$me['hometown'];
			}
		}
 
	  	// Create an empty modx user and populate with facebvook data
		$user = $modx->newObject('modUser');
		$user->fromArray(
                          array('username' => $me['name'],'active' => true
		                ,'remote_key' => $me['id'] ,'remote_data' => $me //store the remote data as json object in db (in case you need more info bout the FB user later)
		             )
                   );
 
//We'll also toss a profile on to save their email and photo and such
		$profile = $modx->newObject('modUserProfile');
		$profile->fromArray(array(
		'email' => isset($me['email']) ? $me['email'] : 'facebook-user@facebook.com'
		,'fullname' => $me['name']
		,'city' => $homet
                ,'photo'=> 'http://graph.facebook.com/'. $me['id'] .'/picture'
		));
		$user->addOne($profile, 'Profile');
 
 
/** Login, (C) 2010, Jason Coward, Shaun McCormick**/
 
		/* if usergroups set */
		$usergroups = $modx->getOption('usergroups',$scriptProperties,'');
		if (!empty($usergroups)) {
		    $usergroups = explode(',',$usergroups);
 
		    foreach ($usergroups as $usergroupMeta) {
			$usergroupMeta = explode(':',$usergroupMeta);
			if (empty($usergroupMeta[0])) continue;
 
			/* get usergroup */
			$pk = array();
			$pk[intval($usergroupMeta[0]) > 0 ? 'id' : 'name'] = $usergroupMeta[0];
			$usergroup = $modx->getObject('modUserGroup',$pk);
			if (!$usergroup) continue;
 
			/* get role */
			$rolePk = !empty($usergroupMeta[1]) ? $usergroupMeta[1] : 'Member';
			$role = $modx->getObject('modUserGroupRole',array('name' => $rolePk));
 
			/* create membership */
			$member = $modx->newObject('modUserGroupMember');
			$member->set('member',0);
			$member->set('user_group',$usergroup->get('id'));
			if (!empty($role)) {
			    $member->set('role',$role->get('id'));
			} else {
			    $member->set('role',1);
			}
			$user->addMany($member,'UserGroupMembers');
		    }//end foreach
		}//end user grops
/** End Login Code Block froom Login, (C) 2010 Jason Coward, Shaun McCormick **/
		$saved = $user->save();
	}//end if new user
 
	//new or not, they're logged in
        if(!$user->hasSessionContext('web')){
	   foreach ($contexts as $context) {
		$user->addSessionContext($context);
	   }
 
	   $modx->sendRedirect('/');
        }
 
} else {
	//else give them the chance to login
/* This should parse the generated URL with a chunk, for now you can edit the image or text here */
  	$output.= '<a href="'.$facebook->getLoginUrl($par).'"><img src="http://static.ak.fbcdn.net/rsrc.php/zB6N8/hash/4li2k73z.gif"></a>';
}
 
 
return $output;

Now just call that snippet, with the optional attribute of “userGroups” to add group membership for new users. That attribute, like Login’s accepts a comma separated list of groups.

[[!facebookLogin? &usergroups=`Members`]]

Well there you have it, users can now authenticate against facebook, and if they grant permissions, MODX will be able to add them as a new user complete with email, photo and hometown.

..Oh what’s that? You want them to be able to logout too. OK, ok..

facebookLogout hook for Modx’s Login Extra

The login code above adds the Facebook user as a fully fledged user. SO if you have Jason and Shaun’s Login extra working properly they should see a logout link. But what happens when you click it?

Modx says Logout!, Done. Redirect to whatever page. Facebook extra says Hey! i know you welcome back, and adds user back into the current context.

So unless we also tell Facebook that the user is logging out, they will never be able to logout again! Oh my..

So the solution is use the facebookLogout snippet as a post hook. This allows MOdx and facebook to work in tandem.

Modx says Logout!, Done. Redirect to Facebook’s logout URL. Facebook kills authentication session, sends back to whatever page. Facebook extra still checks, but can not load the previous session

/**
  *FacebookLogin Snippet for Modx
  * FacebookLogin is free software; you can redistribute it and/or modify it
 * under the terms of the <a href="www.gnu.org/licenses/gpl-2.0.html">GNU General Public License</a> as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option) any
 * later version.
 *
 * FacebookLogin is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the <a href="www.gnu.org/licenses/gpl-2.0.html">GNU General Public License</a> for more details.
 *
 * You should have received a copy of the <a href="www.gnu.org/licenses/gpl-2.0.html">GNU General Public License</a> along with
 * FacebookLogin; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
*/
 
//Login doesn't provide separate hook calls for logging or logging out, so we check..
$islogout = array_key_exists('logoutResourceId', $hook->getValues());
 
 
if($islogout){
 
require_once $modx->getOption('core_path').'components/facebookLogin/facebook.php';
 
//
// Where do these come from? Add them as Properties to the snippet!
//
$appId= $modx->getOption('appId',$scriptProperties,'');
$secret= $modx->getOption('secret',$scriptProperties,'');
 
// Create our Application instance.
$facebook = new Facebook(array(
  'appId' => $appID,
  'secret' => $secret,
  'cookie' => true,
));
 
$session = $facebook->getSession();
 
 
$modx->toPlaceholder('facebook_session',json_encode($session));
$modx->toPlaceholder('facebook_app_id',$facebook->getAppId());
 
 
 
 
$me = null;
// Session based API call.
if ($session) {
  try {
    $uid = $facebook->getUser();
    $me = $facebook->api('/me');
 
  } catch (FacebookApiException $e) {
    error_log($e);
  }
}
 
 
 
if($me){
     //we are logging out but facebook is still logged in, kill session
     die($modx->sendRedirect($facebook->getLogoutUrl()));
}
 
 // they are logging out, but not FB user, do nothing
 
 
}else{
 
       //just logging in
       $modx->sendRedirect('/users');
}

SO how do we call that code? Remember you’re dedicated Login/Logout page?

[[!Login? &tplType=`modChunk` &loginTpl=`lgnLoginTpl`]]

Just add the logout snippet as a posthook

[[!Login? &tplType=`modChunk` &loginTpl=`lgnLoginTpl` &postHooks=`facebookLogout`]]

That’s everything I think. As I said I am still new to MOdx, and have much to learn, so don’t hold back on the input.

23 Responses leave one →
  1. May 13, 2011

    Nice work Eddie!

    There is however a less drastic way to log the user out without logging them out of Facebook as well (hey, I don’t want other sites I’m visiting signing me out of FB!)

    		  require_once $modx->config['base_path'] .'lib/facebook-sdk/src/facebook.php';
      		$facebook = new Facebook(array(
        	  'appId'  => '11111',
        	  'secret' => 'ffffffffffff',
        	  'cookie' => true,
        	));
        	// As we use cookies, to log FB user out of our site requires the cookie to be destroyed
        	$facebook->destroyCookie();

    Code is from a MODX Evo site, but the idea is the same – just destroy their login cookie rather than their FB-login.

  2. May 13, 2011

    This is really awesome. Thanks for sharing this!

  3. Eddie permalink*
    May 13, 2011

    Thanks peter!

  4. Eddie permalink*
    May 13, 2011

    Thanks Anselm, hope it proves useful.

  5. Roopam permalink
    June 4, 2011

    hi there, nice walk-through!
    Need a little help though. Am not able to figure out where I am supposed to give the appId and the secret!
    I installed a the snippets and the php clients. Everything seems to be running fine except for the message for $output : No AppID or Secret Provided, please obtain from developer.facebook.com
    Thanks in advance!

  6. Eddie permalink*
    June 6, 2011

    @Roopam
    Sorry for the delay, went up north for the weekend.

    ANyway, each snippet has a property (or needs rather) for the key and secret. These should be done once, in the system properties, but I am still learning the modx landscape, and in my rush used customer properties in each snippet. I’ve added a screenshot above, just click “unlock default properties” and add appID and secret.

  7. June 19, 2011

    Great work Eddie.
    Is there any chance to get same functionality under EVO? Unfortunately, my hosting provider doesn’t support xPDO so I’m stuck with 1.05 (don’t ask why, it’s complicated)…

    Greetings from Serbia.

  8. June 27, 2011

    Wow! I’ve been waiting for something like this.

    I’ll try it out on our new website.

  9. Buddhi permalink
    August 4, 2011

    Nice work, I will try this.

  10. September 11, 2011

    Great Job dude! I will give it a try and if it works nicely, i will use it on every revo-page i do! Wow…

  11. Ben M permalink
    December 26, 2011

    Nice post! I saw you created a package on ModX extras but it’s gone/broken now. Was there an issue?

  12. Eddie permalink*
    January 6, 2012

    I don’t think so?.. But ill have to check. It may not have been completed on my end. If i get a chance this weekend I will follow up.

  13. Buddhi permalink
    January 31, 2012

    Package does not install.

  14. February 13, 2012

    Great article – gave me a wonderful head-start on getting Facebook Login working on a site I’m working on. Particularly the handling the data coming back from Facebook was very useful.

    However, the logout code didn’t work for me. The reason seems to be that the &postHook script (FacebookLogout) only gets called on registration and not when a user logs out.

    The way around this is to use the OnWebLogout system event. If you want to log your user out of Facebook as well as your own site you can call:

    $logoutURL = $facebook->getLogoutUrl(array(‘next’=>’url to return to after logout’);
    $modx->sendRedirect($logoutURL);

    However, I think that’s a little more aggressive than I wanted to be so I used the following code to just kill the user’s Facebook session and cookie instead leaving them logged out of my site but still logged in to Facebook:

    $returnURL = $modx->makeURL(The id of the resource you want to redirect to on your own site after logout,”,”,’full’);
    setcookie(‘fbs_’.$appID, ”, time() – 3600, ‘/’,’.yourdomain’);
    $facebook->setSession(NULL);
    header(‘Location:’.$returnURL);

    Thanks again for the great article.

  15. February 13, 2012

    Incidentally, there is a typo in your logout code too, when instantiating the Facebook object you use $appID instead of $appId :)

  16. Eddie permalink*
    February 15, 2012

    Thanks @Joe Molloy! Glad this got you started, even happier you gave back!

  17. Buddhi permalink
    February 16, 2012

    Eddie, Could you please look at on the package as I could not install it, seems like something is broken.
    Thanking you in advance.

  18. Buddhi permalink
    February 16, 2012

    The error message is as follows:
    Error:
    Could not install package with signature: facebookLogin-1-pl

    Console message is:

    Attempting to install package with signature: facebookLogin-1-pl
    package found … now preparing to install.
    Could not find package manifest at home/../Public_html/…

  19. Eddie permalink*
    February 17, 2012

    Yeah, the package is incomplete, I thought removed from MOdx repo.

    You’re better off just coding the solution from the examples above. Also, take head to Joe and Peter’s comments above

  20. February 20, 2012

    Thanks Eddie. Just to let you know, I have just submitted my version of an extra to achieve this for approval – it’s called fbLogin and it uses version 3 of the Facebook PHP SDK.

    I have blogged about it here – http://www.hyper-typer.com/news/facebook-login-modx

  21. Eddie permalink*
    March 4, 2012

    That’s great!

    Link?

  22. March 19, 2012

    Hi there,

    I cant get below piece of code to work. If I leave it out things work but I need this to display if a user is logged in right?

    $session = $facebook->getSession();

    /*
    * Make the appID and current session available to the front end
    * (in case you want to add more social features using Javascript, specify those in the init() call)
    */
    $modx->toPlaceholder(‘facebook_session’,json_encode($session));
    $modx->toPlaceholder(‘facebook_app_id’,$facebook->getAppId());

    $output=”";

    My page fails to load if use the code above.

  23. March 26, 2012

    Very useful and simple, tired to dig different boting solutions.

    This one works! Thanks a lot for saving my time!

Leave a Reply

Note: You can use basic XHTML in your comments. Your email address will never be published.

Subscribe to this comment feed via RSS