Browsing:

Tag: nodejs

Beginning Node.js – local authentication with Passport-Local Mongoose – part 6

Disclaimer: This is a series about me, creating a web application in Node.js. The completed example is available here.

I have a deep respect for all those developers out there that write fantastic modules I can use in my projects. One little gem is passport-local-mongoose. I’ve written about it before but as you see I’m doing it again.

What is passport-local-mongoose?

You can plug-in passport-local-mongoose into your Mongoose Userschema. This decorates the User object with a set of methods. E.g.

  • authenticate
  • register
  • setPassword
  • serialize
  • deserialize

It also hashes the passwords. This saves a lot of work.

What we should work on

  • Users should be able to register
  • Users should be able to authenticate
  • Users should be able to change their password
  • Users should be able to change their e-mail address
  • There should be a ‘forgot password’ procedure
  • Users should be able to delete their accounts

This article covers only ‘register’ and ‘authenticate’. You can go ahead and clone the restaurant github repo for a full example.

To add local authentication to your app you’ll need to run:

Of course, you may add your own properties to the model:

var mongoose = require('mongoose'),
    Schema = mongoose.Schema,
    passportLocalMongoose = require('passport-local-mongoose');

var User = new Schema({
    uuid: {
        type: String,
        required: false
    },
    firstname: {
        type: String,
        required: true
    },
    active: {
        type: String,
        required: false
    }
});

var options = ({missingPasswordError: "Wrong password"});
User.plugin(passportLocalMongoose,options);

module.exports = mongoose.model('User', User)

Now let’s hook up Passport in our app.

Controller

Let’s create a user controller which contains the register, the login and the getLogin functions (to check if a user has logged in).
Create a file named controller.user.js and put it in the app folder:

var mongoose = require('mongoose');
var User = require('./model.user');

exports.register = function (req, res) {
    console.log("registering: " + req.body.firstName);
    User.register(new User({
        username: req.body.username,
        firstname: req.body.firstname
    }), req.body.password, function (err, user) {
        if (err) {
            console.log(err);
            return res.send(err);
        } else {
            res.send({
                success: true,
                user: user
            });
        }
    });
};

exports.login = function (req, res, next) {

    User.authenticate()(req.body.username, req.body.password, function (err, user, options) {
        if (err) return next(err);
        if (user === false) {
            res.send({
                message: options.message,
                success: false
            });
        } else {
            req.login(user, function (err) {
                res.send({
                    success: true,
                    user: user
                });
            });
        }
    });

};

exports.getLogin = function (req, res) {
    console.log(req.user);
    if (req.user) {

        return res.send({
            success: true,
            user: req.user
        });

    } //res.send(500, {status:500, message: 'internal error', type:'internal'}); == deprecated


    res.send({
        success: false,
        message: 'not authorized'
    });
};

What happens?
1. User.authenticate and User.register:
The User.authenticate and User.register are functions we get from passport-local-mongoose. I just took this code as an example.

2. Check if a user is logged in with ‘if(req.user)’
If a user is logged in, the req.user property is populated with the user object.
So if it exists, the user is logged in.

Routes

//mongodb
var mongoose = require('mongoose');
var User = require('./model.user');

var users = require('./controller.user');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var cookieParser = require('cookie-parser');
var session = require('express-session');


module.exports = function (app) {

    //initialize passport
    passport.use(User.createStrategy());
    // use static serialize and deserialize of model for passport session support
    passport.serializeUser(User.serializeUser());
    passport.deserializeUser(User.deserializeUser());

    //need this according to passport guide
    app.use(cookieParser());
    app.use(session({
        secret: 'the princess and the frog',
        saveUninitialized: true,
        resave: true
    }));
    app.use(passport.initialize());
    app.use(passport.session());

    //routes
    app.route('/register').post(users.register);
    app.route('/login').post(users.login);
    app.route('/login').get(users.getlogin);
   
   
};

Add this file to main.js, like so:

require('./app/routes.user')(app);

This will be your completed main.js:

var express = require('express');
var app = express();
var bodyparser = require('body-parser');
var mongoose = require('mongoose');

app.use(bodyparser.urlencoded({
  extended: true
}));

app.use(bodyparser.json());

mongoose.connect('mongodb://localhost:27017/restaurant');

require('./app/routes.food')(app);
require('./app/routes.user')(app);

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

app.set('port', process.env.PORT || 3001);
app.listen(app.get('port'));
console.log("the server is running on http://localhost:" + app.get('port'));

Let’s try this

Install Postman or another REST API test tool.

  • Don’t forget to configure the headers: Content-Type application/json

First, let’s register a user (click POST):

Postman_passport

Second, let’s login:

Selection_012

Check the login status:

Selection_015

The end of this series

This is where this series end. I hope anyone will enjoy this and at least learns something from my struggles. I know I did!
Your feedback is more than welcome by the way.


Beginning Node.js – REST API with a Mongodb backend – part 3

This is the sequel to this article. I’m building a REST API from the ground up with Node.js and Express.js 4. I’m adding functionality as I go. I’m starting with Node.js, then I’m adding Express and now I’m adding Mongoose. In the next articles I will be adding Passport.js for authentication and start building a frontend, either with Angular of Ember.

Things have changed slightly since my former series, see here. That’s because Express.js version 3 has evolved to version 4. The main difference between version 3 and 4 is that version 3 contains middleware (JSON parsers, session support and so on). In version 4 you need to add the middleware yourself. That means installing body-parser, session and so on with NPM.

In my former article we used a JSON string with data. Now let’s use a real data backend: MongoDb. Mongoose is an ORM for MongoDb for Node.js, so let’s add the module to the project:

npm install --save mongoose

We also need to install the body-parser, which parses JSON bodies for Express.

npm install --save body-parser

Next, copy all of this to your main.js file:

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
//mongoose uses Schema as a layer on a MongoDb document (which represents a model):
var Schema = mongoose.Schema;

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

//connect to mongodb:
mongoose.connect('mongodb://localhost:27017/restaurant');

//create a Schema for our food:
var FoodSchema = new Schema({
    name: {
        type: String,
        index: {
            unique: true
        }
    },
    description: String,
    price: String
});


// Use the schema to register a model with MongoDb
mongoose.model('Food', FoodSchema);
var food = mongoose.model('Food');

//POST verb
app.post('/food', function (req, res) {
    food.create(req.body, function (err, food) {
        if (err) {
            res.send(401, err);
            return;
        }
        res.send(req.body);
    });
});

//GET verb
app.get('/food', function (req, res) {
    food.find(function (err, data) {
        if (err) {
            res.send(err);
        }
        res.json(data);
    });
});

//GET/id
app.get('/food/:id', function (req, res) {
    food.findOne({
        _id: req.params.id
    }, function (error, response) {
        if (error) {
            res.send(error);
        } else {
            res.send(response);
        }
    });
});

//GET by name
app.get('/foodname/:name', function (req, res) {
    food.findOne({
        name: req.params.name
    }, function (error, response) {
        if (error || !response) {
            res.send("not on the menu");
        } else {
            res.send(response);
        }
    });
});


app.set('port', process.env.PORT || 3000);
app.listen(app.get('port'));
console.log("the server is running on http://localhost:" + app.get('port'));

Now run it with the following command:

nodemon main.js

What happened?

Let’s dissect the code from the previous paragraph:

1.
We added and instantiated the prerequisites and connected to our MongodB instance

var bodyParser = require('body-parser');
var mongoose = require('mongoose');
//mongoose uses Schema as a layer on a MongoDb document (which represents a model):
var Schema = mongoose.Schema;

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

//connect to mongodb:
mongoose.connect('mongodb://localhost:27017/restaurant');

2.
We create a Schema for our Food class:

//create a Schema for our food:
var FoodSchema = new Schema({
    name: {
        type: String,
        index: {
            unique: true
        }
    },
    description: String,
    price: String
});

3.
Then we used the schema to register a model with MongoDb
mongoose.model('Food', FoodSchema);
var food = mongoose.model('Food');

4.
And then we are ready to define our CRUD methods, e.g.

//POST verb
app.post('/food', function (req, res) {
    food.create(req.body, function (err, food) {
        if (err) {
            res.send(401, err);
            return;
        }
        res.send(req.body);
    });
});

//GET verb
app.get('/food', function (req, res) {
    food.find(function (err, data) {
        if (err) {
            res.send(err);
        }
        res.json(data);
    });
});

Using the API with Curl

You can add a menu item with the following command:

curl -H "Content-Type: application/json" -d '{ "name": "papadums", "description" : "Thin Bread", "price" : "2.00" }' http://localhost:3000/food

Issue a get:

curl http://localhost:3000/food