Recently , While Working on My Side Project , I thought why not authenticate the end user who is going to access my application with the help of various Social Network platforms such as Facebook and Twitter.
Many People who are reading this blog , will resonate with the fact that , it is so much easier to signup/login via social network platforms such as Facebook and Twitter , instead of manually going through this traditional process of filling in your E-mail Address , Your Name , then setting up a password. This traditional process takes up a lot of time.
That's why we have this savior for Developers , who want to provide their users with the comfort of , signing in/signing up to their web app with the Social Authentication Mechanism and that Savior is PassportJS.
PassportJS is basically a authentication middleware for NodeJS. The interesting fact about PassportJS is that , it can be integrated with any Express Based Web Application.
Let's dive into it and explore more about it
At the root , we have our server.js file , and main role of this file is instantiating our server , and instantiating the middleware for PassportJS.
I am assuming that you are already familiar with the fundamentals of Express and NodeJS , therefore I will not go into the deep nitty gritties of every line of code , and I will focus mostly on explaining the concepts related to PassportJS
const express = require('express');
const app = express();
const passport = require('passport');
app.set("port" , process.env.PORT || 3000);
app.use(passport.initialize());
app.use(passport.session());
app.listen(app.get("port"), () => {
console.log("The Server is Up and Running");
});
The Main thing to notice here is these two statements
app.use(passport.initialize());
app.use(passport.session());
Let's understand what we're doing in each of the above statements :
The First Statement basically imports the Passport Middleware Function which is essentially designed for integration with Express. It basically gives Passport the access to the request and response streams(or we can say request and response objects)
The Second Statement basically integrates Express Session Middleware with PassportJS.
Now , comes the main deal where we will now purely deal with PassportJS.
Now , we have another file named auth.js , where we will now deal with the
Social Authentication Logic Using PassportJS
const passport = require('passport');
const FacebookStrategy = require("passport-facebook").Strategy ;
/*
Importing this User Route because , here I have defined several helper functions and since I am using Mongoose as an Interface to communicate with my MongoDB Database , I have performed all of the queries on the database in this File(Will attach the screenshot of this route file below so that you can know how I have performed query on database using the helper functions.
*/
const user = require("../routes/users");
module.exports = ()=> {
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
//Find the User Using the _id Property inside of our MongoDB Collection :
user
.findById(id)
.then((user) => done(null, user))
.catch((error) => console.log("Error While Deserializing the User"));
});
let authProcessor = async (accessToken, refreshToken, profile, done) => {
const result = await user.findOne(profile.id);
if (result) {
done(null, result);
} else {
const newUserSaved = await user.createNewUser(profile);
if (newUserSaved) {
done(null, newChatUser);
} else {
console.log("Error While Creating a New User");
}
}
};
passport.use(new FacebookStrategy(config.fb, authProcessor));
}
Let's start from*top-down* and see what we are trying to accomplish here:
Firstly , we have imported this package named as passport-facebook and while importing this package , we are invoking this Strategy Constructor Function. This statement basically goes into the passport-facebook module and imports the constructor function , which internally uses the OAuth 2.0 Module to provide authentication and login functionality to your Application.
Now , if we have a look at the end of the file , we have this code :
passport.use(new FacebookStrategy(config.fb, authProcessor));
This is basically the passport middleware function , here inside this middleware function , we are passing in a new instance of the FacebookStrategy Module.
(SideNote : If you are Familiar with Object Oriented Programming, you might be already familiar that in order to invoke constructor of a class automatically , we just create a new instance of that class. The Same thing we are doing here as well.)
Inside this new instance Of FacebookStrategy , we will pass two things as parameters , A Configuration Block which basically is providing access to the ApplicationID , SecretKey and a A Callback URL
This is the Configuration block that I am talking about :
"fb": {
"clientID": "3417694501659554",
"clientSecret": "e9b2089b3c04886396983be6d01cbb7f",
"callbackURL": "//localhost:3000/auth/facebook/callback",
"profileFields": ["id", "displayName", "photos"]
}
It is basically an object with these key-value pairs . The ClientID and ClientSecret is provided by Facebook only and it will be provided by Facebook when you register your application on Facebook .
You can do so by visiting *www.developers.facebook.com *
Next Login with your Account , then you will see an option in the top-right corner named as MyApps , click on that. After Clicking on MyApps Option , you will see an option named as Create New App , click on that .
Next , it will show you a set of options asking you what purpose does your app serve
Since we are working on creating a Web App , Click on the Last Option and then proceed forward . Fill all the required details about your Web Application .
At last when everything gets completed , Go to Settings and then click on Basic.
You will see something like this :
This is the AppID and the App Secret Key that is provided by Facebook , so that you can use it's API , to proceed with the Social Authentication.(SideNote : It is advised that you should not share your SecretKey with anyone)
The CallBack URL Key is used such that when you are successfully authenticated by facebook , using your credentials , then facebook needs a way to get back to your Application , this purpose is served by the CallBack URL Property. We Type in the URL , where we want Facebook to redirect to , after we are successfully authenticated.
The profileFields property is basically an array of strings , and those strings represent what do we want to request back from the Facebook Server for our Application.
The Next Parameter inside this Passport Middleware is an Callback Function which has four Parameters :
AccessToken , RefreshToken , profile(A Profile Object) and done(A Method)
This Callback Function is technically known as the VerifyCallbackbecause its job is to find a User Profile in the local MongoDB Instance using the profile.id which refers to the UserID as sent by Facebook or any other Social Authentication Provider.
The AccessToken and RefreshToken are Provided by Facebook as a part of OAuth 2.0 Process.
The done() method gets the data out of the Authentication Pipeline and provides it to the requesting application.
As we can see in the code snippet for the callback function , we are querying our Database for finding if the user is present in our Local MongoDB instance or not . If the user is present , we directly return it to the app using the done() method
else we create the new user in the database and then return it to requesting application using done() method.
let authProcessor = async (accessToken, refreshToken, profile, done) => {
const result = await user.findOne(profile.id);
if (result) {
done(null, result);
} else {
const newUserSaved = await user.createNewUser(profile);
if (newUserSaved) {
done(null, newChatUser);
} else {
console.log("Error While Creating a New User");
}
}
};
Here is the code of the helper function users route , as i mentioned earlier :
const router = require("express").Router();
const UserModel = require("../models/User");
let findOne = (profileID) => {
return UserModel.findOne({
profileId: profileID,
});
};
let createNewUser = async (profile) => {
return new Promise((resolve, reject) => {
let newChatUser = new UserModel({
profileId: profile.id,
fullName: profile.displayName,
profilePic: profile.photos[0].value || "",
});
newChatUser.save((error) => {
if (error) {
console.log("Error in Creating New User");
reject(error);
} else {
resolve(newChatUser);
}
});
});
};
let findById = (id) => {
return new Promise((resolve, reject) => {
UserModel.findById(id, (error, user) => {
if (error) {
reject(error);
} else {
resolve(user);
}
});
});
};
module.exports = {
findOne,
createNewUser,
findById,
};
That's all for this Part 1 of the Blog . In the Part 2 , I will continue the discussion further and there we will talk about Serializing and De serializing user data
I hope , I was able to provide some value with my content.
If you liked it , please share it with your Network .
You can connect with me through following platforms :
LinkedIn : https://www.linkedin.com/in/deepansharora27/
Twitter UserHandle : Deepansharora27
Instagram : deepanshu.codes
Top comments (2)
Hi,
I likedthe passport introduction. AAny chance part 2 will be released soon?
Best
Hendrik
Hii Hendrik ,
Sorry for the late reply , was busy on one of my side project , will try to release part 2 ASAP
Regards
Deepanshu Arora