Handling Authentication in NodeJS (Express) with Passport Part 4 — Handling Google SignUp/Login

Sijuade Ajagunna
The Startup
Published in
5 min readJun 28, 2020
image credits: https://reactjsexample.com/a-google-oauth-sign-in-log-in-component-for-react/

Having set up local authentication for our small Express app, It’s time to bring in something Passport really excels at — Handling third party authentication. Passport helps you integrate authentication via lots of popular websites into your app using packages. With Passport packages, users can sign up and log in to your app using services like Google, Twitter, Steam, GitHub, LinkedIn among others. These packages typically use the OAuth 2.0 authorization protocol to grant access to protected routes. You can go through this illustration to get a better understanding of how OAuth 2.0 works.

Let’s get started with Google:

To use the Google authentication strategy, you’ll need to register an app on the Google Developers website. Once you agree to the terms and conditions, at the upper left corner, there’s a dropdown menu to select a project. Click on it, and then on the New Project option at the top of the pop-up modal. Register your project name and create it.

Now to set up our Project:

Your project page should look like this now:

You’ll need to create credentials to use to get a clientID and a secret key that we’ll be using in our application. On the ‘Credentials tab’, you’ll find an option to Create Credentials. You should create a OAuth Client ID. This Google Developers’ guide explains how to go about it.

Remember to create a web app. Since we are working with our localhost, your origin URI can be http://localhost:5000and Authorized redirect URI set to http://localhost:5000/auth/google/callback. You can add more URIs to this if you have the intention to deploy your application. Remember to set your port to whatever port your server is set to listen to.

Click the create button, Your app should now be created and your Client ID and client secret will be shown to you. Copy these and add them to your .env file. Your client details are always present in the credentials screen so you can copy it whenever you need it.

Let’s get it working:

First, initialization

Back to our code: First, install the Passport Google OAuth 2.0 Strategy:

npm i passport-google-oauth20

Now, you should update your user model by adding a new field googleId, which will be of type String.

In the passport folder, create a passport-google.js file and import the necessary packages. Here, we’re going to configure our app to employ Google Authentication using the Client ID and Secret provided for us and the Callback URL we provided.

This is almost similar to the passport-local flow, except for the access Token and refresh Token. These tokens are not provided by us, They are issued to us by Google and our application uses this token to get protected information, such as user data. Access tokens have short lifespans for security reasons and when they expire, refresh tokens are provided to the client to create a new access token.

profile contains the details of the user which we have been granted access to (defined in our scope earlier).

done is a callback function which indicates that we’re done and carries information back to the next middleware or function in our application workflow (the socialAuth controller). Like most callbacks, done receives an error as the first argument so it’s typical to use null as the first argument when there’s no error.

First, we check if a user with the googleID has been registered on our application, if such a user exists, we send back the user with else, we query if such the email exists (for example, if a user previously registered with a username and password tries to sign in by mail). If such a user exists, we update their profile to include the googleId and send back the user object. Otherwise, we create a new user.

The validate before save option is to enable us to save the user without a password. You can also go about this by removing the required field of the password in the user model and use custom validators to ensure users who try to register locally do so with their passwords.

Then, controllers

Add this snippet to your auth.controller.js file:

socialAuth: async (req, res) => {
try {
const { authInfo, user } = req;
createCookieFromToken(user, authInfo.statusCode || 201, req, res);
} catch (err) {
res.status(500).json({
status: 'error',
error: {
message: err.message,
},
});
}
},

This controller handles the user object (or error) we get from the passport middleware and creates the token and cookie (or sends back the error).

Finally, the routes:

Update your auth.route.js file with these:

Woah! what’s happening here?

When a user attempts to sign in with Google, they are directed to the first URL. At this point, Google takes over authentication (Remember, Passport is just a middleware) and the interactive box pops up asking you which account you’d like to sign in with. We are not using sessions in this app, hence, session is set to false. The scope as I said earlier, indicates the user’s data you need from Google.

If the user chooses an account to log in with, Google generates an access token that is attached to our callback URL. Passport then uses this accessToken to get the user’s info from Google — the authentication middleware in passport-google.js file kicks into action. The data from the authenticate middleware is then passed to the socialAuth middleware which sends a response.

Now to ensure our authentication middleware works, let’s test it:

  • First visit the /auth/google route and then log in with your email. If you get an error here after logging in, ensure your callback URL matches the one provided to Google. Also, ensure you import the required Passport packages in your auth.route.js file.
  • Now visit the /auth/amiworthy route which is protected. You can copy the bearer token from the first step into your authorization header. There is a cookie created already though so you don’t have to do so. If your app is properly set up, you should have access to this route.

You can get the code from the GitHub repository.

--

--