Setup passportjs for local authentication and authorization using expressjs
Packages
npm install --save passport passport-local passport-github connect-flash
For the purpose of this demo, I will also be using a library called ‘async‘ that makes it easy and clear to process multiple asynchronous functions.
npm install --save async
Passport Setup
Next, we will create a file where we configure passport to perform the authentication. In our case, I will be creating a config/passport.js file.
Lets setup the skeleton of this file
config/passport.js
var LocalStrategy = require('passport-local').Strategy,
async = require('async');
// Create a list of users just for demo.
// In reality, you will probably query a database
// to fetch this user
var users = {
'[email protected]': {
id: 'userOne',
password: 'one',
email: '[email protected]'
},
'[email protected]': {
id: 'userTwo',
password: 'two',
email: '[email protected]'
}
};
function createAccount (payload, callback) {
// Call API to create account
users[newUser.email] = newUser;
callback(null, newUser);
}
// Invokes callback with an error if email already exists, null otherwise
function findExistingEmail (email, callback) {
// Call API to check if the email already exists
if(users[email]) {
callback({err: 'Email Already Exists'});
} else {
callback(null, null);
}
}
module.exports = function (passport) {
// All our authentication code for signup and signin will go here
}
Continuing with our module.exports, we will now define the functions that handle the session data once a user has successfully authenticated.
config/passport.js
module.exports = function (passport) {
passport.serializeUser(function(user, done) {
// This is what passport will save in the session/cookie
done(null, user.email);
});
passport.deserializeUser(function(email, done) {
// Use the email saved in the session earlier
// to fetch the actual user
var user = users[email];
done(null, user);
});
}
SignIn
Lets add our first crucial function where we will perform the actual task of login.
config/passport.js
module.exports = function (passport) {
// ...
// We name our strategy 'local-login'.
// You can use any name you wish
// This is the name you will refer to later in a route
// that handles the login on client request
passport.use('local-login', new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
if(users[email] && users[email].password === password) {
return done(null, users[email]);
} else {
done(null, false, req.flash('message', 'Invalid email or password'));
}
}));
}
As you can tell in the code above, I expect the login form to have two fields with the names – ’email’ and ‘password’ respectively. Also, by setting the passReqToCallback to true, I can access any other fields on the request object if need be.
SignUp
Now, lets write some code that handles signup. For our case, I will use async to chain the function. We will first check if the email already exists. If not, asynch will proceed with invoking the function that creates an account.
config/passport.js
passport.use('local-signup',
new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // Pass back the entire request to the callback
}, function (req, email, password, done) {
process.nextTick(function() {
async.auto({
doesEmailAlreadyExist: function (cb, results) {
findExistingEmail(email, cb);
},
newAccount: ['doesEmailAlreadyExist',
function (cb, results) {
var newUser = {
email: email,
password: password
};
createAccount(newUser, cb);
}
]
}, function (err, results) {
if (err) {
done(null, false, req.flash('signupMessage', err.message));
} else {
done(null, results.newAccount);
}
});
});
})
);
With that out of the way, lets setup our app.js and make it passport aware.
Setup app.js
Add the following lines of code just below your first set of variable declarations.
app.js
// --- Authentication ---
var passport = require('passport'),
flash = require('connect-flash');
require('./config/passport')(passport);
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
// --------
Since we are using a session, passport requires express.session to be initialized before it can be used. In our code, the express session was already initialized a few lines ago such as
app.js
app.use(session({
secret: config.session.SESSION_SECRET, // This can be any string
saveUninitialized: true,
resave: true
}));
We will also include the passport reference in our settings object that is passed down to all the routes
app.js
var settings = {
config: config,
passport: passport
};
Route handlers
Now that we have everything in place, lets create simple route handlers for the login and signup actions.
In our routes folder, I will create a file called auth.js that will handle all the login actions. In the future, we will add route handlers for OAUTH based login strategies in the same file.
routes/auth.js
module.exports = function(app, settings){
var url = require('url'),
express = require('express'),
router = express.Router(),
passport = settings.passport;
router.post('/local-login', passport.authenticate('local-login', {
successRedirect : '/home.html',
failureRedirect : '/login.html',
failureFlash : true
}));
router.post('/local-signup', passport.authenticate('local-signup', {
successRedirect : '/home.html',
failureRedirect : '/signup.html',
failureFlash : true
}));
app.use('/auth',router);
};
If you look at the code above, in my router, I handle the local-signup and local-signin actions tand specify the handler function to be passport authenticate. The first argument for authenticate must match the name of the strategy that we used in our passport configuration file we created earlier. Note that I named everything the same for consistency and ease of maintenance.
Also, we add this newly added route handler file to our routes/index.js
routes/index.js
// Divide all of your modules in different files and
// require them here
module.exports = function(app, settings){
require('./main')(app, settings);
require('./auth')(app, settings);
require('./home')(app, settings);
};
And thats pretty much it. Now you can create a login and signup form that submits to the above url’s and you’re all set to use passport as a way to login and register for your application.
The full source code for this demo can be found in the passport-local-login branch of the node-starter-kit repository.