Implementing Facebook Authentication With Node.js

In this small project we'll see how to implement Facebook authentication for your Node app with a help of Passport.js. Facebook authentication might sound like a lot of work but with the help of Passport, it is a surprisingly easy task. To give you a good feeling about the volume of code that you're about to write: It is about 80 lines of JavaScript and couple more lines of HTML. Before we start let's see how our application will look like. This is a truely minimalistic app with two pages: a login page and a "secret" page that only authenticated users are allowed to see. We'll let users authenticate with Facebook and then see the page. Reloading the page will keep the "logged in" status as user data is preserved in a "mini-database" (an in-memory object). Let's start from setting up the dependencies. In this project we will need 'express', 'express-session', 'cookie', 'cookie-parser', 'ejs', 'passport' and 'passport-facebook'. Having them installed, let's configure our basic express application.
  1. const express  = require('express');
  2. const passport = require('passport');
  3. const cookieParser = require('cookie-parser');
  4. const session = require('express-session');
  5.  
  6. const app = express();
  7.  
  8. app.use(cookieParser());
  9. app.set('view engine', 'ejs');
  10. app.use(session({
  11.   secret: 'keyboardcat',
  12.   resave: true,
  13.   saveUninitialized : true
  14. }));
  15.  
  16. /******* Routes *********/
  17.  
  18. app.get('/', (req, res) => res.render('index.ejs'));
  19. app.get('/secret', secured, (req, res) => res.render('secret.ejs'));
  20.  
  21. function secured(req, res, next) {
  22.   console.log('You shall not pass!');
  23.   res.redirect('/');
  24. }
  25.  
  26. app.listen(8080, () => console.log('Ready on 8080'));
This is a fairly standard app that has sessions, cookies and EJS templates, together with two pages: an index page that is accessibe to everybody and /secret page that nobody can go to, because our dedicated secured() function redirects all requests to index, printing famous Gandalf's words to console. To keep the listing complete, here are our EJS views. index.ejs:
  1. <!doctype html>
  2.   <meta charset="utf-8">
  3.   <title>Auth Demo</title>
  4. </head>
  5.   <div class="container">
  6.     <a href="/fb">Facebook Login</a>
  7.   </div>
  8. </body>
  9. </html>
and secret.ejs:
  1. <!doctype html>
  2.   <meta charset="utf-8">
  3.   <title>Auth Demo</title>
  4. </head>
  5.   secret page
  6. </body>
  7. </html>
You can try this app now to make sure it works and you are on a right track. Now, let's make it real Facebook-friendly application with some Passport magic. First thing you will have to do is go to https://developers.facebook.com and register your application. You will need to complete this step, find your Application ID, secret key and set up callback URL to http://localhost:8080/fb-callback (find it under "Facebook Auth" menu item). Once you have the keys, you're ready to write the rest of the code. Let's start from setting up our "User Database" and adding Passport.
  1. const passport = require('passport');
  2. const FacebookStrategy = require('passport-facebook').Strategy;
  3.  
  4. /* express.js setup code here */
  5.  
  6. app.use(passport.initialize());
  7. app.use(passport.session());
  8.  
  9. /******** Mini user DB **************/
  10. let maxId = 0;
  11.  
  12. const users = {
  13. };
  14.  
  15. function newUser(fbId) {
  16.   const id = ++maxId;
  17.   return users[id] = { id, fbId };
  18. }
  19.  
  20. /******** SETTING UP PASSPORT *************/
  21.  
  22. passport.serializeUser((user, done) => done(null, user.id));
  23.  
  24. passport.deserializeUser((id, done) => {
  25.   setImmediate(() => {
  26.     done(null, users[id]);
  27.   })
  28. });
This code does few things. Firstly, we add Passport to the express middleware stack. Passport will work together with session middleware to find user ID (if present), find user in database and put user object into a session. This whole process is orchestrated by two functions that we define: serializeUser() and deserializeUser(). These functions "know" how to find user in DB and which part of User object is a "key" that can be later used to perform search. This code is very simple as we are using a JS object as a DB. In a real-world scenario you would probably put here your MongoDB or PostgreSQL code. Now, that Passport.js is in place we can re-write our "security middleware".
  1. function secured(req, res, next) {
  2.   if (req.isAuthenticated()) {
  3.     console.log('This user is OK to see secrets');
  4.     return next();
  5.   } else {
  6.     console.log('You shall not pass!');
  7.     res.redirect('/');
  8.   }
  9. }
req.isAuthenticated() is a new function that Passport.js adds to the request object. Since passport only knows how to save users (but not how to authenticate them), this function is still not letting anybody in. However once we finish our Passport.js setup, it will work! The last step is to set up the routes required for authentication together with passport's Facebook strategy.
  1. app.get('/fb', passport.authenticate('facebook', { scope : 'email' }));
  2.  
  3. app.get('/fb-callback', passport.authenticate('facebook', {
  4.   successRedirect : '/secret',
  5.   failureRedirect : '/'
  6. }));
  7.  
  8. passport.use(new FacebookStrategy({
  9.     clientID : '<client-id>',
  10.     clientSecret : '<client-secret>',
  11.     callbackURL : 'http://localhost:8080/fb-callback'
  12.   },
  13.   (token, refreshToken, profile, done) => {
  14.     const fbId = profile.id;
  15.     done(null, newUser(fbId));
  16.     console.log('Authenticated with Facebook. FB ID is', fbId);
  17.   }));
That's it! Once you configured the Facebook strategy Passport will take care to redirect browser to Facebook and retrieve the information from Facebook once user confirms access. Afterwards, you will receive an object called "profile" that contains the information that you requested. At this point you can count your user authenticated.

Add new comment