/wp-content/plugins/wp-fb-autoconnect/_process_login.php

https://bitbucket.org/openfarmtech/weblog-content · PHP · 291 lines · 195 code · 37 blank · 59 comment · 36 complexity · b5ae26b36e49286b7ea5415438bbfaf3 MD5 · raw file

  1. <?php
  2. /**
  3. * This file handles Facebook Connect login requests. When a user logs in via the FB popup window,
  4. * the js_callbackfunc will redirect us here. We then use information from FB to log them into WP.
  5. * See the bottom of this file for notes on the Facebook API
  6. */
  7. //Include our options and the Wordpress core
  8. require_once("__inc_opts.php");
  9. require_once("__inc_wp.php");
  10. $jfb_log = "Starting login process (Client: " . $_SERVER['REMOTE_ADDR'] . ", Version: $jfb_version)\n";
  11. //Check the nonce to make sure this was a valid login attempt (not a hack), unless the check has been disabled (not recommended!)
  12. if( !get_option($opt_jfb_disablenonce) )
  13. {
  14. if( wp_verify_nonce ($_REQUEST ['_wpnonce'], $jfb_nonce_name) != 1 )
  15. j_die("Failed nonce check. Login aborted.");
  16. $jfb_log .= "WP: nonce check passed\n";
  17. }
  18. else
  19. $jfb_log .= "WP: nonce check DISABLED\n";
  20. //Get the redirect URL
  21. if( !isset($_POST['redirectTo']) || !$_POST['redirectTo'] )
  22. j_die("Error: Missing POST Data (redirect)");
  23. $redirectTo = $_POST['redirectTo'];
  24. $jfb_log .= "WP: Found redirect URL ($redirectTo)\n";
  25. //Include Facebook, making sure another plugin didn't already do so
  26. if( class_exists('Facebook') )
  27. {
  28. $jfb_log .= "WP: WARNING - Another plugin has already included the Facebook API. "
  29. . "If the login fails, please contact the other plugin's author and ask them not to "
  30. . "include Facebook for every page throughout Wordpress.\n";
  31. }
  32. else
  33. {
  34. if(version_compare('5', PHP_VERSION, "<="))
  35. require_once('facebook-platform/php/facebook.php');
  36. else
  37. j_die("Error: This plugin requires PHP5 or better.");
  38. }
  39. //Connect to FB and make sure we've got a valid session (we should already from the cookie set by JS)
  40. $facebook = new Facebook(get_option($opt_jfb_api_key), get_option($opt_jfb_api_sec), null, true);
  41. $fb_uid = $facebook->get_loggedin_user();
  42. if(!$fb_uid) j_die("Error: Failed to get the Facebook session. Please verify your API Key and Secret.");
  43. $jfb_log .= "FB: Connected to session (uid $fb_uid)\n";
  44. //Get the user info from FB
  45. $fbuserarray = $facebook->api_client->users_getInfo($fb_uid, array('name','first_name','last_name','profile_url','contact_email', 'email_hashes', 'pic_square', 'pic_big'));
  46. $fbuser = $fbuserarray[0];
  47. if( !$fbuser ) j_die("Error: Could not access the Facebook API client (failed on users_getInfo($fb_uid)): " . print_r($fbuserarray, true) );
  48. $jfb_log .= "FB: Got user info (".$fbuser['name'].")\n";
  49. //See if we were given permission to access the user's email
  50. //This isn't required, and will only matter if it's a new user without an existing WP account
  51. //(since we'll auto-register an account for them, using the contact_email we get from Facebook - if we can...)
  52. if( $fbuser['contact_email'] )
  53. $jfb_log .= "FB: Email privilege granted (" .$fbuser['contact_email'] . ")\n";
  54. else
  55. {
  56. $fbuser['contact_email'] = "FB_" . $fb_uid . $jfb_default_email;
  57. $jfb_log .= "FB: Email privilege denied\n";
  58. }
  59. //Run a hook so users can`examine this Facebook user *before* letting them login. You might use this
  60. //to limit logins based on friendship status - if someone isn't your friend, you could redirect them
  61. //to an error page (and terminate this script).
  62. do_action('wpfb_connect', array('FB_ID' => $fb_uid, 'facebook' => $facebook) );
  63. //Examine all existing WP users to see if any of them match this Facebook user.
  64. //First we check their meta: whenever a user logs in with FB, this plugin tags them with usermeta
  65. //so we can find them again easily. This obviously will only work for returning FB Connect users.
  66. $wp_users = get_users_of_blog();
  67. $wp_user_hashes = array();
  68. $jfb_log .= "WP: Searching for user by meta...\n";
  69. foreach ($wp_users as $wp_user)
  70. {
  71. $user_data = get_userdata($wp_user->ID);
  72. $meta_uid = get_usermeta($wp_user->ID, $jfb_uid_meta_name);
  73. if( $meta_uid && $meta_uid == $fb_uid )
  74. {
  75. $user_login_id = $wp_user->ID;
  76. $user_login_name = $user_data->user_login;
  77. $jfb_log .= "WP: Found existing user by meta (" . $user_login_name . ")\n";
  78. break;
  79. }
  80. //In case we don't find them by meta, we'll need to search for them by email below.
  81. //Precalculate each non-FB-connected user's mail-hash (http://wiki.developers.facebook.com/index.php/Connect.registerUsers)
  82. if( !$meta_uid )
  83. {
  84. $email= strtolower(trim($user_data->user_email));
  85. $hash = sprintf('%u_%s', crc32($email), md5($email));
  86. $wp_user_hashes[$wp_user->ID] = array('email_hash' => $hash);
  87. }
  88. }
  89. //If we couldn't find any usermeta identifying an existing WP user for this FB user, we'll use
  90. //FB to see if they've registered an email address on FB that matches any of our WP users' emails.
  91. //We can do this even without the email extended_permission, because we use email *hashes*.
  92. if( !$user_login_id && count($wp_user_hashes) > 0 )
  93. {
  94. if(version_compare(PHP_VERSION, '5', "<"))
  95. {
  96. $jfb_log .= "FP: CANNOT search for users by email in PHP4\n";
  97. }
  98. else
  99. {
  100. //Search for users via their email hashes. Facebook can handle 1000 at a time.
  101. $insert_limit = 1000;
  102. $hash_chunks = array_chunk( $wp_user_hashes, $insert_limit );
  103. $jfb_log .= "FP: Searching for user by email (" . count($wp_user_hashes) . " candidates of " . count($wp_users) . " total users)...\n";
  104. foreach( $hash_chunks as $num => $hashes )
  105. {
  106. //First we send Facebook a list of email hashes we want to check against this FB user.
  107. $jfb_log .= " Checking Users #" . ($num*$insert_limit) . "-" . ($num*$insert_limit+count($hashes)-1) . "\n";
  108. $ret = $facebook->api_client->connect_registerUsers(json_encode($hashes));
  109. if( !$ret ) j_die("Error: Could not register hashes with Facebook (connect_registerUsers).\n");
  110. //Next we get the hashes for the current FB user; This will only return hashes we
  111. //registered above, so if we get back nothing we know the current FB user is not in this group of WP users.
  112. $this_fbuser_hashes = $facebook->api_client->users_getInfo($fb_uid, array('email_hashes'));
  113. $this_fbuser_hashes = $this_fbuser_hashes[0]['email_hashes'];
  114. //If we did get back a hash, all we need to do is find which WP user it came from - and that's who's logging in!
  115. if(!empty($this_fbuser_hashes))
  116. {
  117. foreach( $this_fbuser_hashes as $this_fbuser_hash )
  118. {
  119. foreach( $wp_user_hashes as $this_wpuser_id => $this_wpuser_hash )
  120. {
  121. if( $this_fbuser_hash == $this_wpuser_hash['email_hash'] )
  122. {
  123. $user_login_id = $this_wpuser_id;
  124. $user_data = get_userdata($user_login_id);
  125. $user_login_name = $user_data->user_login;
  126. $jfb_log .= "FB: Found existing user by email (" . $user_login_name . ")\n";
  127. break;
  128. }
  129. }
  130. }
  131. }
  132. if( $user_login_id ) break;
  133. } //Try the next group of hashes
  134. }
  135. }
  136. //If we found an existing user, check if they'd previously denied access to their email but have now allowed it.
  137. //If so, we'll want to update their WP account with their *real* email.
  138. if( $user_login_id )
  139. {
  140. if( strpos($user_data->user_email, $jfb_default_email) !== FALSE && strpos($fbuser['contact_email'], $jfb_default_email) === FALSE )
  141. {
  142. $jfb_log .= "WP: Previously denied email has now been allowed; updating to (".$fbuser['contact_email'].")\n";
  143. $user_upd = array();
  144. $user_upd['ID'] = $user_login_id;
  145. $user_upd['user_email'] = $fbuser['contact_email'];
  146. wp_update_user($user_upd);
  147. }
  148. }
  149. //If we STILL don't have a user_login_id, the FB user who's logging in has never been to this blog.
  150. //We'll auto-register them a new account. Note that if they haven't allowed email permissions, the
  151. //account we register will have a bogus email address (but that's OK, since we still know their Facebook ID)
  152. if( !$user_login_id )
  153. {
  154. $jfb_log .= "WP: No user found. Automatically registering (FB_". $fb_uid . ")\n";
  155. $user_data = array();
  156. $user_data['user_login'] = "FB_" . $fb_uid;
  157. $user_data['user_pass'] = wp_generate_password();
  158. $user_data['first_name'] = $fbuser['first_name'];
  159. $user_data['last_name'] = $fbuser['last_name'];
  160. $user_data['user_nicename'] = $fbuser['name'];
  161. $user_data['display_name'] = $fbuser['first_name'];
  162. $user_data['user_url'] = $fbuser["profile_url"];
  163. $user_data['user_email'] = $fbuser['contact_email'];
  164. //Run a filter so the user can be modified to something different before registration
  165. $user_data = apply_filters('wpfb_insert_user', $user_data, $fbuser );
  166. //Insert a new user to our database and make sure it worked
  167. $user_login_id = wp_insert_user($user_data);
  168. if( get_class($user_login_id) == "WP_Error" )
  169. {
  170. $jfb_log .= "LOGIN ERROR: wp_insert_user() failed.\n";
  171. $jfb_log .= print_r($user_login_id, true) . "\n";
  172. j_die("Error: wp_insert_user failed! This should never happen; if you see this bug, please report it to the plugin author at $jfb_homepage.");
  173. }
  174. //Success! Notify the site admin.
  175. $user_login_name = $user_data['user_login'];
  176. wp_new_user_notification($user_login_name);
  177. //Run an action so i.e. usermeta can be added to a user after registration
  178. do_action('wpfb_inserted_user', array('WP_ID' => $user_login_id, 'FB_ID' => $fb_uid, 'facebook' => $facebook) );
  179. //If the option was selected and permission exists, publish an announcement about the user's registration to their wall
  180. if( get_option($opt_jfb_ask_stream) )
  181. {
  182. if( $facebook->api_client->users_hasAppPermission('publish_stream') )
  183. {
  184. $facebook->api_client->stream_publish(get_option($opt_jfb_stream_content));
  185. $jfb_log .= "FB: Publishing registration news to user's wall.\n";
  186. }
  187. else
  188. $jfb_log .= "FB: User has DENIED permission to publish to their wall.\n";
  189. }
  190. }
  191. //Tag the user with our meta so we can recognize them next time, without resorting to email hashes
  192. update_usermeta($user_login_id, $jfb_uid_meta_name, $fb_uid);
  193. $jfb_log .= "WP: Updated usermeta ($jfb_uid_meta_name)\n";
  194. //Also store the user's facebook avatar(s), in case the user wants to use them later
  195. if( $fbuser['pic_square'] )
  196. {
  197. update_usermeta($user_login_id, 'facebook_avatar_thumb', $fbuser['pic_square']);
  198. update_usermeta($user_login_id, 'facebook_avatar_full', $fbuser['pic_big']);
  199. $jfb_log .= "WP: Updated avatars (" . $fbuser['pic_square'] . ")\n";
  200. }
  201. else
  202. $jfb_log .= "FB: User does not have a profile picture; avatar will not be fetched.\n";
  203. //Log them in
  204. wp_set_auth_cookie($user_login_id);
  205. //Run a custom action. You can use this to modify a logging-in user however you like,
  206. //i.e. add them to a "Recent FB Visitors" log, assign a role if they're friends with you on Facebook, etc.
  207. do_action('wpfb_login', array('WP_ID' => $user_login_id, 'FB_ID' => $fb_uid, 'facebook' => $facebook) );
  208. do_action('wp_login', $user_login_name);
  209. //Email logs if requested
  210. $jfb_log .= "Login complete!\n";
  211. $jfb_log .= " WP User : $user_login_name (" . admin_url("user-edit.php?user_id=$user_login_id") . ")\n";
  212. $jfb_log .= " FB User : " . $fbuser['name'] . " (" . $fbuser["profile_url"] . ")\n";
  213. $jfb_log .= " Redirect: " . $redirectTo . "\n";
  214. j_mail("Facebook Login: " . $user_login_name);
  215. //Redirect the user back to where they were
  216. $delay_redirect = get_option($opt_jfb_delay_redir);
  217. if( !isset($delay_redirect) || !$delay_redirect )
  218. {
  219. header("Location: " . $redirectTo);
  220. exit;
  221. }
  222. ?>
  223. <!doctype html public "-//w3c//dtd html 4.0 transitional//en">
  224. <html>
  225. <head>
  226. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  227. <title>Logging In...</title>
  228. </head>
  229. <body>
  230. <?php echo "<pre>".$jfb_log."</pre>"?>
  231. <?php echo '<a href="'.$redirectTo.'">Continue</a>'?>
  232. </body>
  233. </html>
  234. <?php
  235. /*
  236. NOTES:
  237. ->Basic FB Connect Tutorial: http://wiki.developers.facebook.com/index.php/Facebook_Connect_Tutorial1
  238. ->Facebook Javascript API: http://developers.facebook.com/docs/?u=facebook.jslib.FB
  239. ->How authentication works: http://wiki.developers.facebook.com/index.php/How_Connect_Authentication_Works
  240. ->Note: The FB API is available in JS and PHP; a session that's been started in either of these languages
  241. can be used in the other: http://wiki.developers.facebook.com/index.php/Using_Facebook_Connect_with_Server-Side_Libraries
  242. Once you login with Javascript, it creates a session cookie. Then if you create a new Facebook object in PHP with the same
  243. API key, it'll automatically activate the session found in the cookie set by JS.
  244. ->Note: It's easiest to connect in Javascript (via a popup) then transfer to PHP (as I've done here), but you can also login directly with PHP
  245. by creating a new Facebook instance, generating a token with auth_token, ask the user to click the login URL, then get the session key
  246. by using getSession() with this token (as done in Facebook Photo Fetcher). See: http://forum.developers.facebook.com/viewtopic.php?pid=148426
  247. ->Note: An api_key and api_secret are NOT the same as a session_key and session_secret; the api_key identifies the APPLICATION (i.e. this webpage),
  248. and the SESSION represents an active user connected to this website (about whom we can pull profile info).
  249. */
  250. ?>