Tutorial

Easy Node Authentication: Facebook

Draft updated on Invalid Date
Default avatar

By Chris on Code

Easy Node Authentication: Facebook

This tutorial is out of date and no longer maintained.

Note: This article is part of our Easy Node Authentication series.

Introduction

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.

Note: Edit 11/18/2017: Updated to reflect Facebook API changes.

  • 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.

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',
            'profileURL'    : 'https://graph.facebook.com/v2.5/me?fields=first_name,last_name,email',
            'profileFields' : ['id', 'email', 'name'] // For requesting permissions from Facebook API
        },

        '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 : ['public_profile', '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 and 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.

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.

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:

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.

Conclusion

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!

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Chris on Code

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel