Reading Time: about 15 minutes.

In this tutorial, we will use CenturyLink AppFog to build and deploy a Node.js application that authenticates users against a MongoDB instance hosted on a CenturyLink Compute server.

Tools Used

CenturyLink AppFog is our Platform-as-a-Service (PaaS) offering based on Cloud Foundry. It allows you to host cloud-native applications easily without worrying about provisioning and maintaining servers and separate services. AppFog applications deploy quickly and scale automatically while having access to the full range of CenturyLink Cloud products.

CenturyLink Cloud Compute servers are high-performance cloud servers. These enterprise-grade virtual machines are easy to deploy and manage from the CenturyLink Cloud Console and via our powerful API.

MongoDB is an open-source, document-oriented NoSQL database. It supports a number of enterprise features, including replication, load-balancing, and query aggregation.

Before We Start

If you don't have a CenturyLink Cloud account yet, head over to our website and sign up for a free trial. You'll need it to access AppFog and to deploy virtual servers.

You will also need to install Node.js, Express, and the Cloud Foundry client tools on your development machine. Check with the documentation for your development environment to find out the best way to do this.

  • With Ubuntu, run the following commands to install the development tools and binaries:

    $ sudo apt-get install nodejs npm cf-cli
    $ sudo npm install express -g
    $ sudo npm install express-generator -g
    

Deploying a Cloud Server with MongoDB

We will host our MongoDB instance on a CenturyLink Cloud virtual server. To provision a new virtual server, follow the steps below.

  1. Log into the CenturyLink Cloud Control Portal.
  2. From the Navigation Menu, click Infrastructure > Servers. wl-nodejs-authen-1.png
  3. Select a region for your new server, then click create > server.
  4. Fill out the information for your new server. Make sure to select CentOS 7 | 64-bit for the operating system.
  5. After the server is provisioned, return to the server list from Step 2 and find your new server in the list.
  6. Click the more > add public ip from the drop-down menu.
  7. Check the box for SSH/SFTP (22) to allow SSH access to the server.
  8. Click custom port > single port from the drop-down menu.
  9. Type 27017 in the blank box to open up the MongoDB server port. wl-nodejs-authen-2.png
  10. Click add public ip address.

Installing and Configuring MongoDB

  1. Navigate to your server in the Control Portal as in the previous section. Your server's public IP address will be noted on the screen.
  2. From a shell on your local machine, connect to your new server with the following command. Replace "YOUR.VPS.IP" with your server's public IP address.
    ssh [email protected]
    
  3. Install the MongoDB server software by running the following commands.
    $ yum install -y mongodb mongodb-server
    
  4. With a text editor, open /etc/mongod.conf. Look for the line that begins "bind_ip" and comment it out. The top of your file should now look like this:

    ##
    ### Basic Defaults
    ##
    
    # Comma separated list of ip addresses to listen on (all local ips by default)
    #bind_ip = 127.0.0.1
    
  5. Start the MongoDB service by running the following command.
    $ service mongod start
    

Set Up the Node.js Project

The next step is to create the Node.js project. We will use the Express.js web application server framework, which you installed earlier.

Note: If you want to follow along with our already-completed example, download the code for this project. It is located in the Git repository here. Clone the Git repository with the following commands:

   $ git clone https://github.com/pymander/ctl-appfog-mongodb.git
   $ cd ctl-appfog-mongodb

Initialize Your Express.js Project

Use the Express command line tool to start your project. The express tool creates a new directory to host a project. We will also use the --git flag to instruct the tool to initialize a new Git repository in the directory. Run the following shell command:

   $ express --git <my-project-name>

Note: Replace <my-project-name> with the name of your project. It should be a valid Unix filename and cannot contain any spaces.

Edit the Package Configuration

  1. In your project directory, open the package.json file in a text editor. Edit the file to look like this:

    {
      "name": "<my-project-name>",
      "version": "0.1.0",
      "description": "Get started with Node.js and MongoDB on CenturyLink AppFog",
      "private": true,
      "scripts": {
            "prestart": "npm install",
            "start": "node ./bin/www"
      },
      "dependencies": {
            "bcryptjs": "^2.3.0",
            "body-parser": "~1.13.2",
            "cookie-parser": "~1.3.5",
            "debug": "~2.2.0",
            "express": "~4.13.1",
            "express-session": "^1.12.1",
            "jade": "~1.11.0",
            "morgan": "~1.6.1",
            "serve-favicon": "~2.3.0"
      }
    }
    
  2. Set the name attribute to your project name. The name can't contain any spaces. Note that because AppFog uses a global project namespace, this will need to be unique across all projects.

Configure the Web Server

Since we are using AppFog to deploy the application, it's important that it understands how to read configuration values from AppFog's environment.

  1. Open the bin/www file with a text editor.
  2. Locate the line that begins var port = normalizePort.
  3. Replace that block with the code below:
    /*
     * Get the port from the environment. Note that the VCAP_APP_PORT is
     * used by CenturyLink AppFog to specify which port this should run
     * on. Otherwise, fall back to port 3000.
     */
    var port = normalizePort(process.env.VCAP_APP_PORT || '3000');
    app.set('port', port);
    

Your project should now be properly configured and ready for development.

Running Your Application

Your configuration only serves a single route at the moment. This is a good time to make sure that everything is working correctly.

  1. Run the following command in your project directory.

    $ npm start
    
  2. You will see all of your dependencies checked and updated. The local server will begin serving your application. wl-nodejs-authen-3.png

  3. Visit http://localhost:3000/.

Deploying Your Application to AppFog

At this point, we will configure AppFog in your CenturyLink Cloud account and prepare your app for deployment. To get a good understanding of AppFog, you should read the Getting Started Guide. For now, we will just cover the basics.

Configuring the Cloud Foundry CLI Tool

Since all of our application's dependencies will be handled by Node.js, we can configure the Cloud Foundry CLI tool to exclude those from upload commands. This can save time and resources by significantly reducing the number of files uploaded by the tool. Configure the CLI tool by following these steps:

  1. In your project working directory, create and edit .cfignore to look like this:

    node_modules/
    *~
    *.sample
    .git/
    
  2. If your text editor uses different filenames for backups, temporary files, or lock files, add those patterns to the .cfignore file.

  3. If your application uses temporary files, installation-specific configuration files, or other files that don't need to be uploaded to AppFog, add those filenames or patterns to the .cfignore file.

Enable AppFog

First, enable AppFog in the CenturyLink Cloud Control Portal.

  1. Go to the Control Portal and log in.
  2. From the Navigation Menu, click Services > AppFog.

Login to AppFog using the Cloud Foundry Tools

Once AppFog is set up in your CenturyLink Cloud account, you need to login to AppFog using the Cloud Foundry CLI. The Control Portal will give you an exact command on the Overview tab.

  1. Find the cf login command on your AppFog control panel. It will look similar to this:

    cf login -a https://api.useast.appfog.ctl.io -o C007 -u your.username
    

    wl-nodejs-authen-4.png

  2. Run the command in your project directory.

  3. Enter your CenturyLink Cloud password when prompted. wl-nodejs-authen-5.png

  4. Select the Dev application space. wl-nodejs-authen-6.png

Note: To learn more about logging into AppFog using the Cloud Foundry command line tools, read this Knowledge Base article.

Deploy Your Application

After the initial configuration and login, deployment is just one command. Run the following command in your project directory, replacing <my-project-name> with the name of your project.

   $ cf push <my-project-name>

A successful deployment will produce a lot of output, and look similar to this when it finishes: wl-nodejs-authen-7.png

Note: To learn more about deploying Node.js applications, read this Knowledge Base article.

Authentication with Passport and MongoDB

Our application will use Passport to authenticate against user information stored in MongoDB. Passport is authentication middleware for Node.js and makes for simple, fast development of different backend methods, which it calls "strategies."

Install Additional Libraries

We will now install the additional libraries needed to add Passport and MongoDB support to our application. The npm tool is the package manager for Node.js. The --save flag adds the installed library to the dependencies in your project's package.json file. Run the following commands:

   $ npm install mongodb --save
   $ npm install passport --save
   $ npm install passport-local --save
   $ npm install q --save

Create a Private Configuration

Next, we will create config.js, a file to store private configuration values, such as API keys. Because it's private, don't add config.js to your Git repository! For now, it will only contain the IP address of the virtual server hosting your MongoDB instance.

  1. Create a file called config.js in your project directory.

  2. Edit the file to look like the following example, replacing <mongodb-host-ip> with the IP address of your virtual server.

    // config.js
    // This file contains private configuration details.
    // Do not add it to your Git repository.
    module.exports = {
    "mongodbHost": "<mongodb-host-ip>"
    };
    

Create Passport Strategy

In this step, you will create a Passport strategy that uses MongoDB to store user credentials.

  1. Create a new file in your project directory called functions.js.

  2. Edit the file to contain the following code:

    // functions.js
    // Handle setup of Passport configuration and user authentication
    
    var bcrypt = require('bcryptjs'),
        Q = require('q'),
        config = require('./config.js');
    
    // MongoDB connection information
    var mongodbUrl = 'mongodb://' + config.mongodbHost + ':27017/users';
    var MongoClient = require('mongodb').MongoClient
    
    // Register a new user, failing if that user already exists.
    exports.localReg = function (username, password) {
      var deferred = Q.defer();
    
      MongoClient.connect(mongodbUrl, function (err, db) {
        var collection = db.collection('localUsers');
    
        collection.findOne({'username' : username})
          .then(function (result) {
            if (null != result) {
              console.log("USERNAME ALREADY EXISTS:", result.username);
              deferred.resolve(false); // username exists
            }
            else  {
              var hash = bcrypt.hashSync(password, 8);
              var user = {
                "username": username,
                "password": hash,
                "note": "Created by CenturyLink Cloud test application"
              }
    
              console.log("CREATING USER:", username);
    
              collection.insert(user)
                .then(function () {
                  db.close();
                  deferred.resolve(user);
                });
            }
          });
      });
    
      return deferred.promise;
    };
    
    // Check for the existence of a username.
    // Use bcrypt.compareSync to check encrypted password.
    exports.localAuth = function (username, password) {
      var deferred = Q.defer();
    
      MongoClient.connect(mongodbUrl, function (err, db) {
        var collection = db.collection('localUsers');
    
        collection.findOne({'username' : username})
          .then(function (result) {
            if (null == result) {
              console.log("USERNAME NOT FOUND:", username);
    
              deferred.resolve(false);
            }
            else {
              var hash = result.password;
    
              console.log("FOUND USER: " + result.username);
    
              if (bcrypt.compareSync(password, hash)) {
                deferred.resolve(result);
              } else {
                console.log("AUTHENTICATION FAILED");
                deferred.resolve(false);
              }
            }
    
            db.close();
          });
      });
    
      return deferred.promise;
    };
    
    // End functions.js
    

Add Passport to the Application

Now that we have a Passport strategy that uses MongoDB, it's time to modify the app.js file, which is the guts of the Express web application. Near the top of the file, we need to make sure that functions.js is loaded. We also need to load some additional required libraries, such as Passport and Express-session.

  1. Open app.js in a text editor.
  2. Find the block of code at the beginning of the file that starts with var express and ends with var users.
  3. Edit that section so it looks like this:

    var express = require('express');
    var path = require('path');
    var favicon = require('serve-favicon');
    var logger = require('morgan');
    var cookieParser = require('cookie-parser');
    var bodyParser = require('body-parser');
    var session = require('express-session');
    var routes = require('./routes/index');
    var users = require('./routes/users');
    
    // Load our functions.
    var funct = require('./functions.js');
    
    // Passport Libraries
    var passport = require('passport'),
        LocalStrategy = require('passport-local');
    

Integrate Passport into Session Object

Next, just after the Passport libraries are loaded, we need to tell Passport how to integrate into the web session object and how to use our MongoDB strategy.

  1. Find the point in app.js after the libraries have been loaded.
  2. Add the following code:

    // Passport session integration functions.
    passport.serializeUser(function(user, done) {
      done(null, user);
    });
    
    passport.deserializeUser(function(obj, done) {
      done(null, obj);
    });
    
    // Use the LocalStrategy within Passport to login/”signin” users.
    passport.use('local-signin', new LocalStrategy(
      {passReqToCallback : true}, //allows us to pass back the request to the callback
      function(req, username, password, done) {
            delete req.session.success;
            delete req.session.error;
    
            funct.localAuth(username, password)
            .then(function (user) {
              if (user) {
                console.log("LOGGED IN AS: " + user.username);
                req.session.success = 'You are successfully logged in ' + user.username + '!';
                done(null, user);
              }
    
              if (!user) {
                console.log("COULD NOT LOG IN");
                req.session.error = 'That username and password combination is not valid!';
                done(null, user);
              }
            })
            .fail(function (err){
              console.log(err.body);
            });
      }
    ));
    
    // Use the LocalStrategy within Passport to register/"signup" users.
    passport.use('local-signup', new LocalStrategy(
      {passReqToCallback : true}, //allows us to pass back the request to the callback
      function(req, username, password, done) {
            delete req.session.success;
            delete req.session.error;
    
            console.log("TRYING TO REGISTER: " + username);
            funct.localReg(username, password)
              .then(function (user) {
                if (user) {
                  console.log("REGISTERED: " + user.username);
                  req.session.success = 'You are successfully registered and logged in ' + user.username + '!';
                  done(null, user);
                }
                if (!user) {
                  console.log("COULD NOT REGISTER");
                  req.session.error = 'That username is already in use, please try a different one.';
                  done(null, user);
                }
              })
              .fail(function (err){
                console.log(err.body);
                 });
          }
        ));
    

Enable Passport Middleware

Finally, we need to enable the Passport middleware so Express knows when to use it.

  1. Find the line in the app.js file that begins with app.use(express.static.
  2. After that line, add the following code:
    // Enable Passport middleware
    app.use(session({secret: 'binarystar', saveUninitialized: true, resave: true}));
    app.use(passport.initialize());
    app.use(passport.session());
    
    Note: Be sure to change the secret parameter to something other than "binarystar."

Add Views to the Application

Now we need to add views, so our application actually gives us something to look at. We will use Jade templates.

  1. Open the views/index.jade file in your text editor.
  2. Edit the contents to look like the following code:
    extends layout
    block content
      h1= title
        p
            | This example shows you how to authenticate against an
            a(href='mongodb.com') MongoDB
            |  database.
        if user
            p
                strong Welcome back, #{user.username}!
            p
                a(href='/logout') Logout
        else
            p You have never been here before.
                a(href='/signin') Please sign in
    

This template accesses #{user.username}, which contains a valid record, if a login was successful. We will see how this is passed to the template when we build our routes in the next step.

Create Login Screen View

We also need to create a view to contain the template with our login and account creation forms.

  1. Create views/signin.jade with a text editor.
  2. Edit the contents to look like the following code:
    extends layout
    block content
      h1= title
      p Please sign in using the form below.
      form(method='post', action='/login')
            label Username:
                input(type='text', name='username', size='20')
            br
            label Password:
                input(type='password', name='password', size='20')
            br
            input(type='submit', value='Login')
      h2 If you don't have an account, please make one
      form(method='post', action='/local-reg')
            label New username:
                input(type='text', name='username', size='20')
            br
            label New password:
                input(type='password', name='password', size='20')
            br
            input(type='submit', value='Create account')
    

Create Application Routes

Express uses “routes” to map URIs to functions, like other web frameworks. For our application, we will need to significantly rewrite routes/index.js in order to integrate the Passport functions we need.

  1. Open routes/index.js in a text editor.
  2. Replace the contents with the following code:

    var express = require('express');
    var router = express.Router();
    var passport = require('passport');
    
    /* GET home page. */
    router.get('/', function(req, res, next) {
      var tpl = { title   : 'CenturyLink Cloud Example',
                  user    : req.user,
                  session : req.session };
    
      res.render('index', tpl);
    });
    
    router.get('/signin', function(req, res, next) {
      res.render('signin', { title   : 'Please sign in',
                             session : req.session });
    });
    
    // Sends the request through our local signup strategy, and if
    // successful takes user to homepage, otherwise returns then to signin
    // page
    
    router.post('/local-reg', passport.authenticate('local-signup', {
        successRedirect : '/',
        failureRedirect : '/signin'
    }));
    
    // Sends the request through our local login/signin strategy, and if
    // successful takes user to homepage, otherwise returns then to signin
    // page
    
    router.post('/login', passport.authenticate('local-signin', {
        successRedirect : '/',
        failureRedirect : '/signin'
    }));
    
    // Logs user out of site, deleting them from the session, and returns to homepage
    router.get('/logout', function(req, res){
        delete req.session.notice;
        delete req.session.error;
        if (!req.user) {
            res.redirect('/');
            return;
        }
    var name = req.user.username;
    console.log("LOG OUT: " + name)
      req.session.success = "You have successfully been logged out " + name + "!";
      req.logout();
      res.redirect('/');
    });
    
    module.exports = router;
    

Your application should now be ready to run.

Running Your Application

After configuring your application and looking at how Passport and MongoDB work together, it's time to run it and test it.

  1. Run the following command in your project directory.
    $ npm start
    
  2. You will see all of your dependencies checked and updated. The local server will begin serving your application.
  3. Open http://localhost:3000/ in your web browser and click Please sign in.
  4. Create a new account. App Login

Deploying Your Changes to AppFog

When you are satisfied that your application is working correctly, it's time to deploy your changes to AppFog. Run the following command in your project directory to deploy to the cloud. Replace <your-project-name> with the name of your project.

   $ cf push <your-project-name>

Your updated application is now deployed.

Next Steps

AppFog is a powerful platform for deploying enterprise-class applications without having to worry about the underlying infrastructure. In this tutorial, you learned how to connect AppFog to services running on virtual machines, which opens a whole new world in application development possibilities.

Using Passport, an application can authenticate against more than 300 different services. In addition, as our example shows, adding custom authentication sources is relatively simple. You can write authentication modules to check login credentials against enterprise directory services such as Active Directory or other LDAP-based databases.

This application is mostly compatible with our full Node.js AppFog tutorial. With some changes, you should be able to continue with Part 2 of the tutorial and add CenturyLink Object Storage support to your new application.

Sign-up for our Developer-focused newsletter CODE. Designed hands-on by developers, for developers. Keep up to date on topics of interest: tutorials, tips and tricks, and community building events.

We’re a different kind of cloud provider – let us show you why.