Lesson 5: Managing User Authentication Using Passport

Passport is a robust Node.js authentication middleware that helps you to authenticate requests sent to your Express application. Passport uses strategies to utilize both local authentication and OAuth authentication providers, such as Facebook, Twitter, and Google. Using Passport strategies, you'll be able to seamlessly offer different authentication options to your users while maintaining a unified User model. In this Lesson, you'll go through the following basic features of Passport:

  • Understanding Passport strategies
  • Integrating Passport into your users' MVC architecture
  • Using Passport's local strategy to authenticate users
  • Utilizing Passport OAuth strategies
  • Offering authentication through social OAuth providers

Introducing Passport

Authentication is a vital part of most web applications. Handling user registration and sign-in is an important feature, which can sometimes present a development overhead. Express, with its lean approach, lacks this feature, so, as is usual with node, an external module is needed. Passport is a Node.js module that uses the middleware design pattern to authenticate requests. It allows developers to offer various authentication methods using a mechanism called strategies, which allows you to implement a complex authentication layer while keeping your code clean and simple. Just as with any other Node.js module, before you can start using it in your application, you will first need to install it. The examples in this Lesson will continue directly from those in previous Lessons. So for this Lesson, copy the final example from Lesson 4, Introduction to Mongoose, and let's start from there.

Installing Passport

Passport uses different modules, each representing a different authentication strategy, but all of which depend on the base Passport module. To install the Passport base module in your application's modules folders, change your package.json file as follows:

{
  "name": "MEAN",
  "version": "0.0.6",
  "dependencies": {
    "express": "~4.8.8",
    "morgan": "~1.3.0",
    "compression": "~1.0.11",
    "body-parser": "~1.8.0",
    "method-override": "~2.2.0",
    "express-session": "~1.7.6",
    "ejs": "~1.0.0",
    "mongoose": "~3.8.15",
    "passport": "~0.2.1"
  }
}

Before you continue developing your application, make sure you install the new Passport dependency. To do so, go to your application's folder, and issue the following command in your command-line tool:

$ npm install

This will install the specified version of Passport in your node_modules folder. Once the installation process has successfully finished, you will need to configure your application to load the Passport module.

Configuring Passport

To configure Passport, you will need to set it up in a few steps. To create the Passport configuration file, go to the config folder and create a new file named passport.js. Leave it empty for now; we will return to it in a bit. Next, you'll need to require the file you just created, so change your server.js file, as follows:

process.env.NODE_ENV = process.env.NODE_ENV || 'development';

var mongoose = require('./config/mongoose'),
    express = require('./config/express'),
    passport = require('./config/passport');

var db = mongoose();
var app = express();
var passport = passport();

app.listen(3000);

module.exports = app;

console.log('Server running at http://localhost:3000/');

Next, you'll need to register the Passport middleware in your Express application. To do so, change your config/express.js file, as follows:

var config = require('./config'),
    express = require('express'),
    morgan = require('morgan'),
    compress = require('compression'),
    bodyParser = require('body-parser'),
    methodOverride = require('method-override'),
    session = require('express-session'),
    passport = require('passport');

module.exports = function() {
  var app = express();

  if (process.env.NODE_ENV === 'development') {
    app.use(morgan('dev'));
  } else if (process.env.NODE_ENV === 'production') {
    app.use(compress());
  }

  app.use(bodyParser.urlencoded({
    extended: true
  }));
  app.use(bodyParser.json());
  app.use(methodOverride());

  app.use(session({
    saveUninitialized: true,
    resave: true,
    secret: config.sessionSecret
  }));
  app.set('views', './app/views');
  app.set('view engine', 'ejs');

  app.use(passport.initialize());
  app.use(passport.session());

  require('../app/routes/index.server.routes.js')(app);
  require('../app/routes/users.server.routes.js')(app);

  app.use(express.static('./public'));

  return app;
};

Let's go over the code you just added. First, you required the Passport module, and then you registered two middleware: the passport.initialize() middleware, which is responsible for bootstrapping the Passport module and the passport.session() middleware, which is using the Express session to keep track of your user's session.

Passport is now installed and configured, but to start using it, you will have to install at least one authentication strategy. We'll begin with the local strategy, which provides a simple username/password authentication layer; but first, let's discuss how Passport strategies work.

Understanding Passport strategies

To offer its various authentication options, Passport uses separate modules that implement different authentication strategies. Each module provides a different authentication method, such as username/password authentication and OAuth authentication. So, in order to offer Passport-supported authentication, you'll need to install and configure the strategies modules that you'd like to use. Let's begin with the local authentication strategy.

Using Passport's local strategy

Passport's local strategy is a Node.js module that allows you to implement a username/password authentication mechanism. You'll need to install it like any other module and configure it to use your User Mongoose model. Let's begin by installing the local strategy module.

Installing Passport's local strategy module

To install Passport's local strategy module, you'll need to change your package.json file, as follows:

{
  "name": "MEAN",
  "version": "0.0.6",
  "dependencies": {
    "express": "~4.8.8",
    "morgan": "~1.3.0",
    "compression": "~1.0.11",
    "body-parser": "~1.8.0",
    "method-override": "~2.2.0",
    "express-session": "~1.7.6",
    "ejs": "~1.0.0",
    "mongoose": "~3.8.15",
    "passport": "~0.2.1",
    "passport-local": "~1.0.0"
  }
}

Then, go to your application's root folder, and issue the following command in your command-line tool:

$ npm install

This will install the specified version of the local strategy module in your node_modules folder. When the installation process has successfully finished, you'll need to configure Passport to use the local strategy.

Configuring Passport's local strategy

Each authentication strategy you'll use is basically a node module that lets you define how that strategy will be used. In order to maintain a clear separation of logic, each strategy should be configured in its own separated file. In your config folder, create a new folder named strategies. Inside this new folder, create a file named local.js that contains the following code snippet:

var passport = require('passport'),
    LocalStrategy = require('passport-local').Strategy,
    User = require('mongoose').model('User');

module.exports = function() {
  passport.use(new LocalStrategy(function(username, password, done) {
    User.findOne({
      username: username
    }, function(err, user) {
      if (err) {
        return done(err);
      }
      
      if (!user) {
        return done(null, false, {
          message: 'Unknown user'
        });
      }
      if (!user.authenticate(password)) {
        return done(null, false, {
          message: 'Invalid password'
        });
      }
      
      return done(null, user);
      });
  }));
};

The preceding code begins by requiring the Passport module, the local strategy module's Strategy object, and your User Mongoose model. Then, you register the strategy using the passport.use() method that uses an instance of the LocalStrategy object. Notice how the LocalStrategy constructor takes a callback function as an argument. It will later call this callback when trying to authenticate a user.

The callback function accepts three arguments—username, password, and a done callback—which will be called when the authentication process is over. Inside the callback function, you will use the User Mongoose model to find a user with that username and try to authenticate it. In the event of an error, you will pass the error object to the done callback. When the user is authenticated, you will call the done callback with the user Mongoose object.

Remember the empty config/passport.js file? Well, now that you have your local strategy ready, you can go back and use it to configure the local authentication. To do so, go back to your config/passport.js file and paste the following lines of code:

var passport = require('passport'),
    mongoose = require('mongoose');

module.exports = function() {
  var User = mongoose.model('User');
  
  passport.serializeUser(function(user, done) {
    done(null, user.id);
  });

  passport.deserializeUser(function(id, done) {
    User.findOne({
      _id: id
    }, '-password -salt', function(err, user) {
      done(err, user);
    });
  });

  require('./strategies/local.js')();
};

In the preceding code snippet, the passport.serializeUser() and passport.deserializeUser() methods are used to define how Passport will handle user serialization. When a user is authenticated, Passport will save its _id property to the session. Later on when the user object is needed, Passport will use the _id property to grab the user object from the database. Notice how we used the field options argument to make sure Mongoose doesn't fetch the user's password and salt properties. The second thing the preceding code does is including the local strategy configuration file. This way, your server.js file will load the Passport configuration file, which in turn will load its strategies configuration file. Next, you'll need to modify your User model to support Passport's authentication.

Adapting the User model

In the previous Lesson, we started discussing the User model and created its basic structure. In order to use the User model in your MEAN application, you'll have to modify it to address a few authentication process requirements. These changes will include modifying UserSchema, adding a pre middleware, and adding some new instance methods. To do so, go to your app/models/user.js file, and change it as follows:

var mongoose = require('mongoose'),
    crypto = require('crypto'),
    Schema = mongoose.Schema;

var UserSchema = new Schema({
  firstName: String,
  lastName: String,
  email: {
    type: String,
    match: [/.+@.+..+/, "Please fill a valid e-mail address"]
  },
  username: {
    type: String,
    unique: true,
    required: 'Username is required',
    trim: true
  },
  password: {
    type: String,
    validate: [
    function(password) {
        return password && password.length > 6;
      }, 'Password should be longer'
    ]
  },
  salt: {
    type: String
  },
  provider: {
    type: String,
    required: 'Provider is required'
  },
  providerId: String,
  providerData: {},
  created: {
    type: Date,
    default: Date.now
  }
});

UserSchema.virtual('fullName').get(function() {
  return this.firstName + ' ' + this.lastName;
}).set(function(fullName) {
  var splitName = fullName.split(' ');
  this.firstName = splitName[0] || '';
  this.lastName = splitName[1] || '';
});

UserSchema.pre('save', function(next) {
  if (this.password) {
    this.salt = new Buffer(crypto.randomBytes(16).toString('base64'), 'base64');
    this.password = this.hashPassword(this.password);
  }

  next();
});

UserSchema.methods.hashPassword = function(password) {
  return crypto.pbkdf2Sync(password, this.salt, 10000, 64).toString('base64');
};

UserSchema.methods.authenticate = function(password) {
  return this.password === this.hashPassword(password);
};

UserSchema.statics.findUniqueUsername = function(username, suffix, callback) {
  var _this = this;
  var possibleUsername = username + (suffix || '');

  _this.findOne({
    username: possibleUsername
  }, function(err, user) {
    if (!err) {
      if (!user) {
        callback(possibleUsername);
      } else {
        return _this.findUniqueUsername(username, (suffix || 0) + 1, callback);
      }
    } else {
      callback(null);
    }
  });
};

UserSchema.set('toJSON', {
  getters: true,
  virtuals: true
});

mongoose.model('User', UserSchema);

Let's go over these changes. First, you added four fields to your UserSchema object: a salt property, which you'll use to hash your password; a provider property, which will indicate the strategy used to register the user; a providerId property, which will indicate the user identifier for the authentication strategy; and a providerData property, which you'll later use to store the user object retrieved from OAuth providers.

Next, you created a pre-save middleware to handle the hashing of your users' passwords. It is widely known that storing a clear text version of your users' passwords is a very bad practice that can result in the leakage of your users' passwords. To handle this issue, your pre-save middleware performs two important steps: first, it creates an autogenerated pseudo-random hashing salt, and then it replaces the current user password with a hashed password using the hashPassword() instance method.

You also added two instance methods: a hashPassword() instance method, which is used to hash a password string by utilizing Node.js' crypto module, and an authenticate() instance method, which accepts a string argument, hashes it, and compares it to the current user's hashed password. Finally, you added the findUniqueUsername() static method, which is used to find an available unique username for new users. You'll use this method later in this Lesson when you deal with OAuth authentication.

That completes the modifications in your User model, but there are a few other things to care of before you can test your application's authentication layer.

Creating the authentication views

Just as with any web application, you will need to have signup and sign-in pages in order to handle user authentication. We'll create those views using the EJS template engine, so in your app/views folder, create a new file named signup.ejs. In your newly created file, paste the following code snippet:

<!DOCTYPE html>
<html>
<head>
  <title>
    <%=title %>
  </title>
</head>
<body>
  <% for(var i in messages) { %>
    <div class="flash"><%= messages[i] %></div>
  <% } %>
  <form action="/signup" method="post">
    <div>
      <label>First Name:</label>
      <input type="text" name="firstName" />
    </div>
    <div>
      <label>Last Name:</label>
      <input type="text" name="lastName" />
    </div>
    <div>
      <label>Email:</label>
      <input type="text" name="email" />
    </div>
    <div>
      <label>Username:</label>
      <input type="text" name="username" />
    </div>
    <div>
      <label>Password:</label>
      <input type="password" name="password" />
    </div>
    <div>
      <input type="submit" value="Sign up" />
    </div>
  </form>
</body>
</html>

The signup.ejs view simply contains an HTML form, an EJS tag, which renders the title variable, and an EJS loop, which renders the messages list variable. Go back to your app/views folder, and create another file named signin.ejs. Inside this file, paste the following code snippet:

<!DOCTYPE html>
<html>
<head>
  <title>
    <%=title %>
  </title>
</head>
<body>
  <% for(var i in messages) { %>
    <div class="flash"><%= messages[i] %></div>
  <% } %>
  <form action="/signin" method="post">
    <div>
      <label>Username:</label>
      <input type="text" name="username" />
    </div>
    <div>
      <label>Password:</label>
      <input type="password" name="password" />
    </div>
    <div>
      <input type="submit" value="Sign In" />
    </div>
  </form>
</body>
</html>

As you can notice, the signin.ejs view is even simpler and also contains an HTML form, an EJS tag, which renders the title variable, and an EJS loop, which renders the messages list variable. Now that you have your model and views set, it's time to connect them using your Users controller.

Modifying the user controller

To alter the Users controller, go to your app/controllers/users.server.controller.js file, and change its content, as follows:

var User = require('mongoose').model('User'),
  passport = require('passport');

var getErrorMessage = function(err) {
  var message = '';

  if (err.code) {
    switch (err.code) {
      case 11000:
      case 11001:
        message = 'Username already exists';
        break;
      default:
        message = 'Something went wrong';
    }
  } else {
    for (var errName in err.errors) {
      if (err.errors[errName].message) message = err.errors[errName].message;
    }
  }

  return message;
};

exports.renderSignin = function(req, res, next) {
  if (!req.user) {
    res.render('signin', {
      title: 'Sign-in Form',
      messages: req.flash('error') || req.flash('info')
    });
  } else {
    return res.redirect('/');
  }
};
exports.renderSignup = function(req, res, next) {
  if (!req.user) {
    res.render('signup', {
      title: 'Sign-up Form',
      messages: req.flash('error')
    });
  } else {
    return res.redirect('/');
  }
};

exports.signup = function(req, res, next) {
  if (!req.user) {
    var user = new User(req.body);
    var message = null;

    user.provider = 'local';

    user.save(function(err) {
      if (err) {
        var message = getErrorMessage(err);

        req.flash('error', message);
        return res.redirect('/signup');
      }
      req.login(user, function(err) {
        if (err) return next(err);
        return res.redirect('/');
      });
    });
  } else {
    return res.redirect('/');
  }
};

exports.signout = function(req, res) {
  req.logout();
  res.redirect('/');
};

The getErrorMessage() method is a private method that returns a unified error message from a Mongoose error object. It is worth noticing that there are two possible errors here: a MongoDB indexing error handled using the error code and a Mongoose validation error handled using the err.errors object.

The next two controller methods are quite simple and will be used to render the sign-in and signup pages. The signout() method is also simple and uses the req.logout() method, which is provided by the Passport module to invalidate the authenticated session.

The signup() method uses your User model to create new users. As you can see, it first creates a user object from the HTTP request body. Then, try saving it to MongoDB. If an error occurs, the signup() method will use the getErrorMessage() method to provide the user with an appropriate error message. If the user creation was successful, the user session will be created using the req.login() method. The req.login() method is exposed by the Passport module and is used to establish a successful login session. After the login operation is completed, a user object will be signed to the req.user object.

Note

The req.login() will be called automatically while using the passport.authenticate() method, so a manual call for req.login() is primarily used when registering new users.

In the preceding code though, a module you're not yet familiar with is used. When an authentication process is failing, it is common to redirect the request back to the signup or sign-in pages. This is done here when an error occurs, but how can your user tell what exactly went wrong? The problem is that when redirecting to another page, you cannot pass variables to that page. The solution would be to use some sort of mechanism to pass temporary messages between requests. Fortunately, that mechanism already exists in the form of a node module named Connect-Flash.

Displaying flash error messages

The Connect-Flash module is a node module that allows you to store temporary messages in an area of the session object called flash. Messages stored on the flash object will be cleared once they are presented to the user. This architecture makes the Connect-Flash module perfect to transfer messages before redirecting the request to another page.

Installing the Connect-Flash module

To install the Connect-Flash module in your application's modules folders, you'll need to change your package.json file, as follows:

{
  "name": "MEAN",
  "version": "0.0.6",
  "dependencies": {
    "express": "~4.8.8",
    "morgan": "~1.3.0",
    "compression": "~1.0.11",
    "body-parser": "~1.8.0",
    "method-override": "~2.2.0",
    "express-session": "~1.7.6",
    "ejs": "~1.0.0",
    "connect-flash": "~0.1.1",
    "mongoose": "~3.8.15",
    "passport": "~0.2.1",
    "passport-local": "~1.0.0"
  }
}

As usual, before you can continue developing your application, you will need to install your new dependency. Go to your application's folder, and issue the following command in your command-line tool:

$ npm install

This will install the specified version of the Connect-Flash module in your node_modules folder. When the installation process is successfully finished, your next step would be to configure your Express application to use the Connect-Flash module.

Configuring Connect-Flash module

To configure your Express application to use the new Connect-Flash module, you'll have to require the new module in your Express configuration file and use the app.use() method to register it with your Express application. To do so, make the following changes in your config/express.js file:

var config = require('./config'),
    express = require('express'),
    morgan = require('morgan'),
    compress = require('compression'),
    bodyParser = require('body-parser'),
    methodOverride = require('method-override'),
    session = require('express-session'),
    flash = require('connect-flash'),
    passport = require('passport');

module.exports = function() {
  var app = express();

  if (process.env.NODE_ENV === 'development') {
    app.use(morgan('dev'));
  } else if (process.env.NODE_ENV === 'production') {
    app.use(compress());
  }

  app.use(bodyParser.urlencoded({
    extended: true
  }));
  app.use(bodyParser.json());
  app.use(methodOverride());

  app.use(session({
    saveUninitialized: true,
    resave: true,
    secret: config.sessionSecret
  }));

  app.set('views', './app/views');
  app.set('view engine', 'ejs');

  app.use(flash());
  app.use(passport.initialize());
  app.use(passport.session());

  require('../app/routes/index.server.routes.js')(app);
  require('../app/routes/users.server.routes.js')(app);

  app.use(express.static('./public'));

  return app;
};

This will tell your Express application to use Connect-Flash and create the new flash area in the application session.

Using Connect-Flash module

Once installed, the Connect-Flash module exposes the req.flash() method, which allows you to create and retrieve flash messages. To understand it better, let's observe the changes you've made to your Users controller. First, let's take a look at the renderSignup() and renderSignin() methods, which are responsible for rendering the sign-in and signup pages:

exports.renderSignin = function(req, res, next) {
  if (!req.user) {
    res.render('signin', {
      title: 'Sign-in Form',
      messages: req.flash('error') || req.flash('info')
    });
  } else {
    return res.redirect('/');
  }
};

exports.renderSignup = function(req, res, next) {
  if (!req.user) {
    res.render('signup', {
      title: 'Sign-up Form',
      messages: req.flash('error')
    });
  } else {
    return res.redirect('/');
  }
};

As you can see, the res.render() method is executed with the title and messages variables. The messages variable uses req.flash() to read the messages written to the flash. Now if you'll go over the signup() method, you'll notice the following line of code:

req.flash('error', message);

This is how error messages are written to the flash, again using the req.flash() method. After you learned how to use the Connect-Flash module, you might have noticed that we're lacking a signin() method. This is because Passport provides you with an authentication method, which you can use directly in your routing definition. To wrap up, let's proceed to the last part that needs to be modified: the Users routing definition file.

Wiring the user's routes

Once you have your model, controller, and views configured, all that is left to do is define the user's routes. To do so, make the following changes in your app/routes/users.server.routes.js file:

var users = require('../../app/controllers/users.server.controller'),
    passport = require('passport');

module.exports = function(app) {
  app.route('/signup')
     .get(users.renderSignup)
     .post(users.signup);

  app.route('/signin')
     .get(users.renderSignin)
     .post(passport.authenticate('local', {
       successRedirect: '/',
       failureRedirect: '/signin',
       failureFlash: true
     }));

  app.get('/signout', users.signout);
};

As you can notice, most of the routes definitions here are basically directing to methods from your user controller. The only different route definition is the one where you're handling any POST request made to the /signin path using the passport.authenticate() method.

When the passport.authenticate() method is executed, it will try to authenticate the user request using the strategy defined by its first argument. In this case, it will try to authenticate the request using the local strategy. The second parameter this method accepts is an options object, which contains three properties:

  • successRedirect: This property tells Passport where to redirect the request once it successfully authenticated the user
  • failureRedirect: This property tells Passport where to redirect the request once it failed to authenticate the user
  • failureFlash: This property tells Passport whether or not to use flash messages

You've almost completed the basic authentication implementation. To test it out, make the following changes to the app/controllers/index.server.controller.js file:

exports.render = function(req, res) {
  res.render('index', {
    title: 'Hello World',
    userFullName: req.user ? req.user.fullName : ''
  });
};

This will pass the authenticated user's full name to your home page template. You will also have to make the following changes in your app/views/index.ejs file:

<!DOCTYPE html>
<html>
  <head>
      <title><%= title %></title>
    </head>
    <body>
      <% if ( userFullName ) { %>
        <h2>Hello <%=userFullName%> </h2> 
        <a href="/signout">Sign out</a>
      <% } else { %>
        <a href="/signup">Signup</a>
        <a href="/signin">Signin</a>
    <% } %>
    <br>
      <img src="img/logo.png" alt="Logo">
    </body>
</html>

That's it! Everything is ready to test your new authentication layer. Go to your root application folder and use the node command-line tool to run your application:

$ node server

Test your application by visiting http://localhost:3000/signin and http://localhost:3000/signup. Try signing up, and then sign in and don't forget to go back to your home page to see how the user details are saved through the session.

Understanding Passport OAuth strategies

OAuth is an authentication protocol that allows users to register with your web application using an external provider, without the need to input their username and password. OAuth is mainly used by social platforms, such as Facebook, Twitter, and Google, to allow users to register with other websites using their social account.

Note

To learn more about how OAuth works, visit the OAuth protocol website at http://oauth.net/.

Setting up OAuth strategies

Passport support the basic OAuth strategy, which enables you to implement any OAuth-based authentication. However, it also supports a user authentication through major OAuth providers using wrapper strategies that help you avoid the need to implement a complex mechanism by yourself. In this section, we'll review the top OAuth providers and how to implement their Passport authentication strategy.

Note

Before you begin, you will have to contact the OAuth provider and create a developer application. This application will have both an OAuth client ID and an OAuth client secret, which will allow you to verify your application against the OAuth provider.

Handling OAuth user creation

The OAuth user creation should be a bit different than the local signup() method. Since users are signing up using their profile from other providers, the profile details are already present, which means you will need to validate them differently. To do so, go back to your app/controllers/users.server.controller.js file, and add the following module method:

exports.saveOAuthUserProfile = function(req, profile, done) {
  User.findOne({
    provider: profile.provider,
    providerId: profile.providerId
  }, function(err, user) {
    if (err) {
      return done(err);
    } else {
      if (!user) {
        var possibleUsername = profile.username || ((profile.email) ? profile.email.split('@')[0] : '');

        User.findUniqueUsername(possibleUsername, null, function(availableUsername) {
          profile.username = availableUsername;

          user = new User(profile);

          user.save(function(err) {
            if (err) {
              var message = _this.getErrorMessage(err);

              req.flash('error', message);
              return res.redirect('/signup');
            }

            return done(err, user);
          });
        });
      } else {
        return done(err, user);
      }
    }
  });
};

This method accepts a user profile, and then looks for an existing user with these providerId and provider properties. If it finds the user, it calls the done() callback method with the user's MongoDB document. However, if it cannot find an existing user, it will find a unique username using the User model's findUniqueUsername() static method and save a new user instance. If an error occurs, the saveOAuthUserProfile() method will use the req.flash() and getErrorMessage() methods to report the error; otherwise, it will pass the user object to the done() callback method. Once you have figured out the saveOAuthUserProfile() method, it is time to implement the first OAuth authentication strategy.

Using Passport's Facebook strategy

Facebook is probably the world's largest OAuth provider. Many modern web applications offer their users the ability to register with the web application using their Facebook profile. Passport supports Facebook OAuth authentication using the passport-facebook module. Let's see how you can implement a Facebook-based authentication in a few simple steps.

Installing Passport's Facebook strategy

To install Passport's Facebook module in your application's modules folders, you'll need to change your package.json file as follows:

{
  "name": "MEAN",
  "version": "0.0.6",
  "dependencies": {
    "express": "~4.8.8",
    "morgan": "~1.3.0",
    "compression": "~1.0.11",
    "body-parser": "~1.8.0",
    "method-override": "~2.2.0",
    "express-session": "~1.7.6",
    "ejs": "~1.0.0",
    "connect-flash": "~0.1.1",
    "mongoose": "~3.8.15",
    "passport": "~0.2.1",
    "passport-local": "~1.0.0",
    "passport-facebook": "~1.0.3"
  }
}

Before you can continue developing your application, you will need to install the new Facebook strategy dependency. To do so, go to your application's root folder, and issue the following command in your command-line tool:

$ npm install

This will install the specified version of Passport's Facebook strategy in your node_modules folder. Once the installation process has successfully finished, you will need to configure the Facebook strategy.

Configuring Passport's Facebook strategy

Before you begin configuring your Facebook strategy, you will have to go to Facebook's developer home page at https://developers.facebook.com/, create a new Facebook application, and set the local host as the application domain. After configuring your Facebook application, you will get a Facebook application ID and secret. You'll need those to authenticate your users via Facebook, so let's save them in our environment configuration file. Go to the config/env/development.js file and change it as follows:

module.exports = {
  db: 'mongodb://localhost/mean-book',
  sessionSecret: 'developmentSessionSecret',
  facebook: {
    clientID: 'Application Id',
    clientSecret: 'Application Secret',
    callbackURL: 'http://localhost:3000/oauth/facebook/callback'
  }
};

Don't forget to replace Application Id and Application Secret with your Facebook application's ID and secret. The callbackURL property will be passed to the Facebook OAuth service, which will redirect to that URL after the authentication process is over.

Now, go to your config/strategies folder, and create a new file named facebook.js that contains the following code snippet:

var passport = require('passport'),
    url = require('url'),
    FacebookStrategy = require('passport-facebook').Strategy,
    config = require('../config'),
    users = require('../../app/controllers/users.server.controller');

module.exports = function() {
  passport.use(new FacebookStrategy({
    clientID: config.facebook.clientID,
    clientSecret: config.facebook.clientSecret,
    callbackURL: config.facebook.callbackURL,
    passReqToCallback: true
  },
  function(req, accessToken, refreshToken, profile, done) {
    var providerData = profile._json;
    providerData.accessToken = accessToken;
    providerData.refreshToken = refreshToken;

    var providerUserProfile = {
      firstName: profile.name.givenName,
      lastName: profile.name.familyName,
      fullName: profile.displayName,
      email: profile.emails[0].value,
      username: profile.username,
      provider: 'facebook',
      providerId: profile.id,
      providerData: providerData
    };

    users.saveOAuthUserProfile(req, providerUserProfile, done);
  }));
};

Let's go over the preceding code snippet for a moment. You begin by requiring the Passport module, the Facebook Strategy object, your environmental configuration file, your User Mongoose model, and the Users controller. Then, you register the strategy using the passport.use() method and creating an instance of a FacebookStrategy object. The FacebookStrategy constructor takes two arguments: the Facebook application information and a callback function that it will call later when trying to authenticate a user.

Take a look at the callback function you defined. It accepts five arguments: the HTTP request object, an accessToken object to validate future requests, a refreshToken object to grab new access tokens, a profile object containing the user profile, and a done callback to be called when the authentication process is over.

Inside the callback function, you will create a new user object using the Facebook profile information and the controller's saveOAuthUserProfile() method, which you previously created, to authenticate the current user.

Remember the config/passport.js file? Well, now that you have your Facebook strategy configured, you can go back to it and load the strategy file. To do so, go back to the config/passport.js file and change it, as follows:

var passport = require('passport'),
    mongoose = require('mongoose');

module.exports = function() {
  var User = mongoose.model('User');
  
  passport.serializeUser(function(user, done) {
    done(null, user.id);
  });

  passport.deserializeUser(function(id, done) {
    User.findOne({
      _id: id
    }, '-password -salt', function(err, user) {
      done(err, user);
    });
  });

  require('./strategies/local.js')();
  require('./strategies/facebook.js')();
};

This will load your Facebook strategy configuration file. Now, all that is left to do is set the routes needed to authenticate users via Facebook and include a link to those routes in your sign-in and signup pages.

Wiring Passport's Facebook strategy routes

Passport OAuth strategies support the ability to authenticate users directly using the passport.authenticate() method. To do so, go to app/routes/users.server.routes.js, and append the following lines of code after the local strategy routes definition:

app.get('/oauth/facebook', passport.authenticate('facebook', {
  failureRedirect: '/signin'
}));
app.get('/oauth/facebook/callback', passport.authenticate('facebook', {
  failureRedirect: '/signin',
  successRedirect: '/'
}));

The first route will use the passport.authenticate() method to start the user authentication process, while the second route will use the passport.authenticate() method to finish the authentication process once the user has linked their Facebook profile.

That's it! Everything is set up for your users to authenticate via Facebook. All you have to do now is go to your app/views/signup.ejs and app/views/signin.ejs files, and add the following line of code right before the closing BODY tag:

<a href="/oauth/facebook">Sign in with Facebook</a>

This will allow your users to click on the link and register with your application via their Facebook profile.

Using Passport's Twitter strategy

Another popular OAuth provider is Twitter, and a lot of web applications offer their users the ability to register with the web application using their Twitter profile. Passport supports the Twitter OAuth authentication method using the passport-twitter module. Let's see how you can implement a Twitter-based authentication in a few simple steps.

Installing Passport's Twitter strategy

To install Passport's Twitter strategy module in your application's modules folders, you'll need to change your package.json file, as follows:

{
  "name": "MEAN",
  "version": "0.0.6",
  "dependencies": {
    "express": "~4.8.8",
    "morgan": "~1.3.0",
    "compression": "~1.0.11",
    "body-parser": "~1.8.0",
    "method-override": "~2.2.0",
    "express-session": "~1.7.6",
    "ejs": "~1.0.0",
    "connect-flash": "~0.1.1",
    "mongoose": "~3.8.15",
    "passport": "~0.2.1",
    "passport-local": "~1.0.0",
    "passport-facebook": "~1.0.3",
    "passport-twitter": "~1.0.2"
  }
}

Before you continue developing your application, you will need to install the new Twitter strategy dependency. Go to your application's root folder, and issue the following command in your command-line tool:

$ npm install

This will install the specified version of Passport's Twitter strategy in your node_modules folder. Once the installation process has successfully finished, you will need to configure the Twitter strategy.

Configuring Passport's Twitter strategy

Before we begin configuring your Twitter strategy, you will have to go to the Twitter developers' home page at https://dev.twitter.com/ and create a new Twitter application. After configuring your Twitter application, you will get a Twitter application ID and secret. You'll need them to authenticate your users via Twitter, so let's add them in our environment configuration file. Go to the config/env/development.js file, and change it as follows:

module.exports = {
  db: 'mongodb://localhost/mean-book',
  sessionSecret: 'developmentSessionSecret',
  facebook: {
    clientID: 'Application Id',
    clientSecret: 'Application Secret',
    callbackURL: 'http://localhost:3000/oauth/facebook/callback'
  },
  twitter: {
    clientID: 'Application Id',
    clientSecret: 'Application Secret',
    callbackURL: 'http://localhost:3000/oauth/twitter/callback'
  }
};

Don't forget to replace Application Id and Application Secret with your Twitter application's ID and secret. The callbackURL property will be passed to the Twitter OAuth service, which will redirect the user to that URL after the authentication process is over.

As stated earlier, in your project, each strategy should be configured in its own separated file, which will help you keep your project organized. Go to your config/strategies folder, and create a new file named twitter.js containing the following lines of code:

var passport = require('passport'),
    url = require('url'),
    TwitterStrategy = require('passport-twitter').Strategy,
    config = require('../config'),
    users = require('../../app/controllers/users.server.controller');

module.exports = function() {
  passport.use(new TwitterStrategy({
    consumerKey: config.twitter.clientID,
    consumerSecret: config.twitter.clientSecret,
    callbackURL: config.twitter.callbackURL,
    passReqToCallback: true
  },
  function(req, token, tokenSecret, profile, done) {
    var providerData = profile._json;
    providerData.token = token;
    providerData.tokenSecret = tokenSecret;

    var providerUserProfile = {
      fullName: profile.displayName,
      username: profile.username,
      provider: 'twitter',
      providerId: profile.id,
      providerData: providerData
    };

    users.saveOAuthUserProfile(req, providerUserProfile, done);
  }));
};

You begin by requiring the Passport module, the Twitter Strategy object, your environmental configuration file, your User Mongoose model, and the Users controller. Then, you register the strategy using the passport.use() method, and create an instance of a TwitterStrategy object. The TwitterStrategy constructor takes two arguments: the Twitter application information and a callback function that it will call later when trying to authenticate a user.

Take a look at the callback function you defined. It accepts five arguments: the HTTP request object, a token object and a tokenSecret object to validate future requests, a profile object containing the user profile, and a done callback to be called when the authentication process is over.

Inside the callback function, you will create a new user object using the Twitter profile information and the controller's saveOAuthUserProfile() method, which you previously created, to authenticate the current user.

Now that you have your Twitter strategy configured, you can go back to the config/passport.js file and load the strategy file as follows:

var passport = require('passport'),
    mongoose = require('mongoose');

module.exports = function() {
  var User = mongoose.model('User');
  
  passport.serializeUser(function(user, done) {
    done(null, user.id);
  });

  passport.deserializeUser(function(id, done) {
    User.findOne({
      _id: id
    }, '-password -salt', function(err, user) {
      done(err, user);
    });
  });

  require('./strategies/local.js')();
  require('./strategies/facebook.js')();
  require('./strategies/twitter.js')();
};

This will load your Twitter strategy configuration file. Now all that is left to do is set the routes needed to authenticate users via Twitter and include a link to those routes in your sign-in and signup pages.

Wiring Passport's Twitter strategy routes

To add Passport's Twitter routes, go to your app/routes/users.server.routes.js file, and paste the following code after the Facebook strategy routes:

app.get('/oauth/twitter', passport.authenticate('twitter', {
  failureRedirect: '/signin'
}));

app.get('/oauth/twitter/callback', passport.authenticate('twitter', {
  failureRedirect: '/signin',
  successRedirect: '/'
}));

The first route will use the passport.authenticate() method to start the user authentication process, while the second route will use passport.authenticate() method to finish the authentication process once the user has used their Twitter profile to connect.

That's it! Everything is set up for your user's Twitter-based authentication. All you have to do is go to your app/views/signup.ejs and app/views/signin.ejs files and add the following line of code right before the closing BODY tag:

<a href="/oauth/twitter">Sign in with Twitter</a>

This will allow your users to click on the link and register with your application via their Twitter profile.

Using Passport's Google strategy

The last OAuth provider we'll implement is Google as a lot of web applications offer their users the ability to register with the web application using their Google profile. Passport supports the Google OAuth authentication method using the passport-google-oauth module. Let's see how you can implement a Google-based authentication in a few simple steps.

Installing Passport's Google strategy

To install Passport's Google strategy module in your application's modules folders, you'll need to change your package.json file, as follows:

{
  "name": "MEAN",
  "version": "0.0.6",
  "dependencies": {
    "express": "~4.8.8",
    "morgan": "~1.3.0",
    "compression": "~1.0.11",
    "body-parser": "~1.8.0",
    "method-override": "~2.2.0",
    "express-session": "~1.7.6",
    "ejs": "~1.0.0",
    "connect-flash": "~0.1.1",
    "mongoose": "~3.8.15",
    "passport": "~0.2.1",
    "passport-local": "~1.0.0",
    "passport-facebook": "~1.0.3",
    "passport-twitter": "~1.0.2",
    "passport-google-oauth": "~0.1.5"
  }
}

Before you can continue developing your application, you will need to install the new Google strategy dependency. Go to your application's root folder, and issue the following command in your command-line tool:

$ npm install

This will install the specified version of Passport's Google strategy in your node_modules folder. Once the installation process has successfully finished, you will need to configure the Google strategy.

Configuring Passport's Google strategy

Before we begin configuring your Google strategy, you will have to go to the Google developers' home page at https://console.developers.google.com/and create a new Google application. In your application's settings, set the JAVASCRIPT ORIGINS property to http://localhost and the REDIRECT URIS property to http://localhost/oauth/google/callback. After configuring your Google application, you will get a Google application ID and secret. You'll need them to authenticate your users via Google, so let's add them in our environment configuration file. Go to the config/env/development.js file, and change it as follows:

module.exports = {
  db: 'mongodb://localhost/mean-book',
  sessionSecret: 'developmentSessionSecret',
  facebook: {
    clientID: 'Application Id',
    clientSecret: 'Application Secret',
    callbackURL: 'http://localhost:3000/oauth/facebook/callback'
  },
  twitter: {
    clientID: 'Application Id',
    clientSecret: 'Application Secret',
    callbackURL: 'http://localhost:3000/oauth/twitter/callback'
  },
  google: {
    clientID: 'Application Id',
    clientSecret: 'Application Secret',
    callbackURL: 'http://localhost:3000/oauth/google/callback'
  }
};

Don't forget to replace Application Id and Application Secret with your Google application's ID and secret. The callbackURL property will be passed to the Google OAuth service, which will redirect the user to that URL after the authentication process is over.

To implement the Google authentication strategy, go to your config/strategies folder, and create a new file named google.js containing the following lines of code:

var passport = require('passport'),
    url = require('url'),
    GoogleStrategy = require('passport-google-oauth').OAuth2Strategy,
    config = require('../config'),
    users = require('../../app/controllers/users.server.controller');

module.exports = function() {
  passport.use(new GoogleStrategy({
    clientID: config.google.clientID,
    clientSecret: config.google.clientSecret,
    callbackURL: config.google.callbackURL,
    passReqToCallback: true
  },
  function(req, accessToken, refreshToken, profile, done) {
    var providerData = profile._json;
    providerData.accessToken = accessToken;
    providerData.refreshToken = refreshToken;

    var providerUserProfile = {
      firstName: profile.name.givenName,
      lastName: profile.name.familyName,
      fullName: profile.displayName,
      email: profile.emails[0].value,
      username: profile.username,
      provider: 'google',
      providerId: profile.id,
      providerData: providerData
    };

    users.saveOAuthUserProfile(req, providerUserProfile, done);
  }));
};

Let's go over the preceding code snippet for a moment. You begin by requiring the Passport module, the Google Strategy object, your environmental configuration file, your User Mongoose model, and the Users controller. Then, you register the strategy using the passport.use() method and create an instance of a GoogleStrategy object. The GoogleStrategy constructor takes two arguments: the Google application information and a callback function that it will later call when trying to authenticate a user.

Take a look at the callback function you defined. It accepts five arguments: the HTTP request object, an accessToken object to validate future requests, a refreshToken object to grab new access tokens, a profile object containing the user profile, and a done callback to be called when the authentication process is over.

Inside the callback function, you will create a new user object using the Google profile information and the controller's saveOAuthUserProfile() method, which you previously created, to authenticate the current user.

Now that you have your Google strategy configured, you can go back to the config/passport.js file and load the strategy file, as follows:

var passport = require('passport'),
    mongoose = require('mongoose');

module.exports = function() {
  var User = mongoose.model('User');
  
  passport.serializeUser(function(user, done) {
    done(null, user.id);
  });

  passport.deserializeUser(function(id, done) {
    User.findOne({
      _id: id
    }, '-password -salt', function(err, user) {
      done(err, user);
    });
  });

  require('./strategies/local.js')();
  require('./strategies/facebook.js')();
  require('./strategies/twitter.js')();
  require('./strategies/google.js')();
};

This will load your Google strategy configuration file. Now all that is left to do is set the routes required to authenticate users via Google and include a link to those routes in your sign-in and signup pages.

Wiring Passport's Google strategy routes

To add Passport's Google routes, go to your app/routes/users.server.routes.js file, and paste the following lines of code after the Twitter strategy routes:

app.get('/oauth/google', passport.authenticate('google', {
  failureRedirect: '/signin',
  scope: [
    'https://www.googleapis.com/auth/userinfo.profile',
    'https://www.googleapis.com/auth/userinfo.email'
  ],
}));

app.get('/oauth/google/callback', passport.authenticate('google', {
  failureRedirect: '/signin',
  successRedirect: '/'
}));

The first route will use the passport.authenticate() method to start the user authentication process, while the second route will use the passport.authenticate() method to finish the authentication process once the user used their Google profile to connect.

That's it! Everything is set up for your user's Google-based authentication. All you have to do is go to your app/views/signup.ejs and app/views/signin.ejs files and add the following line of code right before the closing BODY tag:

<a href="/oauth/google">Sign in with Google</a>

This will allow your users to click on the link and register with your application via their Google profile. To test your new authentication layers, go to your root application folder and use the node command-line tool to run your application:

$ node server

Test your application by visiting http://localhost:3000/signin and http://localhost:3000/signup. Try signing up and signing in using the new OAuth methods. Don't forget to visit your home page to see how the user details are saved throughout the session.

Note

Passport has similar support for many additional OAuth providers. To learn more, it is recommended that you visit http://passportjs.org/guide/providers/.

Wiring Passport's Google strategy routes
Wiring Passport's Google strategy routes
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.76.250