node.js authentication using passport local strategy

Passport is a node module that is used as an authentication middleware. The only purpose of this module is to authenticate requests. It abstracts away the complexity of authentication process, which makes the application code more clean and maintainable. A strategy is a separate module that needs to be installed alone with the passport module. For example, if you are storing the username and password of a user in your own database, then a the module passport-local need to be installed together with passport. If you are using facebook, twitter, google’s api to authenticate users, then passort-facebook, passport-twitter and passport-goolge will have to be installed. Other than facebook, twitter, and google, passport supports hundreds of other api for authentication.

The code below demonstrates how to use passport with express framework to do authentication locally, aka authenticate the user using the username and password stored locally in the application.
passport_auth.js

var express  		= require('express'),
	app      		= express(),
	passport 		= require('passport'),
	LocalStrategy	= require('passport-local').Strategy,
	bodyParser 		= require('body-parser'),
	session      	= require('express-session');

// hardcoded users, ideally the users should be stored in a database
var users = [{"id":111, "username":"amy", "password":"amyspassword"}];

// passport needs ability to serialize and unserialize users out of session
passport.serializeUser(function (user, done) {
    done(null, users[0].id);
});
passport.deserializeUser(function (id, done) {
	done(null, users[0]);
});

// passport local strategy for local-login, local refers to this app
passport.use('local-login', new LocalStrategy(
	function (username, password, done) {
	    if (username === users[0].username && password === users[0].password) {
	        return done(null, users[0]);
	    } else {
	        return done(null, false, {"message": "User not found."});
	    }
	})
);

// body-parser for retrieving form data
app.use(bodyParser.json()); 
app.use(bodyParser.urlencoded({ extended: true }));

// initialize passposrt and and session for persistent login sessions
app.use(session({
	secret: "tHiSiSasEcRetStr",
	resave: true,
    saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());

// route middleware to ensure user is logged in
function isLoggedIn(req, res, next) {
    if (req.isAuthenticated())
        return next();

    res.sendStatus(401);
}

app.get("/", function (req, res) {
	res.send("Hello!");
});

// api endpoints for login, content and logout
app.get("/login", function (req, res) {
	res.send("<p>Please login!</p><form method='post' action='/login'><input type='text' name='username'/><input type='password' name='password'/><button type='submit' value='submit'>Submit</buttom></form>");
});
app.post("/login", 
	passport.authenticate("local-login", { failureRedirect: "/login"}),
	function (req, res) {
		res.redirect("/content");
});
app.get("/content", isLoggedIn, function (req, res) {
	res.send("Congratulations! you've successfully logged in.");
});
app.get("/logout", function (req, res) {
    req.logout();
    res.send("logout success!");
});

// launch the app
app.listen(3030);
console.log("App running at localhost:3030");

To run the above from command line assume you’ve saved it to passport_auth.js and you’ve installed node and npm.

npm install express passport passport-local body-parser express-session
node passport_auth.js

Then visit these urls to try out
http://localhost:3030/
http://localhost:3030/content
http://localhost:3030/login
http://localhost:3030/content
http://localhost:3030/logout
http://localhost:3030/content

Code explanation
Import necessary modules.

var express  		= require('express'),
    app      		= express(),
    passport 		= require('passport'),
    LocalStrategy	= require('passport-local').Strategy,
    bodyParser 		= require('body-parser'),
    session      	= require('express-session');

Hardcoded users, ideally the users should be stored in a database.

var users = [{"id":111, "username":"amy", "password":"amyspassword"}];

Passport serialize and deserialize user instances to and from the session. Here we are only serialize the id of the user in the session in order to keep the data in session small. When getting the user from the session, we deserialize the user by just getting the entire user object from the above hardcoded users for simplicity. The logic in the deserializeUser can be changed to use the id to retrieving the user from a database.

passport.serializeUser(function (user, done) {
    done(null, users[0].id);
});
passport.deserializeUser(function (id, done) {
    done(null, users[0]);
});

Passport local strategy for local-login, local refers to this app. Again the username and password are hardcoded to simplify the process for passport demo. In a real world application, the username and password would be stored in a database, and the logic in the new LocalStragegy would be retrieving the username and password from a database and then do the comparison.

passport.use('local-login', new LocalStrategy(
	function (username, password, done) {
	    if (username === users[0].username && password === users[0].password) {
	        return done(null, users[0]);
	    } else {
	        return done(null, false, {"message": "User not found."});
	    }
	})
);

Add body-parser as a middleware in express for retrieving form data, so the data would be saved to req.body

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

Initialize passposrt and and session for persistent login sessions

app.use(session({
	secret: "tHiSiSasEcRetStr",
	resave: true,
    saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());

Route middleware to ensure user is logged in. Passport attached a handy function isAuthenticated() to the req object, and we can use it to determine if the user is authenticated.

function isLoggedIn(req, res, next) {
    if (req.isAuthenticated())
        return next();

    res.sendStatus(401);
}

Home page

app.get("/", function (req, res) {
	res.send("Hello!");
});

Login page. It just sends the form in string format. In a real world application, the request handler function will usually use a template framework to generate a html code and renders that to the response.

app.get('/login', function (req, res) {
	res.send("<p>Please login!</p><form method='post' action='/login'><input type='text' name='username'/><input type='password' name='password'/><button type='submit' value='submit'>Submit</buttom></form>");
});

Login post request. It calls passport.authenticate to do the authentication, if the authentication is successful, it then goes to the request handler function which then redirects to the content page.

app.post('/login', 
	passport.authenticate('local-login', { failureRedirect: '/login'}),
	function (req, res) {
		res.redirect('/content');
});

Content page, it calls the isLoggedIn function defined above first. If the user is logged in, then proceed to the request handler function, else the isLoggedIn will send 401 status instead.

app.get('/content', isLoggedIn, function (req, res) {
	res.send("Congratulations! you've successfully logged in.");
});

Logout request handler, passport attaches a logout() function to the req object, and we call this to logout the user, same as destroying the data in the session.

app.get('/logout', function(req, res) {
    req.logout();
    res.send('logout success!');
});

Launch the app 🙂

app.listen(3030);
console.log('App running at localhost:3030');

Search within Codexpedia

Custom Search

Search the entire web

Custom Search