Theodore Lowe, Ap #867-859 Sit Rd, Azusa New York
Theodore Lowe, Ap #867-859 Sit Rd, Azusa New York
Since its launch in early 2010s, NodeJS has rapidly changed the way in which web applications are being developed. It, thus, doesn’t come as a surprise that several top names in innovation, media and technology employ NodeJS as the mainstay of their backend computing.
What makes NodeJS a hot property in the developer community is the fact that it enables rapid development of complex user-centric web applications that can easily handle large amount of concurrent data being processed. This has been a key factor in why NodeJS is the building block of several popular applications that you might be using daily such as Netflix, Uber, PayPal among others. In our earlier blogs, we had pointed out in detail why NodeJS is a power to be reckoned with.
One thing to notice in the above list is that almost all of these applications are highly user-centric and hence involves users creating accounts on the application and sharing their personal and/or contact information. With that point, concerns about user privacy and users authentication are natural to crop up.
After years of absence of a single unifying authentication system, the developer community has just found the answer to all their prayers – Passport JS.
Passport.js acts as the authentication middleware for Node.js. It is extremely flexible and modular in the sense that it can be unobtrusively dropped in to any express-based web application.
Passport.js’s sole purpose is to authenticate requests, which it does through an extensible set of plugins known as strategies. It offers more than 300 authentication strategies to support authentication using a username & password, twitter, facebook, and more.
Over the years, we have developed several applications based on NodeJS and have thus gained high level expertise in leveraging the goodness of Passport JS to make user authentication a seamless process.
We recently developed an eCommerce store for a Fortune500 Fashion brand and are glad to share how we incorporated Passport JS into the application.
Let’s Get Started with Installing Passport.js
> Installing Passport.js
Begin with installing Passport.js via NPM in your application.
$ npm install passport
Now since we are using facebook and twitter strategies as well, let’s install local plugin for local authentication as well as their plugins.
$ npm install passport-local
$ npm install passport-facebook
And then:
passport = require("passport");
LocalStrategy = require('passport-local').Strategy;
FacebookStrategy = require('passport-facebook').Strategy;
Adding passport middleware
Next up, you will have to make sure that this middleware is in use.
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({ secret: 'SECRET' }));
app.use(passport.initialize());
app.use(passport.session());
Database and Models
Now we used mongoose to apply MongoDB serve in our web application
Mongod
mongoose.connect("mongodb://localhost/myapp");
var LocalUserSchema = new mongoose.Schema({
username: String,
salt: String,
hash: String
});
var Users = mongoose.model('userauths', localUserSchema);
var FacebookUserSchema = new mongoose.Schema({
fbId: String,
email: { type : String , lowercase : true},
name : String
});
var FbUsers = mongoose.model('fbs',FacebookUserSchema);
Strategies
Next we need to set the strategies to be initialized using passport initialize middleware.
Local Strategy
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.' });
}
hash( password, user.salt, function (err, hash) {
if (err) { return done(err); }
if (hash == user.hash) return done(null, user);
done(null, false, { message: 'Incorrect password.' });
});
});
}));
First, you need to register a new application at Facebook Developers to get clientID and ClientSecret.
Kindly note that since we are running this application locally so we need to set up URL to http://localhost:3000. Also in callbackURL we passed the “/auth/facebook/callback” route at the end of the address because the authentication data would send to this route to connect to Facebook.
passport.use(new FacebookStrategy({
clientID: "YOUR ID",
clientSecret: "YOUR CODE",
callbackURL: "http://localhost:3000/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, done) {
FbUsers.findOne({fbId : profile.id}, function(err, oldUser){
if(oldUser){
done(null,oldUser);
}else{
var newUser = new FbUsers({
fbId : profile.id ,
email : profile.emails[0].value,
name : profile.displayName
}).save(function(err,newUser){
if(err) throw err;
done(null, newUser);
});
}
});
}
));
Another important aspect of configuring passport is searlizeUser and DesearlizeUser, which basically enables the user to req.user and establish a secure session via a cookie set is the user’s browser.
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
FbUsers.findById(id,function(err,user){
if(err) done(err);
if(user){
done(null,user);
}else{
Users.findById(id, function(err,user){
if(err) done(err);
done(null,user);
});
}
});
});
In deserializeUser we basically locate a user in the database based on the provided id by facebook and pass the result to done().
Function Handlers
Here we have 2 helper functions. One to check if the user is already registered and logged in to the application and if so redirect to the previous session and if not registered, the user is redirected to sign up process.
function authenticatedOrNot(req, res, next){
if(req.isAuthenticated()){
next();
}else{
res.redirect("/login");
}
}
function userExist(req, res, next) {
Users.count({
username: req.body.username
}, function (err, count) {
if (count === 0) {
next();
} else {
// req.session.error = "User Exist"
res.redirect("/singup");
}
});
}
Routes
The routes in themselves are quite easy to configure.
First, We need to define the user routes once he decides to sign up via facebook. We post request to “/signup” using passport built in login method which will automatically redirect the user to the designated screen.
Second, we also define the alternate route in case of a failed login process.
app.get("/auth/facebook", passport.authenticate("facebook",{ scope : "email"}));
app.get("/auth/facebook/callback",
passport.authenticate("facebook",{ failureRedirect: '/login'}),
function(req,res){
res.render("loggedin", {user : req.user});
}
);
It uses passport.authenticate() method to pass via facebook strategy.
Third, If we don’t use facebook or etc for signing in process and the user simply fills in the login form in “/login” route, it will send a post request to “/login” which in turn will be verified and authenticated by passing “local” as a first argument of passport.authenticate()method. Also there is some options like successRedirect, failureRedirect.
And lastly, passport have a built in logout() method that instead of destroying session like what we did before, we can use req.logout() on our “/logout” route.
Winding Up
Passport.js has proved to be instrumental in enabling developers to make good use of NodeJS and to continue developing robust user-centric applications that make this world a better place.
By providing itself as the middleware really, Passport JS makes the job of developers easier by providing an easy and quick way of taking care of the user privacy and authentication paradigms.
If you are a developer or just someone looking to have a NodeJS web application developed, we will be more than happy to help you in case you are stuck somewhere. Or if you just want to share your experience of working on NodeJS and PassportJS.
Ritika is a Senior Tech Lead at ClixLogix. A technology enthusiast by day and a foodie by night. She loves to explore ex