DEV Community

Deepanshu Arora
Deepanshu Arora

Posted on

Working on Social Authentication through PassportJS and integrating it with our Backend ExpressJS App (Part - 1 )

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.

Screenshot 2020-11-09 at 8.01.33 PM.png

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");

});
Enter fullscreen mode Exit fullscreen mode

The Main thing to notice here is these two statements

app.use(passport.initialize());
app.use(passport.session());

Enter fullscreen mode Exit fullscreen mode

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));

}

Enter fullscreen mode Exit fullscreen mode

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));
Enter fullscreen mode Exit fullscreen mode

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"]
  }
Enter fullscreen mode Exit fullscreen mode

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 *

Screenshot 2020-11-09 at 11.12.28 PM.png

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

Screenshot 2020-11-09 at 11.16.07 PM.png

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 :

Screenshot 2020-11-09 at 11.20.05 PM.png

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");
      }
    }
  };
Enter fullscreen mode Exit fullscreen mode

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,
};

Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
hendrik77 profile image
Hendrik Neumann

Hi,
I likedthe passport introduction. AAny chance part 2 will be released soon?

Best
Hendrik

Collapse
 
deepansharora27 profile image
Deepanshu Arora

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