Setting up Social Logins(Google + Microsoft) with Node.js and Passport.js
Tags: node, javascript, webdev, authentication
Published: 2020-10-07 20:08:32

Before diving into code, let's first briefly discuss what the heck is **Oauth**?

### **What is Oauth:**

**OAuth(or Open Authorization)** is one of the approaches for authenticating a user in an application. It makes it much easier and faster for an end-user to choose a social login(Google, Outlook, or Twitter, etc) to signup on an application rather than the traditional (email/password) signup form.

Simply, it is a way to provide third-party websites or apps access to user's data(name, email, etc.) without requiring them to share their credentials.

### **Oauth Abstract View**

<div style="text-align:center">![oauth basic](https://blogs-static-assets.s3.amazonaws.com/node/oauth-basic.png)</div>

A lot of things are happening behind the scenes and a detailed explanation is provided in the below image as to how we are going to set this up in our Node app.

### **Oauth Developer's View**

<div style="text-align:center">![oauth complex](https://blogs-static-assets.s3.amazonaws.com/node/oauth_complex.png)</div>

Let's now set up the above login flow by first setting up our app on Google and Microsoft Console.

### **Step 1: Google - Create client ID and client secret**

1.  Head to [Google API Console](https://console.developers.google.com/) and sign in using your email id.
2.  From the project drop-down create a new project by filling in the project name and organization(optional).
3.  In the sidebar under "APIs & Services", select **OAuth Consent Screen**, choose the appropriate **User Type** basis requirement. For public-facing app select external.
4.  Fill in the application name, logo(optional), support email(optional), and hit Save.
5.  Switch to **Credentials** tab from the sidebar and from the Create credentials drop-down list, choose OAuth client ID.
6.  Under Application type, select Web application.
7.  In Authorized redirect URI add http://localhost:5500/auth/google/redirect for dev env, for production env, this will be the server IP address or domain name followed by /auth/google/redirect
8.  Press the Create button and copy the generated **client ID** and **client secret**. This will be used later in the Node app

### **Step 2: Microsoft - Create client ID and client secret**

1.  Head to [Microsoft Azure portal](https://portal.azure.com/) and sign in using your email id.
2.  Search for **App registrations** from the search bar.
3.  Select **New registration** from the top and fill in your application name.
4.  Choose Account type basis your requirement. For our application, it will be **personal accounts + organizational directory**.
5.  In Redirect URI add http://localhost:5500/auth/microsoft/redirect.
6.  Press the Register button to register your app.
7.  From the sidebar select **Overview** tab and copy the Application (client) ID. To generate the client secret head to **Certificates & secrets** from the sidebar and click on **New Client Secret** from the Client secrets section. Copy the generated secret for future use.

### **Step 3: Passport Setup**

It is an **authentication middleware** and can be easily configured with express. It provides a comprehensive set of strategies supporting authentication using a username and password, Google, Facebook, Twitter, and many more.

Install the following packages:

```javascript
npm install passport passport-google-oauth20 passport-microsoft --save
```

### **Step 4: Routes Setup**

We will be setting up **token-based** redirection basis social login in our app. This flow is helpful when we have an existing app with email, password setup, and social logins are being added as an enhancement.

Use **express-generator** to set up the basic boilerplate for the express app.

Setup the following routes in index.js:

```javascript
app.get('/auth/google', passport.authenticate('google', { scope: ['profile', 'email'], session: false }));
app.get('/auth/google/redirect', passport.authenticate('google', { session: false, failureRedirect: `https://localhost:3000/login` }), (req, res) => {
  res.redirect(req.user); //req.user has the redirection_url
});

// Microsoft Routes
router.get('/auth/microsoft', passport.authenticate('microsoft', { session: false }));
router.get('/auth/microsoft/redirect', passport.authenticate('microsoft', { session: false, failureRedirect: `https://localhost:3000/login` }), (req, res) => {
  res.redirect(req.user);
});
```

Over here the route **/auth/google** or **/auth/microsoft** is called when the user clicks Log in with Google or Microsoft in the browser. Behind the scenes, passport communicates with Google/Microsoft and directs the user to their respective consent screen.

The **consent screen** tells users who is requesting access to their data and what kind of data the app is asking to access. The latter part of the statement comes under **scope**. In our app, we need access to the user's Google **profile** and **email address**, thus added it to the scope object. Redirect routes will be discussed later.

### **Step 5: Google and Microsoft Strategies Setup**

Create a new file(**google_oauth.js**) in the project root directory and add the following code.

```javascript
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const jwt = require('jsonwebtoken');
passport.use(new GoogleStrategy({
  callbackURL: `http://localhost:5500/auth/google/redirect`,  //same URI as registered in Google console portal
  clientID: process.env.GOOGLE_CLIENT_ID, //replace with copied value from Google console
  clientSecret: process.env.GOOGLE_CLIENT_SECRET,
},
  async (accessToken, refreshToken, profile, done) => {
    try {
      let user_email = profile.emails && profile.emails[0].value; //profile object has the user info
      let [user] = await db('users').select(['id', 'name', 'email']).where('email', user_email); //check whether user exist in database
      let redirect_url = "";
      if (user) {
        const token = jwt.sign(user, process.env.JWT_SECRET, { expiresIn: '1h' }); //generating token
        redirect_url = `http://localhost:3000/${token}` //registered on FE for auto-login
        return done(null, redirect_url);  //redirect_url will get appended to req.user object : passport.js in action
      } else {
        redirect_url = `http://localhost:3000/user-not-found/`;  // fallback page
        return done(null, redirect_url);
      }
    } catch (error) {
      done(error)
    }
  }
));
```

In a similiar fashion, create a new file(**microsoft_oauth.js**) and copy paste the above code. Just make the following changes to it:
```javascript
const MicrosoftStrategy = require('passport-microsoft').Strategy;
passport.use(new MicrosoftStrategy({  
callbackURL: `http://localhost:5500/auth/microsoft/redirect`,  
clientID: process.env.MICROSOFT_CLIENT_ID,  
clientSecret: process.env.MICROSOFT_CLIENT_SECRET,  
scope: ['openid', 'profile', 'email']  
}  
```

A brief explanation of the arguments in the callback function:

1.  **accessToken** are used to make API requests on behalf of a user. Not required in our app.
2.  Generally, access tokens have a limited lifetime so a **refreshToken** can be used to obtain new access tokens.
3.  **profile** will contain user profile information provided by the service provider.
4.  **done** the callback function which gets invoked upon successful look-up and supplies passport with the user that gets authenticated. The first argument to done is the error object which is null in our code thus telling passport that things are fine and there is no error.

When an end-user approves our app in the consent screen, the redirect api's(**/auth/google/redirect** or **/auth/microsoft/redirect**) are invoked , **req.user** object is already set to the appropriate **redirection URL** through passport and the user lands on a page basis that.

Since we are not maintaining session through cookies thus an additional **session: false** key was passed. If we have to set up an app using cookies, then this is not passed. Furthermore, we need to require **cookie-session** and use passport **serializeUser** and **deserializeUser** functions to effectively manage the saved cookie basis google/microsoft unique **profile id**. Though, it is recommended using the **user id** present in the database.
Require the above two files in **index.js** and test the application locally.

### **Optional: App Verification**

Though it is not mandatory if sensitive scopes are not accessed. For production apps, this should be done irrespective of the scope requested.

For **Microsoft**, it is pretty easy to get our app verified, head to the Azure portal, and from the sidebar to the branding section.

Over here, you can upload the app logo. Add in Terms of Service, Privacy statement link if needed. To verify the publisher domain just upload the **microsoft-identity-association.json** file in **.well-known** folder on your website s3 bucket.

For **Google**, things are a bit tricky. Though some explanation is provided [here](https://support.google.com/cloud/answer/6158849?hl=en).
We can Submit our app for Verification from the **OAuth Consent Screen**(Google Console Portal) after providing the appropriate Authorized domain, Homepage, Term of Service, and privacy policy links. The process takes around **4-5 days** if no sensitive scope is being requested. A few rounds of follow-up emails from the google team if needed and that's it.

Apart from getting verified, the benefit of this is that a user can see our **App logo** and **App name** in the consent screen which makes it more professional.

Thanks for reading and congrats on making it till the end.

Cheers!!