Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passport.js User Login & Authentication

For the past few days I have been developing my first User login & authentication system using Passport.js. Awkwardly enough, I have finished it and it works just as intended. The problem is, even though I have read a lot of articles and checked tens of examples online, I seem to not completely understand the code per se. I have no issues understanding the process behind it and why it has to happen like that. I would really appreciate it if you could clarify some parts of the code for me. This is the working code, stored in my app.js file:

// Passport session setup
passport.serializeUser(function (user, done) {
    done(null, user._id);
});

passport.deserializeUser(function (id, done) {
    User.findById(id, function(err, user) {
        done(err, user);
    });
});

// Use the Local Strategy within passport
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: ' + username});
        }

        user.comparePassword(password, function(err, isMatch) {
            if (err) {
                return done(err);
            }

            if (isMatch) {
                return done(null, user);
            } else {
                return done(null, false, { message: 'Invalid Password' });
            }
        });
    });
}));

var app = module.exports = express();

app.configure(function () {
    app.set('views', path.join(__dirname + '/views'));
    app.set('view engine', 'html');
    app.engine('html', hbs.__express);
    app.use(express.logger());
    app.use(express.cookieParser());
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.session({ secret: 'xxx' }));    
    app.use(passport.initialize());
    app.use(passport.session());
    app.use(app.router);
    app.use(express.static(path.join(__dirname + '/public')));

});

I am using MongoDB (User - mongoose model). Also, to store passwords in the database I am currently using bcrypt.

I think that the most critical part that I do not understand here is the done callback function. I can understand that it simply passes some values, and I know that much to realize that the first parameter of it is the error and the second the data. Still, I do not fully grasp it because I haven't specifically provided one as a parameter. For example, if I would have a function like this:

// Random Function
var randomFunction = function (a, b, done) {
    done(a, b);
};

// Then I would call the randomFunction providing my own **done**
randomFunction('Random', 'Words', function(a, b) { return a + b; });

Still, in my example I am not the one specifying the done callback. Is it simply a required callback function parameter or is it the same as the next function in a normal middleware such as:

function middleware (req, res, next) {
    next(req.user); // pass the req.user to next middleware
}

Also, where does Passport.js bind the user that it handles? Does it bind it to req.user? And how can I pass it to certain views in order, for example, to display the username?

I am looking forward to your feedback!

Thank you!

like image 544
vladzam Avatar asked Oct 24 '13 07:10

vladzam


People also ask

What does req login () do in Passport?

Passport exposes a login() function on req (also aliased as logIn() ) that can be used to establish a login session. req.

What is Passport login 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.


1 Answers

Done callback

Look at the code of Local Strategy:

function Strategy(options, verify) {
  ...
  this._verify = verify;
  ...
}

verify is the function that will be used by strategy to verify a user and you've specified it here:

passport.use(new LocalStrategy(function (username, password, done) {
    // your verification code here
}));

Later in strategy you can find authenticate method that calls verify function from the step above:

this._verify(username, password, verified);

So, you now see where username, password and done==verified come from. Later in your code you will call the done callback with (err, user, info) arguments. In a few words, done is needed to finish asynchronous procedure of user verification.

req.user and views

Yes, you are right about req.user. So you can pass it to your views by two ways:

  1. As an argument of res.render function. See docs

    res.render('some-template', { name: req.user });
    
  2. Use res.locals as some kind of context provider (now user object will be available in all the views that are defined in app.router). See docs

    // before app.use(app.router);
    app.use(function(req, res, next) {
       res.locals.user = req.user;
       next();
    });
    
like image 69
Peter Gerasimenko Avatar answered Oct 01 '22 12:10

Peter Gerasimenko