You’ve seen how some popular websites let you register or login via Facebook — Pinterest, Vimeo, Lifehacker, and many more.
In this blog post, I’ll show you the very basic setup of the Facebook login button for your website. It is up to you to enhance the functions of your registration system.
Note: There are periodic updates to the Facebook API, sometimes resulting to issues with previously working implementations of the API. So please note that this tutorial was written in the third week August 2015. π
I began writing this tutorial late in August, but because of other projects, was able to publish it when it’s already September.
This Facebook login tutorial’s coverage and limitations
- This Facebook login tutorial will cover only the basic setup of the “Login via Facebook” button.
- Extended permissions request and the setup on your server will not be included for now.
Requirements of this tutorial to integrate Facebook login to your website
- Internet connection
- JavaScript turned on in your web browser
- Basic knowledge in HTML, PHP, and JavaScript
- A Facebook Developer account – find out how to create one here
- Apache and PHP on local machine (e.g. in XAMPP)
My coding environment for this tutorial
I’ll run the codes in XAMPP on Windows 7.
And now, let’s begin with the Facebook login integration tutorial!
There will be previews of codes from here on, and near the end of the page will be a download link so you could get a copy of my Login via Facebook codes.
I will not explain the codes line by line anymore, but I already placed comments on most lines on the code previews.
Part 1: Prepare a Facebook app
Go to the Facebook Developers site.
Under the “My Apps” menu item, choose “Add a New App”.
Choose “Website”.
We are about to create a Facebook app ID.
Type a name for your Facebook app. I’m naming mine “My App August 2015” for this tutorial.
Leave the toggle for “Is this a test version of another app?” to know, and choose any category that fits the type of website you’r going to use the Facebook login with. I’m going to choose “Reference” for this tutorial.
Let’s setup our site later locally. I plan to put my codes later in a directory named “fb_login_tut_aug2015” in my XAMPP htdocs folder.
First, you may want to take note of the JavaScript code snippet provided in the “Setup the Facebook SDK for JavaScript” page (see photo above). You’ll need it for the site later. The “app ID” is included in the fbAsyncInit as “appId”.
Enter the URL of our localhost site: http://localhost/fb_login_tut_aug2015. You can use any folder name you want, but remember that I’ll refer to the local directory as “fb_login_tut_aug2015” from here on.
Part 2: Setup the site in localhost
Database table schema
Create the users
table using this schema:
1 2 3 4 5 6 7 8 9 10 11 |
CREATE TABLE IF NOT EXISTS `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `fb_app_user_id` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, `picture_url` longtext NOT NULL, `created_at` datetime NOT NULL, `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `fb_app_user_id` (`fb_app_user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; |
Folder structure:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
fb_login_tut_aug2015 β index.php β login.php β includes.php β controllers.php ββββclasses β β Account.class.php β β MysqliDb.class.php ββββassets ββββcss β β style.css ββββimages β β loading.gif ββββjs β β login.js |
index.php for Login via Facebook
Create a file, index.php
, and save it under the directory fb_login_tut_aug2015
.
The index will be the page shown to the user when logged in. Picture, name and email address as saved on the user’s Facebook account, if available, are shown on the index page.
Place the following code into that file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
<?php /** * Include session start, class inclusions, and database config */ require_once('includes.php'); /** * If not logged in, take user to login page */ if(!isset($_SESSION['logged_user_id']) || strlen($_SESSION['logged_user_id']) < 1 ) { header("Location: login.php"); } /** * Fetch user data for profile display */ $account = new Account; $user = $account->get_user_data($_SESSION['logged_user_id']); ?> <!DOCTYPE html> <head> <title>Login via Facebook from blog.catzie.net</title> <link rel="stylesheet" href="assets/css/style.css"> </head> <body> <h1>Welcome!</h1> <div id="profile"> <h2>Your Profile</h2> <div class="profile-picture_url"> <img src="https://graph.facebook.com/<?php echo isset($user['fb_app_user_id']) ? $user['fb_app_user_id'] : '' ; ?>/picture?type=large" alt="" width="200" height="200"> </div> <div class="profile-name"> <span class="label">Name:</span> <?php echo (isset($user['name']) && strlen($user['name']) > 0 ) ? $user['name'] : 'n/a' ;?> </div> <div class="profile-email"> <span class="label">Email:</span> <?php echo (isset($user['email']) && strlen($user['email']) > 0 ) ? $user['email'] : 'n/a' ;?> </div> <div class="profile-created_at"> <span class="label">Registration Date:</span> <?php echo (isset($user['created_at']) && strlen($user['created_at']) > 0 ) ? $user['created_at'] : 'n/a' ;?> </div> <p><a href="controllers.php?p=logout">Logout</a></p> </div> </body> </html> |
login.php for Login via Facebook
When index.php detects that the user is not logged in, he or she is redirected to the login.php page.
The login page is where the “Login via Facebook” button is placed. The user authorizes the Facebook app here to retrieve user data, before being able to finally log in.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<?php if(session_id() == '') { session_start(); } ?> <html> <head> <title>Login via Facebook from blog.catzie.net</title> <link rel="stylesheet" href="assets/css/style.css"> <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script> <script src="assets/js/login.js"></script> </head> <body> <h2>Login</h2> <button type="button" onclick="fb_login()">Login via Facebook</button> <img src="assets/images/loading.gif" alt="loading..." id="loading_gif" style="visibility:hidden"> <div id="messages"> <div class="message auth-failed error" style="display:none"> Sorry, something went wrong with your Facebook login attempt. </div> <div class="message success" style="display:none"> Logging you in... </div> </div> <div id="footer"> <small> Sample "Login via Facebook" from <a href="http://blog.catzie.net" target="_blank">blog.catzie.net</a> </small> </div> </body> </html> |
includes.php for Login via Facebook
When at least two of my PHP files call for the same functions or require the same group of files, I think it’s practical to just embed those common codes. I placed my common codes in includes.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
≪?php /** * Start session for login system */ if(session_id() == '') { session_start(); } /** * Include classes */ require_once('classes/Account.class.php'); require_once('classes/MysqliDb.class.php'); /** * Database Config and instantiation */ $db_config = array( 'host' => 'localhost', 'username' => 'root', 'password' => '', 'databaseName' => 'fb_login_tut_aug2015' ); $mysqli = new MysqliDb ($db_config['host'], $db_config['username'], $db_config['password'], $db_config['databaseName']); |
controllers.php for Login via Facebook
I made controllers.php so I could place functions that will possibly be called from different places. It will be good to place functions that interact with classes/models and views.
check_fb_data() is called through Ajax from the login page, and then it calls the Accounts class to check for data in our “users” table.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
<?php /** * Include session start, class inclusions, and database config */ require_once('includes.php'); /** * Controller for page routes * Add more switch cases and functions as needed */ if(isset($_GET['p'])){ $p = $_GET['p']; $return = array(); switch($p){ case "check_fb_data": check_fb_data(); break; case "logout": logout(); break; } } /** * Save FB data (attempt) * return { "success" : boolean, "message" : string } */ function check_fb_data(){ $fb_data = $_POST; $fb_data['fb_app_user_id'] = (isset($fb_data['fb_app_user_id'])) ? $fb_data['fb_app_user_id'] : ""; $fb_data['name'] = (isset($fb_data['name'])) ? $fb_data['name'] : ""; $fb_data['email'] = (isset($fb_data['email'])) ? $fb_data['email'] : ""; $fb_data['picture_url'] = (isset($fb_data['picture_url'])) ? $fb_data['picture_url'] : ""; /** * Check existence of user in database */ $account = new Account; $user_db_id = $account->get_user_db_id($fb_data['fb_app_user_id']); /** * If the Facebook app user ID is not in the database */ if(false === $user_db_id){ $user_db_id = $account->add_user($fb_data['fb_app_user_id'], $fb_data['name'], $fb_data['email'], $fb_data['picture_url']); $return['success'] = true; $return['message'] = "Registration successful!"; } /** * If the Facebook app user ID is already in the database */ else { $return['success'] = true; $return['message'] = "Welcome back!"; } /** * Log the user in by setting the session */ $_SESSION['logged_user_id'] = $user_db_id; /** * Return JSON to the Ajax call */ echo json_encode($return); exit; } /** * Logout * Destroy session and redirect user */ function logout(){ session_destroy(); header("Location: index.php"); } |
Account.class.php for Login via Facebook
I wrote this Account class, separating the database interactions from the rest of the code, with goal of making the functions in the class reusable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
<?php /** * The Account class saves and retrieves data * to and from the `users` table. Coded in August 2015. * * @author Catzie <[email protected]> */ class Account{ protected $db; function __construct(){ /** * $mysqli was instantiated in includes.php * Now, we assign it to the property $this->db */ global $mysqli; $this->db = $mysqli; } /** * Get database row ID of user * @param string $fb_app_user_id ID of user in the Facebook app * @return int|boolean Database row ID if existing, * otherwise returns boolean false */ function get_user_db_id($fb_app_user_id){ $users = $this->db->rawQuery('SELECT * from users WHERE fb_app_user_id = ?', Array ($fb_app_user_id)); if(count($users) > 0){ return $users[0]['id']; } else { return false; } } /** * Add user to database * @param string $fb_app_user_id ID of user in the Facebook app * @param string $name Name of user from Facebook * @param string $email Email of user from Facebook * @param string $picture_url URL of user's picture from Facebook * @return int|boolean Row ID of user in database */ function add_user($fb_app_user_id, $name, $email, $picture_url){ $data = Array ( "fb_app_user_id" => $fb_app_user_id, "name" => $name, "email" => $email, "picture_url" => $picture_url, "created_at" => date("Y-m-d H:i:s"), "updated_at" => date("Y-m-d H:i:s") ); $new_id = $this->db->insert ('users', $data); if($new_id){ return $new_id; } else { return false; } } /** * Get user data for profile page * @param int $user_id Row ID of user in the database * @return array|boolean Array with user data, otherwise boolean false */ function get_user_data($user_id){ $user = $this->db->rawQuery('SELECT * from users WHERE id = ?', Array ($user_id)); if(count($user) > 0){ return $user[0]; } else { return false; } } } |
MysqliDb.class.php for Login via Facebook
For my database interactions, I use a premade class called MysqliDb.class.php, written by Jeffery Way, Josh Campbell, and Alexander V. Butenko.
The following is a download link to MysqliDb.class.php from Dropbox.
MysqliDb.class.php (unknown, 379 hits)
style.css CSS for Login via Facebook
Just simple styling for the display.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
*{ font-family: sans-serif; } .label{ font-weight: bold; } #footer{ margin-top:50px; padding-top:10px; border-top:1px solid #ccc; } #footer, #footer a{ color: #999; } #messages .message{ padding: 10px; border: 1px solid #E29C9C; margin: 10px 0; font-size: .8em; } #messages .error{ border: 1px solid #E29C9C; background-color: #FFEBEB; } #messages .success{ border: 1px solid #9CE29C; background-color: #EBFFED; } |
loading.gif image for Login via Facebook
Right click on the following GIF image, then choose to "Save image as…"
login.js JavaScript for Login via Facebook
First off, please remember to set your Facebook App ID as the value of the variable the_app_id
at the beginning of login.js. The app won’t work without it.
This JavaScript is the core of our Facebook login system. The user needs to be logged in to Facebook and needs to authorize the app we are making to retrieve user data.
You will see here how we can call a function with a dynamic name. For example, you have two available functions: function A(){}
and function B(){}
. You can have a condition to determine which of the two functions the script should call, like, if(condition===true){ var func_name = "A"; } else { var func_name = "B"; }
When it’s time to call the function, based on the condition check performed, you type this: window[func_name]()
. Whatever value was assigned to the variable “func_name” is the name of the function that will be run by JavaScript.
Try to analyze this login.js script, and you’ll realize that to decide whether we’ll call the function named init_fb_login
or fb_get_user_data
, we check if it’s the first time that the Login via Facebook button has been clicked.
Originally, my script always called the window.fbAsyncInit
function that contains FB.init regardless of how many times the button’s been clicked. But that setup only let the Facebook login work on the first click.
So I revised my script so that if it’s the first click on the button, we’ll run init_fb_login()
because that is where FB.init()
is. And from the second click onward, we would always call fb_get_user_data()
.
Reminder: Set your Facebook App ID as the value of the variable the_app_id
at the beginning of login.js. The app won’t work without it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
/** * Variable setup * Remember to set your Facebook App ID here */ var the_app_id = '<YOUR-FACEBOOK-APP-ID>'; var first_click = true; /** * Fire when Login Via Facebook button is pressed */ function fb_login(){ /** * Run function based on whether * it's the first time the button is clicked * Function name is dynamic */ if(first_click===true){ var func_name = 'init_fb_login'; } else { var func_name = 'fb_get_user_data'; } /** * Set first_click to false now, * since this function is fired on click */ first_click = false; /** * Show loading.gif */ $('#loading_gif').css('visibility','visible'); /** * Call function by dynamic name then CALLBACK */ window[func_name](function(fb_login_data){ /** * If no Facebook data fetched */ if(fb_login_data === false){ $('.message.auth-failed').show(); $('#loading_gif').css('visibility','hidden'); } ajax_fb_login(fb_login_data); }); } function ajax_fb_login(fb_login_data){ if(fb_login_data != false) { $.ajax({ type : 'POST', dataType: "json", url : 'controllers.php?p=check_fb_data', data : { 'fb_app_user_id': fb_login_data.id, 'name': fb_login_data.name, 'picture_url': fb_login_data.picture.data.url }, success : function(data){ if(data.success == true){ var success_msg = $('.message.success').html(); success_msg = data.message + " " + success_msg; $('.message.success').html(success_msg); $('.message.success').show(); window.setTimeout(function(){ // SET SESSION FOR LOGIN! var redirect_url = "index.php"; $(location).attr('href', redirect_url) }, 2000); } else { $('.message.auth-failed').show(); } $('#loading_gif').css('visibility','hidden'); } }); } } /** * Call this function directly AFTER first click * On first click, we call a different function * which calls this too */ function fb_get_user_data(callback){ FB.getLoginStatus(function(response) { /** * If already logged in to Facebook from the web browser */ if (response.status === 'connected') { /** * Fetch Facebook data */ FB.api('/me?fields=id,name,email,picture', 'get', function(response) { /** * Pass response to callback function */ callback(response); }); } /** * If not yet logged in */ else { /** * Ask user to login */ FB.login(function(response) { /** * If Facebook login was successful */ if (response.authResponse) { /** * Fetch Facebook data */ FB.api('/me?fields=id,name,email,picture', 'get', function(response) { callback(response); }); } /** * If Facebook login was not successful */ else { /** * Pass boolean false to callback */ callback(false); } }); } }); } /** * Initiate Facebook * On first click of Login button */ function init_fb_login(callback){ window.fbAsyncInit = function() { /** * Initiate FB object */ FB.init({ appId : the_app_id, cookie : true, xfbml : true, version : 'v2.4', oauth : true }); /** * Get Facebook user data */ fb_get_user_data(callback); }; /* * Load the SDK asynchronously */ (function(d, s, id){ var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) {return;} js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/en_US/sdk.js"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk')); } |
Download link to my codes for “Login via Facebook”
Feel free to download and use the codes I’m sharing:
Login via Facebook - August 2015 Codes (unknown, 511 hits)
Troubleshooting
“App Not Setup: This app is still in development mode, and you don’t have access to it. Switch to a registered test user or ask an app admin for permissions.
In the Facebook Developers site, go to your app then Settings -> Basic, then enter your email address on the field “Contact Email”. Before you make your Facebook app live, you are required to enter an email address.
Once you enter an email address in Settings -> Basic, you may now make your Facebook app live. To the right side of the message “Do you want to make this app and all its live features available to the general public?”, click the button that says “NO” to toggle it.
I can’t promise to be able to help with every issue you might encounter, but feel free to post questions in the comments section, and please try to help each other out. π
Great having you around IT world and sharing technological code advancement. Keep it up!