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

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

asim_ansari7 profile image Asim Originally published at 404found.tech Ńâ╗6 min read

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

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

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

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']  
}  

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

Discussion

pic
Editor guide
Collapse
asim_ansari7 profile image
Asim Author

Happy to share the souce code if needed :)

Collapse
maciejwiatr profile image
Maciej Wiatr

I'd be very thankfull ­čśů

Collapse
asim_ansari7 profile image
Asim Author

Had to implement this in a production app, so just shared my learnings from it and some basic pieces of the setup. Will try to set up a personal app and get this done asap.

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 Author

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