React Authentication with Twitter, Google, Facebook and Github

Jesse Heaslip
codeburst
Published in
9 min readJul 30, 2018

--

In a previous post, I went over an approach you could take to authenticate your React app with Twitter. The obvious next step is to add Facebook, Google and Github to the authentication mix.

To interact with an OAuth provider you need API keys. For security reasons, those keys must be kept private on a server. However, if you are following modern development practices your React app is likely completely decoupled from your server and running on a different port or domain.

So how can we solve this communication gap between an OAuth provider that needs the keys and the React client that can’t keep those keys private?

Before we get too far, here is a demo of what we are going to be building today. And the repo for the client and the server if you want to skip ahead.

A (very) Brief History of Authentication

If you have worked with OAuth authentication in the past this diagram will seem familiar:

Here the server is spitting out the html the user sees on the client when they use an application, this is referred to as server side rendering.

Since the client and the server are the same entity operating on the same port or domain, we can keep the keys private and simply redirect the user after a successful authentication to a new url on the same application with the user’s personal data.

But with modern application architecture now decoupling the client from a server (or services) it is getting data from, how can a React client that can’t keep API keys private authenticate with an OAuth provider that needs a private API key?

There are solutions out there to this problem.

Some, like Firebase are steeped in black magic and abstract away all the inner workings. Others, like React Auth Twitter, seem over engineered (see workflow below).

Twitter Auth via react-auth-twitter

Lots of relay points for things to go wrong there, surely there must be a better way…

I believe a good approach is to use sockets:

Here, the client communicates with the server via a request to start the authentication process (2) and via a socket response to ultimately receive the user’s information (5).

Fortunately, because of the reusable nature of React components and the overlap in PassportJS strategies this can happen with a small amount of code for multiple OAuth providers including Twitter, Facebook, Google, Github and many others.

Let’s take a look on how to pull that off.

Client

This project is bootstrapped with Create React App. I am assuming you already have some experience with CRA. If not, here is a great tutorial.

The first file we are going to look at is the drop in point for any React app, App.js:

Note that the socket is connected in the parent App.js component on line 6 and then passed into the child OAuth.js components as a prop on line 19.

The providers variable is just an array of strings of the OAuth provider’s names. That is good news! Adding another provider on the client is as easy as including the appropriate string in our array.

Super extensible.

Next, lets take a look at the child OAuth.js component:

OAuth.js is the meat of the client, so we are going to break it into pieces to ensure we have an understanding of each part.

We need a bit of state to help manage whether to show the user or to show the login button. On line 15 we are also listening for any response via sockets from the server that matches our ‘provider’ prop in componentDidMount.

Next, we have to set up custom methods on the OAuth class to start the authentication process and ensure the state updates appropriately if the user aborts the process of authentication.

There is a bit of functionality here, so lets go through it in more detail.

checkPopup() — Checks the popup every second to re-enable the login button if the user closes the popup without authenticating.

openPopup() — Launches the popup and makes a request to the server that includes the socket id as a query parameter so it can be used to send back user data to the appropriate socket on the connected client. That happens on line 25 when the url is built and is the key to allowing us to relay information over sockets securely.

startAuth() — Kicks off the processes of opening the popup on the server and listening to the popup. It also disables the login button so the user can not attempt to login to the provider multiple times.

closeCard() — Pretty sure you can figure out what that does :)

With the functionality in place, lets have a look at the render() method:

Not too much to this one.

The state of the user is used to selectively render either their social profile if it exists or the appropriate login button so the user can login. Using font awesome and some CSS we create authentic buttons using the provider prop as a className.

Thats all for the client. Time to move on to the server.

Server

Since the server is where we are going to store the API keys, we will need to get those keys from the providers that we are working with. Here are the instructions to get your keys for Twitter, Google, Facebook and Github.

Make sure that callback URL you provide when registering an app is in the form:

https://localhost:8080/__provider__/callback

After signing up for each provider we need to plug those keys into a .env file on the server. If you need a little help with that, please read this.

We are now ready to move on to writing the server:

Server.js is pretty lightweight as a lot of the functionality for the application will be shelled off to other files.

The key bits to note are that the application is started in HTTPS mode on line 22 (more on that below) and that the sockets are set directly on the app on line 45. By attaching the socket to the app, the socket can be accessed in the routes at a future point in time when we need to communicate with the client.

On to the PassportJS setup:

We are pulling in the strategies for the different providers that we are working with and adding those to the Passport singleton so they can be used throughout the application. If you have worked with Passport before this will feel familiar.

If you haven’t worked with Passport before, this is a great tutorial.

What is a little unusual above is that the callback on line 21 is the same for all the OAuth providers. Normally that callback is where you would save the user to a database and would need to be configured for every provider individually.

Because saving user data is not a focus of this tutorial, the callback is intentionally generic in this example.

With Passport all set up and ready to go, lets take a look at the router:

We have some setup to incorporate the PassportJS middleware and then have written a small piece of middleware on line 17 to attach the socket id of the connected client that comes in on req.query to req.session.

addSocketIdToSession — Attaches the socket id to the session and allows the server to send back the correct user info to the correct socket when the authentication process is complete with an OAuth provider.

I like to break up routes and callbacks into different files on the server to keep code modularized. But the previous file and the next file could be combined.

Time to look at the next (and final!) file:

Because we used app.set(‘io’, io) in server.js we now have access to the sockets from anywhere in the application that has a request in scope. This is super handy as we can now accept a callback from an OAuth provider and then relay the user’s info to the client via sockets.

Although this code is quite repetitive, at some point we have to build a user Object to send to the client and every OAuth provider sends their data in a different shape. Given that, there does not appear to be a way to avoid what feels like unnecessary boilerplate.

<side note> I would normally move configuration functionality like this into the callbacks of the passportInit function as mentioned above and move the user into a database at that point in time. But this seemed more straightforward given we are not saving any user data in this tutorial. </side note>

And thats it!

The server is not as easily extensible as the client, but we have established a solid pattern where adding an additional OAuth provider requires installing/configuring the PassportJS package for that provider, setting up a couple routes and writing a controller method.

Not too bad.

HTTPS

Now that we have set up the client and server there is one final piece, we need to run our application in a secure environment.

And… you may want to get a cup of coffee before we start. This could take a hot minute.

Because of Facebook’s requirement that any application interacting with the Facebook API (even in development) must serve requests over HTTPS we are going to need to run both the client and server in HTTPS mode.

For the create-react-app client it is as simple as using this command to start your development server:

HTTPS=true react-scripts start (OS X)orset HTTPS=true&&npm start (Windows)

Your client application is now started with HTTPS. However, a secure client can only make requests to a secure server and that setup is a bit more complicated.

The following are OS X instructions to get a local server and HTTPS certificates up and running:

  1. How to get HTTPS working on your local development environment in 5 minutes
  2. Importing and exporting your Email or Personal Authentication certificate using Chrome on Mac OS X
  3. How do I deal with NET:ERR_CERT_AUTHORITY_INVALID in Chrome?

So the process is to make the certificates on your local machine and then tell your machine that it is ‘ok’ to accept those self-signed certificates.

I am not a Windows fella myself, so have not attempted this... But here is a video for how to set up HTTPS on a local Windows server that looks pretty good.

And thanks to Le Gui PPF for providing the following instructions for setting up SSL locally on Windows:

“Hi thanks for the code ! I haven’t tried it yet cause I spent a whole day (hot minute huh !) figuring out how to generate proper ssl certificate with all Chrome requirements… for those who don’t want to lose their time => https://serverfault.com/a/850961 and add

“echo authorityKeyIdentifier=keyid,issuer
echo basicConstraints=CA:FALSE
echo keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment”

to v3 req.

Hope that works for you Windows folks!

I know setting up a local server in HTTPS can be a pain point, if you are struggling with these instructions consider starting with the Twitter only (non-HTTPS) tutorial.

Takeaways

The trend towards decoupled modern application architecture can reopen previously solved problems like authentication. But the overall advantages of building applications in this decoupled services paradigm are too significant to turn back on.

This setup works because React components are reusable and the huge amount of overlap in PassportJS strategies. Once it is up and running, it can be extended to add additional OAuth providers with a small amount of overhead.

So what do you think about this workflow? Do you have a different strategy for OAuth authentication with React? I would love to hear about it.

If this tutorial helped you at all, all I ask is that you high fifty (+50) the clap button.

Thank you :)

✉️ Subscribe to CodeBurst’s once-weekly Email Blast, 🐦 Follow CodeBurst on Twitter, view 🗺️ The 2018 Web Developer Roadmap, and 🕸️ Learn Full Stack Web Development.

--

--