passportJS and Google OAuth
Whenever I start a new project I like to think of it like this:
As the title of this post suggests, for my next project I decided that I wanted users to be able to log in with their Google account.
In addition to Google OAuth, I also implemented passportJS to assist in authorization handling. I’m going to go over my experience in setting these tools up.
There are two components to passportJS — there is the base library ‘passport’, and then there are ‘passport strategies’. Strategies are helpers for authenticating specific to what service you intend to use. For my first implementation, I chose Google.
Here I am making sure to import all the components I need. First is Express. Express is a library that runs code in the Node runtime and has helpers that make dealing with HTTP traffic easier. The second is passportJS, and the third is the Google passport strategy. ‘keys’ is a module export object that contains the clientID/clientSecret, provided by the Google+ API when I generated a new project. There are plenty of tutorials for how to set this up, but I will provide a short jist below.
Google API Setup
- Create a new project through Google Cloud’s console. The only property you need to specify is the project name.
- Set up the ‘OAuth Consent Screen’, with user type ‘External’, save.
Google will then provide a Client ID and a Client Secret. Make sure to store these two keys in a secret file.
Google Strategy Setup
In order for passportJS to know how to handle HTTP requests, call the use() function, passing it a new instance of your selected strategy and any required variables. For this example that would be the ‘clientID’, ‘clientSecret’, and ‘callbackURL’ properties we defined earlier. Google verifies my application by my ClientID/ClientSecret. The second argument passed is a callback function which will ultimately manage the response data, but for testing, I just printed out the properties I will be using to the console.
There were a couple tricky parts to this setup. Because there are two separate HTTP requests in this process, and passportJS is the way it is, both requests need their own route handlers.
First Request Route Handler
For each authentication request, passportJS takes in two arguments. The first is the relative path for the request, and the second is a piece of code that will be executed when the application receives a response. Here, the piece of code that is passed is a passport authenticate() function. The authenticate function takes in two arguments, the first is the strategy type as a string(“google”), and the second is an options object that specifies the data the application needs with a ‘scope’ property set to an array of keys as strings ([‘profile’, ‘email’])
Auth Flow I
- Google asks user to grant my app permission with their login credentials.
- User grants permission by logging into their account.
- The response from the first request contains the user profile and email, as well as a special code embedded in the query string of the response.
- The user is put ‘on hold’ while the second request is executed.
Second Request Route Handler
This is where it gets a bit hairy. So, here we are seeing up the route handler for the ‘/auth/google/callback’ route. Our first response is redirected here by the Google Strategy from our initial request. Now, when that first response comes back it has that special code in the query — passport will see this and assume this isn’t an initial request, and that we are trying to turn that code into a user profile, and so will pass along the scope property back to the Google Strategy, which will provide the profile and email properties of our user.
Auth Flow II
- The first request is redirected to the callbackURL (“/auth/google/callback”) set up in the Google Strategy configuration with a query component ‘response code’ in the URL.
- The route handler then calls passport.authenticate(“google”) — this time, with that special identifying code. It is this piece of code that allows passport to assume that this is not an initial request, but that the user is attempting to access a profile from this request.
- Now the response makes its way back to the Google Strategy, where the final callback function is called and we see our accessToken, refreshToken, and profile printed out in the console. Rejoice.
This concludes the initial setup for Google OAuth using passportJS. The next step in my project will be setting up the database with MongoDB and persisting user information.
Take a whack yourself, and try implementing one of the many strategies passportJS provides!