DEV Community

Cover image for Setting up Social Logins(Google + Microsoft) with Node.js and Passport.js
Asim
Asim

Posted on • Updated on • Originally published at 404found.tech

Setting up Social Logins(Google + Microsoft) with Node.js and Passport.js

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

oauth basic

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

oauth complex

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 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 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:

npm install passport passport-google-oauth20 passport-microsoft --save
Enter fullscreen mode Exit fullscreen mode

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:

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

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.

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

In a similiar fashion, create a new file(microsoft_oauth.js) and copy paste the above code. Just make the following changes to it:

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

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.
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!!

Top comments (13)

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
maciejwiatr profile image
Info Comment hidden by post author - thread only accessible via permalink
wiatr.dev

I'd be very thankfull 😅

Collapse
 
brianmcbride profile image
Brian McBride

The OAuth developer view is great.

I will say that if you want to go ultra simple, Google still offers Firebase Auth for pretty much free. It doesn't mean you need to use GCP for all your services either. And it although it isn't perfect, you can't beat it for the price anywhere.

Collapse
 
asim_ansari7 profile image
Asim

Thanks for the feedback.
I agree with that as I have used firebase social auth in some personal projects which is pretty easy to setup. Though I feel, with so many frameworks and libraries out there the level of abstraction has increased significantly and at times some of us get lost in setting things up from scratch and knowing how the thing actually works under the hood.
I just shared the minimal barebone of setting things up with Node, and even passport could have been avoided here to keep it more basic.

Collapse
 
orenz profile image
Oren Zbeda • Edited

Hi,

This is a lot of work.
I created this: github.com/orenz/azauth
It will allow social login in about 3 lines of code (really).

Also did a short video explainer.
youtu.be/5WehZTrgG8o

Hope you like.

Collapse
 
arifamujawar profile image
Arifa

Hello Asim,

I have used the same library to provide Microsoft verification. I am able to fetch the profile of a user, but my application checks the passport verification again and the application crashes. Did you face the same problem with Microsoft verification?

Collapse
 
asim_ansari7 profile image
Asim

Hey Arifa,
Is the redirection URL the same in both the application and Microsoft portal?
If this true, check for these keys in the manifest file (found under AppRegistration, Manifest tab)
"accessTokenAcceptedVersion" : 2
"signInAudience" : "AzureADandPersonalMicrosoftAccount"
I did face an issue because of this and setting this to the above values fixed it.
Relevant Resources:
docs.microsoft.com/en-gb/azure/act...
docs.microsoft.com/en-gb/azure/act...

Collapse
 
arifamujawar profile image
Arifa

Thanks, Asim. I got it working. I simply added { session: false } in the middleware file.

Collapse
 
nicolasomar profile image
Nicolás Omar González Passerino

Thanks man, I think this is a sweet feature to add in almost any app.
You gave me a great starting point from here.

Collapse
 
long_yoda profile image
Vo Hoang Long

Awesome content. Thank you

Collapse
 
clintonmwachia profile image
ClintonMoshe

Awesome content. Thanks man

Collapse
 
nehanegi220682 profile image
nehanegi220682

Thank you so much from Yatin and Neha :-P

Collapse
 
bhargu27 profile image
Bhargav kanodiya

is app registration on microsoft are free or not

Some comments have been hidden by the post's author - find out more