Easy Node Authentication: Facebook

Free Course

Build Your First Node.js Website

Node is a powerful tool to get JavaScript on the server. Use Node to build a great website.

Welcome to Part 2 of our Easy Node Authentication series. We will be using the foundation of that tutorial to use Facebook authentication with our application. We already have a good application structure for our application packages, user model, application setup and views.

node-auth-facebook Since we set up our application to be as clean and efficient as possible, we will only have to create our Facebook application and add to 4 different files:

Creating the FB App and App ID and Secret config/auth.js
Configuring the Passport Facebook Strategy config/passport.js
Generating Routes app/routes.js
Updating Views views/

This will be a much shorter tutorial than the last one since we don’t have to set up our entire Node application from scratch. This is our code structure from the first tutorial. If you need those files, go read that tutorial.


- app
------ models
---------- user.js  <!-- our user model -->
------ routes.js    <!-- all the routes for our application -->
- config
------ auth.js      <!-- will hold all our client secret keys (facebook, twitter, google) -->
------ database.js  <!-- will hold our database connection settings -->
------ passport.js  <!-- configuring the strategies for passport -->
- views
------ index.ejs    <!-- show our home page with login links -->
------ login.ejs    <!-- show our login form (only needed for local login) -->
------ signup.ejs   <!-- show our signup form (only needed for local signup) -->
------ profile.ejs  <!-- after a user logs in, they will see their profile -->
- package.json      <!-- handle our npm packages -->
- server.js         <!-- setup our application -->

Let’s get down to business.

Authenticating With Facebook using Passport

Creating Our Facebook Application

First thing’s first, let’s go to the Facebook Developers Portal and create our application. Once we do that, we will have the credentials we need for logging in with Facebook.

node-auth-facebook-keys Set your callback URL to http://localhost:8080/auth/facebook/callback. This will ensure that our application will authenticate with Facebook and redirect users back to our application after they have approved access for our application. The callback will be where we store the user details that we need.

Live vs Sandbox: Having your application in sandbox mode means only you can log into the application. Live means logins are available for everyone.

Now that we have our App ID and App Secret, let’s set up our config file. We’ll add the necessary fields for our Twitter and Google auth also so we can just update that in our future Node Authentication tutorials.

// config/auth.js

// expose our config directly to our application using module.exports
module.exports = {

    'facebookAuth' : {
        'clientID'      : 'your-secret-clientID-here', // your App ID
        'clientSecret'  : 'your-client-secret-here', // your App Secret
        'callbackURL'   : 'http://localhost:8080/auth/facebook/callback'
    },

    'twitterAuth' : {
        'consumerKey'       : 'your-consumer-key-here',
        'consumerSecret'    : 'your-client-secret-here',
        'callbackURL'       : 'http://localhost:8080/auth/twitter/callback'
    },

    'googleAuth' : {
        'clientID'      : 'your-secret-clientID-here',
        'clientSecret'  : 'your-client-secret-here',
        'callbackURL'   : 'http://localhost:8080/auth/google/callback'
    }

};

Fill in your clientID (app id) and clientSecret (app secret) and we’ll be using this data when we configure our Passport Facebook Strategy.

Configuring Passport’s Facebook Strategy config/passport.js

Now that we have our Node application set to access our Facebook application, let’s configure our Facebook strategy. This strategy will be used to authenticate with Facebook and handle the callback.

The code from our Local Strategies will be in this file, we’ll just add our Facebook Strategy below them.

// config/passport.js

// load all the things we need
var LocalStrategy    = require('passport-local').Strategy;
var FacebookStrategy = require('passport-facebook').Strategy;

// load up the user model
var User       = require('../app/models/user');

// load the auth variables
var configAuth = require('./auth');

module.exports = function(passport) {

    // used to serialize the user for the session
    passport.serializeUser(function(user, done) {
        done(null, user.id);
    });

    // used to deserialize the user
    passport.deserializeUser(function(id, done) {
        User.findById(id, function(err, user) {
            done(err, user);
        });
    });
    
    // code for login (use('local-login', new LocalStategy))
    // code for signup (use('local-signup', new LocalStategy))

    // =========================================================================
    // FACEBOOK ================================================================
    // =========================================================================
    passport.use(new FacebookStrategy({

        // pull in our app id and secret from our auth.js file
        clientID        : configAuth.facebookAuth.clientID,
        clientSecret    : configAuth.facebookAuth.clientSecret,
        callbackURL     : configAuth.facebookAuth.callbackURL

    },

    // facebook will send back the token and profile
    function(token, refreshToken, profile, done) {

        // asynchronous
        process.nextTick(function() {

            // find the user in the database based on their facebook id
            User.findOne({ 'facebook.id' : profile.id }, function(err, user) {

                // if there is an error, stop everything and return that
                // ie an error connecting to the database
                if (err)
                    return done(err);

                // if the user is found, then log them in
                if (user) {
                    return done(null, user); // user found, return that user
                } else {
                    // if there is no user found with that facebook id, create them
                    var newUser            = new User();

                    // set all of the facebook information in our user model
                    newUser.facebook.id    = profile.id; // set the users facebook id                   
                    newUser.facebook.token = token; // we will save the token that facebook provides to the user                    
                    newUser.facebook.name  = profile.name.givenName + ' ' + profile.name.familyName; // look at the passport user profile to see how names are returned
                    newUser.facebook.email = profile.emails[0].value; // facebook can return multiple emails so we'll take the first

                    // save our user to the database
                    newUser.save(function(err) {
                        if (err)
                            throw err;

                        // if successful, return the new user
                        return done(null, newUser);
                    });
                }

            });
        });

    }));

};

Profile: The callback will pass back user profile information and each service (Facebook, Twitter, and Google) will pass it back a different way. Passport standardizes the information that comes back in its profile object. For more information on how Passport formats its user profile, visit the user profile doc.

We will use this strategy to authenticate with Facebook and handle the callback. We will also store some user information and the Facebook oauth token.

Now that it is all set up, we just have to link to it in our routes.

Generating Our Routes app/routes.js

Remember when we set up our application, our passport object is passed from the server.js file to the config/passport.js file. Then it is passed to the routes. This is why we can just add our Facebook Strategy straight to the config file and have it work in routes.

This is the simplest part. We will need two routes: one for authentication and one for handling the callback.

  • /auth/facebook: Send our user to Facebook to authenticate
  • /auth/facebook/callback: Facebook sends our user back to our application here with token and profile information.
// app/routes.js

module.exports = function(app, passport) {

    // route for home page
    app.get('/', function(req, res) {
        res.render('index.ejs'); // load the index.ejs file
    });

    // route for login form
    // route for processing the login form
    // route for signup form
    // route for processing the signup form

    // route for showing the profile page
    app.get('/profile', isLoggedIn, function(req, res) {
        res.render('profile.ejs', {
            user : req.user // get the user out of session and pass to template
        });
    });

    // =====================================
    // FACEBOOK ROUTES =====================
    // =====================================
    // route for facebook authentication and login
    app.get('/auth/facebook', passport.authenticate('facebook', { scope : 'email' }));

    // handle the callback after facebook has authenticated the user
    app.get('/auth/facebook/callback',
        passport.authenticate('facebook', {
            successRedirect : '/profile',
            failureRedirect : '/'
        }));

    // route for logging out
    app.get('/logout', function(req, res) {
        req.logout();
        res.redirect('/');
    });

};

// route middleware to make sure a user is logged in
function isLoggedIn(req, res, next) {

    // if user is authenticated in the session, carry on
    if (req.isAuthenticated())
        return next();

    // if they aren't redirect them to the home page
    res.redirect('/');
}

By default, Facebook will provide you with user information, but not the email address. We will add this by specifying the scope. You can also add in more scopes to access more information.

When logging in with Facebook, try to use the fewest permissions that you need. You want your users to feel comfortable about their privacy when logging into and using your application.

Showing Our User index.ejs, profile.ejs

Now that we have our routes, we just need to provide users with a button to authenticate with Facebook. Then after they are authenticated and redirected to the authentication required profile page, we want to display their information.

Showing the Login Button

We’ll add this button to our index.ejs file.

<!-- views/index.ejs -->

<!doctype html>
<html>
<head>
    <title>Node Authentication</title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"> <!-- load bootstrap css -->
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
    <style>
        body        { padding-top:80px; }
    </style>
</head>
<body>
<div class="container">

    <div class="jumbotron text-center">
        <h1><span class="fa fa-lock"></span> Node Authentication</h1>

        <p>Login or Register with:</p>

        <a href="/auth/facebook" class="btn btn-primary"><span class="fa fa-facebook"></span> Facebook</a>

    </div>

</div>
</body>
</html>   

Now we have the login button for Facebook. It will point to the route we just created and then the callback will redirect the user to the profile page upon successful authentication.

For simplicity’s sake, I’ve just removed the links from the last tutorial for local login and signup. You can keep them, but they aren’t important. The last tutorial of this series will combine all the forms of login so we’ll deal with those then.

node-auth-facebook When a user clicks that button, they will be taken to our /auth/facebook route where they will be passed to the Passport Strategy. There they will be sent to Facebook for authentication.

node-facebook-permission If a user presses cancel, they will just be redirected back to our home page. This is set in the failureRedirect of our /auth/facebook/callback route. If successful, they will be saved to the database or logged in and redirected to /profile.

The Profile Page views/profile.ejs

The last thing we need to do is to show off our user information on our profile page.

We can see the user below in our database:

node-auth-facebook-database We have our user with the Facebook information that we set. Now we show that user on their profile page.

<!-- views/profile.ejs -->
<!doctype html>
<html>
<head>
    <title>Node Authentication</title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css">
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css">
    <style>
        body        { padding-top:80px; word-wrap:break-word; }
    </style>
</head>
<body>
<div class="container">

    <div class="page-header text-center">
        <h1><span class="fa fa-anchor"></span> Profile Page</h1>
        <a href="/logout" class="btn btn-default btn-sm">Logout</a>
    </div>

    <div class="row">

        <!-- FACEBOOK INFORMATION -->
        <div class="col-sm-6">
            <div class="well">
                <h3 class="text-primary"><span class="fa fa-facebook"></span> Facebook</h3>

                    <p>
                        <strong>id</strong>: <%= user.facebook.id %><br>
                        <strong>token</strong>: <%= user.facebook.token %><br>
                        <strong>email</strong>: <%= user.facebook.email %><br>
                        <strong>name</strong>: <%= user.facebook.name %>
                    </p>

            </div>
        </div>

    </div>

</div>
</body>
</html>

Now we have our user logged in at their profile page.

node-auth-facebook-profile

That’s It!

That wasn’t too much work! Since our application was already set up in the last tutorial, we only had to add a few components. We have configured Passport for Facebook, added our routes, authenticated with Facebook, and shown our user’s profile.

In the next article, we will be tackling Twitter authentication using Passport. This will be very similar to the Facebook authentication so if you want to try to figure out Twitter authentication, I’d definitely encourage it. Until next time!

Chris Sevilleja

Co-founder of Scotch.io. Slapping the keyboard until something good happens.