Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js theory - Node.js, Express.js, Passport.js

Tags:

node.js

I'm struggling with some of the theory and conventions behind some Node.js frameworks. I'm new to Node.js. I'm trying to set up a scenario where I have an MVC framework where I define a set of Controllers which do basic rest functionality, and some of my Controller routes require authentication. If you're not authenticated, it should send you to the Login page, but after you login, it sends you back to the page you had previously requested. I've looked at a bunch of tutorials, and I've looked at some of the questions on StackOverflow, but I think the problem is that something is just not clicking. I'm hoping one of you can kinda explain to me some of the theory behind what's going on, and perhaps point me in the right direction for my problem. My code is below. My real problem is that I don't really understand this next() paradigm. Maybe I'm even doing this all wrong and thinking about it the wrong way. Maybe you can let me in on some good ideas as well.

Edit:

======

I found a solution. I'll answer my question later on for anyone else who may be looking to get some information and solve this problem, too.

-- Server.js

/**
 * Clancy server implementation (using Express)
 */
require('console-trace')({
    always: true,
    right: true,
    colors: true
})

/**
 * Include our server's needed objects. 
 **/
var express = require('express');
var _ = require('underscore');
var cons = require('consolidate');
passport = require('passport'),
LocalStrategy = require('passport-local').Strategy;
var db = require('./lib/db');
var colors = require('colors');
var Users = db.users;
var People = require('./controllers/People');
var Login = require('./controllers/Login');

/**
 * This provides our MVC context object
 **/
var app = express();


/**
 * This is our router definition for the server 
 **/
app.engine('html', cons.underscore);

// set .html as the default extension 
app.set('view engine', 'html');
app.set('views', __dirname + '/views');

/**
 * Set up the server to allow static content, cookies, session, and
 * parsing the server.  Also, we initialize authentication and our
 * routing mechanism.
 *
 */

app.configure(function () {
    app.use('/static', express.static(__dirname + "/webapp"));
    app.use(express.cookieParser());
    app.use(express.bodyParser());
    app.use(express.session({
        secret: 'keyboard cat'
    }));
    app.use(passport.initialize());
    app.use(passport.session());
    app.use(app.router);
});

/**
 * This lets authentication know how it should store
 * and grab users from a request to pass to a mapping
 * function.
 */
passport.serializeUser(function (user, done) {
    done(null, user._id);
});

passport.deserializeUser(function (id, done) {
    Users.findOne({
        _id: db.bson.ObjectID(id)
    }, function (err, user) {
        done(err, user);
    });
});

/**
 * This sets up which authentication strategies we support.
 * as of right now, LocalStrategy (our own username/password)
 * is all we support.
 *
 */
passport.use(new LocalStrategy(

function (username, password, done) {
    Users.findOne({
        username: username
    }, function (err, user) {
        if (err) {
            return done(err);
        }
        if (!user) {
            return done(null, false, {
                message: 'Incorrect username.'
            });
        }
        if (!(user.password == password)) {
            return done(null, false, {
                message: 'Incorrect password.'
            });
        }
        console.info(user.password + " " + password.yellow);
        console.info(!(user.password == password).yellow);
        console.info(user._id);
        return done(null, user);
    });
}));

/**
 * Path mapping
 */

// Index mapping
app.get('/', function (req, resp) {
    resp.render('index', {
        title: "Welcome!"
    });
});

// Allow login, and set up the dependency for passport.
Login.setPassport(passport);
app.get("/login", Login.loginForm);
app.get("/login/error", Login.loginForm);
app.post('/login', passport.authenticate('local', function (req, res, next) {
    passport.authenticate('local', function (err, user, info) {
        // This is the default destination upon successful login.
        var redirectUrl = '/people';

        if (err) {
            return next(err);
        }
        if (!user) {
            return res.redirect('/');
        }

        // If we have previously stored a redirectUrl, use that, 
        // otherwise, use the default.
        if (req.session.redirectUrl) {
            redirectUrl = req.session.redirectUrl;
            req.session.redirectUrl = null;
        }
        req.logIn(user, function (err) {
            if (err) {
                return next(err);
            }
        });
        res.redirect(redirectUrl);
    })(req, res, next);
}));

app.get('/logout', Login.logout);

// People Controller has a dependency on the Passport library
People.setPassport(passport);

// These are our definitions for paths the People Controller can handle.
app.get("/people", People.list);
app.get("/people/:id", People.get);

// These are the error handler mappings.
app.use(function (req, res, next) {
    // the status option, or res.statusCode = 404
    // are equivalent, however with the option we
    // get the "status" local available as well
    res.render('404', {
        status: 404,
        url: req.url
    });
});

app.use(function (err, req, res, next) {
    // we may use properties of the error object
    // here and next(err) appropriately, or if
    // we possibly recovered from the error, simply next().
    console.error(("ERROR: " + err.toString()).red);
    res.render('500', {
        status: err.status || 500,
        error: err
    });
});
app.listen(3000);
console.info('The Clancy server is listening on port: 3000'.green);

-- People controller

/**
 * People Controller
 */
var db = require('../lib/db');
var auth = require('../lib/authUtils');
/**
* People constructor.
* ===================
* The people constructor has dependencies on the database, 
* and on the Passport middleware.  The db object doesn't
* care about maintaining state, so we can just include that
* here, however the Passport plugin needs to have all of the
* stuff the server defines.  So, it's passed in.
*/
function People(){
    var passport;
}
People.prototype = {
        list: function(req, resp){
            auth.ensureAuth(req, resp);
            console.info("user info: " + user._id);
            resp.render('index', {
                title: "User",
                users: [1,2,3]
            });
        },
        get: function(req, resp){

            console.log('> get person' + req.params.id);

            db.users.find( {_id: db.bson.ObjectID(id)}, function(err, users){
                if(err || !users) console.log("No user found");
                resp.send(users);
            });
        },
        setPassport: function(pass){
            this.passport = pass;
        },
        getPassport: function(){
            return this.passport;
        }
}

module.exports = new People();

-- Login Controller

/**
 * People Controller
 */

/**
* Login constructor.
* ===================
* The Login constructor has dependencies on the Passport middleware.  
* The db object doesn't care about maintaining state, so we can just 
* include that here, however the Passport plugin needs to have all 
* of the stuff the server defines.  So, it's passed in.
*/
function Login(){
    var passport;
}
var l = Login.prototype;
Login.prototype = {
        loginForm: function(req, resp){
            var url = require('url').parse(req.url, true);
            console.info('url string: ' + url.pathname.yellow);
            if(url.pathname === '/login/error')
            {
                resp.render('login', {
                    title: "Login to FormPickle.com",
                    message: "Your username or password was incorrect."
                });
            }
            console.info('Trying to login'.yellow);
            resp.render('login', {
                title: "Login to FormPickle.com",
                message: ""
            });
        },
        setPassport: function(pass){
            l.passport = pass;
        },
        getPassport: function(){
            return l.passport;
        },
        logout: function(req, resp){
            req.logout();

            resp.render('logout');
        }
}

module.exports = new Login();

-- DB Middleware

/**
 * DB
 */

var databaseURI = "localhost:27017/clancy";
var collections = ["users", "forms"];
var db = require("mongojs").connect(databaseURI, collections);

module.exports = db;

-- AuthUtils.js

/***
* Define a middleware function for authenticated routes to store the original URL
*
*/
function Auth(){

};

Auth.prototype = {
    ensureAuth: ensureAuthenticated(req, resp, next)
}
var ensureAuthenticated = function (req, res, next) {
  if (req.isAuthenticated()) { return next(); }

  // If the user is not authenticated, then we will start the authentication
  // process.  Before we do, let's store this originally requested URL in the
  // session so we know where to return the user later.

  req.session.redirectUrl = req.url;

  // Resume normal authentication...

  logger.info('User is not authenticated.');
  req.flash("warn", "You must be logged-in to do that.");
  res.redirect('/login');
}

module.exports = new Auth();

Thank you guys in advance. I love the community on StackOverflow. You guys are always so awesome when learning a new piece of technology.

like image 765
Kyle Richter Avatar asked Dec 18 '12 17:12

Kyle Richter


People also ask

What is passport js in node JS?

Passport is a popular, modular authentication middleware for Node. js applications. With it, authentication can be easily integrated into any Node- and Express-based app. The Passport library provides more than 500 authentication mechanisms, including OAuth, JWT, and simple username and password based authentication.

What is the difference between node JS and ExpressJS?

NodeJS is an event-driven, non-blocking I/O model using JavaScript as its main language. It helps to build scalable network applications. Express is a minimal and flexible Node. js web application framework that provides a robust set of features for web and mobile applications.

Why do I need passport for Node JS?

Passport is authentication middleware for Node.js. Extremely flexible and modular, Passport can be unobtrusively dropped in to any Express-based web application. A comprehensive set of strategies support authentication using a username and password, Facebook, Twitter, and more.


1 Answers

Returning next are usually for the Connect middleware. You're passing the reference of the next function to execute. Middleware act like filters, or linked-listed layers(kind of, via reference) that your function calls and follows through before getting to your resources, does logic and then decides to exit/redirect or go to the next middleware. Middleware might be authentication, such as you're using. Even though Passport is a discrete and well-written module, you're implementing it as middleware here(which is normal), which is basically an authentication filter via your ensureAuthenticated function: you basically just created your own middleware right there(Achievement Unlocked). You usually put all your Middleware right before your Routing functions execute.

What you're defining as -- DB Middleware isn't middleware, from what I can tell. It looks more like a module that you're trying separate concerns with(which is good). I'd call that the beginnings of your model module.

It looks like your controllers might get out of hand quickly. I'd suggest researching Routes.

I'm not an expert by any means in regards to Node.js, Express.js and Passport.js, but I'm successfully separating concerns and coding organized in a partially worked project: https://github.com/Tzvayim/theArk/blob/master/app.js

like image 54
EhevuTov Avatar answered Sep 29 '22 17:09

EhevuTov