Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
MongoDB, Express, Angular, and Node.js Fundamentals

You're reading from   MongoDB, Express, Angular, and Node.js Fundamentals Become a MEAN master and rule the world of web applications

Arrow left icon
Product type Paperback
Published in Mar 2019
Publisher
ISBN-13 9781789808735
Length 362 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Paul Oluyege Paul Oluyege
Author Profile Icon Paul Oluyege
Paul Oluyege
Arrow right icon
View More author details
Toc

Table of Contents (9) Chapters Close

MongoDB, Express, Angular, and Node.js Fundamentals
Preface
1. Introduction to the MEAN Stack FREE CHAPTER 2. Developing RESTful APIs to Perform CRUD Operations 3. Beginning Frontend Development with Angular CLI 4. The MEAN Stack Security 5. Angular Declarables, Bootstrapping, and Modularity 6. Testing and Optimizing Angular Applications Appendix

Chapter 4: The MEAN Stack Security


Activity 11: Securing the RESTful API

File name: userController.js
  1. Create an admin user model by creating a file named userModel.js inside the api folder and input the following code:

    const mongoose = require("mongoose"), // loading modules 
    bcrypt = require('bcryptjs'),
    Schema = mongoose.Schema;
    
    const UserSchema = new Schema({ // Schema Instance
      fullName: {
        type: String,
        trim: true,
        required: true
      },
      email: {
        type:String,
        unique:true,
        lovercase:true,
        trim:true,
        required:true
      } ,
      hash_password: {
        type:String,
        required:true
      },
      createdOn: {
        type: Date,
        default: Date.now
      }
    });
    
    UserSchema.methods.comparePassword = function(password){  //password confirmation
      return bcrypt.compareSync(password, this.hash_password);
    }
    module.exports = mongoose.model("User", UserSchema); //user model
  2. Create an admin user controller by creating a file called userController.js inside the controllers/api folder input using the following code:

    const User = require("../models/userModel"); // Import userModel,
        jwt = require('jsonwebtoken'), // load jasonwebtoken module
        bcrypt = require('bcryptjs');  // load bcryptjs module for password hashing
    
    exports.register = (req, res) => { // exportable function to register new user
        let newUser = new User(req.body);
        newUser.hash_password = bcrypt.hashSync(req.body.password, 10);
        newUser.save((err, user) => {
            if (err) {
                res.status(500).send({ message: err });
            }
            user.hash_password = undefined;
            res.status(201).json(user);
        });
    };
    
    //[…]
    
    exports.loginRequired = (req, res, next) => {
        if (req.user) {
            res.json({ message: 'Authorized User!'});
            next();
          } else {
             res.status(401).json({ message: 'Unauthorized user!' });
          }
    };
  3. Update the route file (articleListRoutes.js) in the routes directory (server/api/controllers) with the following code:

    'use strict';
    module.exports = function(app) {
      var articleList = require('../controllers/articleListController');
      var userHandlers = require('../controllers/userController');
    
      // articleList Routes
      app
      .route("/articles")
      .get(articleList.listAllArticles)
      .post(userHandlers.loginRequired, articleList.createNewArticle);
    
     app
      .route("/article/:articleid")
      .get(articleList.readArticle)
      .put(articleList.updateArticle)
      .delete(articleList.deleteArticle);
    
      app
      .route("/articles/by/:tag")
      .get(articleList.listTagArticles);
    
      app
      .route("/auth/register")
      .post(userHandlers.register);
    
      app
      .route("/auth/sign_in")
      .post(userHandlers.signIn);
    
    };
  4. Update the server.js (inside the server folder) file with the following code:

    'use strict'
    const express = require("express");
    const bodyParser = require("body-parser");
    
    // db instance connection
    require("./config/db");
    var User = require('./api/models/userModel'),
        jsonwebtoken = require("jsonwebtoken");
    
    const app = express();
    
    const port = process.env.PORT || 3000;
    app.use(bodyParser.urlencoded({ extended: true }));
    app.use(bodyParser.json());
    
    //CORS (Cross-Origin Resource Sharing) headers to support Cross-site HTTP requests
    app.use(function(req, res, next) {
        res.header('Access-Control-Allow-Origin', '*');
        res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH,OPTIONS');
        res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
        // allow preflight
        if (req.method === 'OPTIONS') {
            res.send(200);
        } else {
            next();
        }
    });
    
    app.use((req, res, next) => { // Verify JWT for user authorization
        if (req.headers && req.headers.authorization && req.headers.authorization.split(' ')[0] === 'JWT') {
            jsonwebtoken.verify(req.headers.authorization.split(' ')[1],  'RESTfulAPIs', (err, decode) => {
                    if (err) req.user = undefined;
                    req.user = decode;
                    next();
                });
        } else {
            req.user = undefined;
            next();
        }
    });
    
    
    // API ENDPOINTS
    var routes = require('./api/routes/articleListRoutes'); //importing route
    routes(app); 
    
    // LISTENING
    app.listen(port, () => {
      console.log('Server running at http://localhost:${port}');
    });
  5. Run the server using node server on the CLI and open Postman for testing.

  6. Test for registration by typing in localhost:3000/auth/register on the address bar. You will obtain the following output:

    Figure 4.15: Screenshot for registration

  7. Attempt the login required path and post request on localhost:3000/articles. You will obtain the following output:

    Figure 4.16: Screenshot for posting articles

  8. Attempt user login by typing in localhost:3000/auth/sign_in on the address bar. You will obtain the following output:

    Figure 4.17: Screenshot for sign in

  9. Set the authentication key on the header and input the value in JWT token format, as shown here:

    Figure 4.18: Screenshot for setting the authentication key

  10. Attempt the login required path and post request on localhost:3000/articles. You will obtain the following output:

    Figure 4.19: Screenshot for articles

Thus, from the preceding outputs, it can be clearly observed that we have successfully secured the RESTful API we developed in the previous exercise. We also managed to provide admin access for creating, updating, and deleting data.

Activity 12: Creating a Login Page to Allow Authentication with Twitter Using Passport Strategies

File name: passport.js
  1. Create a package.json file and install express, body-parser, mongoose, passport-twitter, and passport by running the following code:

    npm init
    npm install express body-parser mongoose passport-twitter passport express-session -save
  2. Create a server.js (using touch server.js on the CLI) file and import express and body-parser using the following code:

    const express = require("express");
    const bodyParser = require("body-parser");
    const session = require('express-session');
  3. Create an Express application, assign a port number, and use the body-parser middleware on the Express application using the following code:

    const app = express();
    const port = process.env.PORT || 4000;
    app.use(bodyParser.urlencoded({ extended: true }));
    app.use(bodyParser.json());
  4. Create a config folder using the following code:

    mkdir config
  5. Create a database by first creating a file named db.js (using touch db.js on the CLI) in the config folder directory and input the following code:

    const mongoose = require("mongoose");
    
    var uri = "mongodb+srv://username:passowrd@cluster0-0wi3e.mongodb.net/test?retryWrites=true";
    
      const options = {
        reconnectTries: Number.MAX_VALUE,
        poolSize: 10
      };
    // Connect to the database using the following code  
    mongoose.connect(uri, options).then(
        () => {
          console.log("Database connection established!");
        },
        err => {
          console.log("Error connecting Database instance due to: ", err);
        }
      );
  6. Create an api directory and three subfolders named controllers, models, and routes using the following code:

    mkdir api
    mkdir – controllers && mkdir – models && mkdir – routes
  7. Create a model file inside the controller directory (using touch userModel.js on the CLI) and then create the schema and model:

    const mongoose = require("mongoose"),
    const Schema = mongoose.Schema;
    const UserSchema = new Schema({
    twitter         : {
        fullName     : String,
        email        : String,
        createdOn: {
          type: Date,
          default: Date.now
        },
    },
    });
    
    //Create a mongoose model from the Schema
    mongoose.model("User", UserSchema);
  8. Create a passport file inside the config directory (using touch passport.js) and then create a Twitter strategy using the following code:

    const  TwitterStrategy = require("passport-twitter").Strategy;
    const mongoose = require('mongoose'),
    const User = mongoose.model('User');
    
    // Create an exposed function 
    module.exports = function (passport) {}
    serialize the user for the session
        passport.serializeUser(function (user, done) {
            done(null, user.id);
        });
    
     deserialize the user
        passport.deserializeUser(function (id, done) {
            User.findById(id, function (err, user) {
                done(err, user);
            });
        });
    
    //[…]
          } else {
            done(null, user);
          }
        });
      }
    )); 
    }
  9. Create a route file inside the routes directory (using touch route.js) and then create an exposed function as an exportable module that takes in app and passport using the following code:

    module.exports = function(app,passport)  {
    app.get('/auth/twitter',
      passport.authenticate(' twitter ', {scope:"email"}));
    
    app.get('/auth/ twitter /callback',
      passport.authenticate('twitter', { failureRedirect: '/error' }),
      function(req, res) {
        res.redirect('/success');
      });
    
    }
  10. Update the server.js file with the following code:

    //db instance connection
    require("./config/db"); app.get('/success', (req, res) => res.send("You have successfully logged in"));
    app.get('/error', (req, res) => res.send("error logging in"));
  11. Import and initialize passport using the following code:

    const passport = require("passport");
    // Authentication configuration
    app.use(session({
        resave: false,
        saveUninitialized: true,
        secret: 'bla bla bla' 
      }))
    app.use(passport.initialize());
    app.use(passport.session());
  12. Update the API endpoint using the following code:

    var routes = require('./api/routes/route'); //importing route
    routes(app,passport);app.listen(port, () => {
        console.log('Server running at http://localhost:${port}');
    });
  13. Run the server (using node server on the CLI), open a browser, and test this by typing in localhost:4000/auth/twitter on the browser address bar. You will obtain the following output:

    Figure 4.20: Successful authentication using Twitter

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at R$50/month. Cancel anytime