GraphQL Authentication with React Native & Apollo [Part 1/2]
Using Express, MongoDB, and JSON Web Tokens
I’ve been playing with Apollo a lot lately and, as with nearly any app, I needed to add authentication to that app. I looked around but couldn’t find any posts specifically on the subject of authentication using React Native, GraphQL, and MongoDB — so that’s what we’ll be covering today.
In part one of this series you’ll learn how to configure the server (using GraphQL/Apollo, Mongo, Express, and JSON Web Tokens). In part two we’ll setup the React Native client to interact with it all.
The app we’ll be building is very simple, but will demonstrate the critical pieces of an authentication flow. It will have 3 screens:
- Login
- Register
- Log out
Ready? Let’s go.
Prefer Video?
Prefer video tutorials? I made this tutorial into a video, which can be found below.
Setup
We’re going to use three tools throughout this, all free, all 100% browser based, some requiring sign up.
Server
Since the whole point of this tutorial is to hook up GraphQL/Apollo and React Native we can leverage an incredible web-based tool, Launchpad.
This will go ahead and setup our server for us, set up GraphQL, and give us an endpoint to work with. When you’re ready to make it more serious you can easily export the entire express app, with all your existing work, and you’re off to the races!
I would suggest signing in/creating an account so you can save your work.
Database
I don’t like setting up database. It’s a personal thing. It’s too serious for me. So I always offload the work to someone else. This time we’ll be using mLab to handle the Mongo database — they’ve got a free sandbox tier and that will be absolutely perfect for us.
You’ll need to sign up for this one.
A note on databases: Don’t want to use Mongo? You can still use this tutorial, just use your preferred DB and modify the queriers to work with it.
React Native
I’m going to be using Snack by Expo. It’s an incredible web-based way to build React Native apps. Once you’re done you can easily export it into your normal development flow, just like Launchpad. One suggestion I do have is to download the Expo app and use that as your development device, I find the experience to be better than when using the in-browser simulator.
I would suggest signing in/creating an account so you can save your work, though it’s not required.
Easiest setup in history? I’d say so.
Configuring the Server
The first thing we’ll do is configure our server on Apollo Launchpad. Here’s a simplified version of the server you can use to get started. If you’ve got a basic familiarity with GraphQL (which I’m assuming you do) you’ll pick up on this quickly.
In Launchpad you should now be able to query “hello” and get a response.
First thing we’ll do is setup all the necessary queries for our GraphQL endpoint. Given our basic feature spec the only query we need is one that will return a user. Just return a basic user object that has an _id
and email
for now.
Now let’s set up the necessary mutations, login
and signup
. These should each accept an email
and password
.
Go ahead and give it a try to make sure you don’t have any errors.
Connecting to the Database
Once you’ve set up your account with mLab and created a new mongo db instance you should see a url along the lines of
mongodb://<dbuser>:<dbpassword>@ds121665.mlab.com:21665/graphql-auth-demo-1
you’ll also need to create a user for that database (with write permissions). Take that url and add it to our “secrets” as MONGO_URL
in Launchpad. Make sure to swap out <dbuser>
and <dbpassword>
with the values of the user you created.
By adding the secret values here we can access them from our context
function, which is where we’ll be establishing our mongo connection. This context function will be called each time a request is made to a GraphQL endpoint so we want to store a reference to our connection outside of the function (allowing us to only make the db connection request once). We’ll then return the mongo
variable in context so that we access it in our resolver functions.
Make sure you import { MongoClient } from 'mongodb';
at the top of your file! Launchpad will automatically install it.
Sign Up
Now let’s access the database from the resolver functions… first signup. We’ll get a reference to a users
collection and then check if a user with the given email address already exists.
We can then insert the user document into the collection. I’ll be using bcrypt
to hash the user’s password, because you should never store a plain text password in your database! Make sure you import the lib:
import bcrypt from 'bcrypt';
I’m going to be using async/await because it makes for easy to follow code.
Go ahead and give it a shot in Launchpad
Login
The login mutation is very similar to the one we just put together.
First we’ll check if a user with that email address exists.
Then we’ll compare the password supplied and the password we have stored. If they match then return the user!
So now we’ve got our sign in & sign up mutations working, but that isn’t all! We don’t want to force the user to have to login with every request. Enter…
JSON Web Tokens
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA.
For us this means that we can generate a JWT and use that as a means of confidently identifying a user. The flow will work like this
- User logs in or signs up
- The server returns a JWT
- The client saves the JWT to their local storage (covered in part 2)
- The JWT is passed with each request from client to server (on the
Authorization
header — covered in part 2) - The server uses the JWT to grab the user’s information and add it to
context
A note on step 5: Since I wanted to keep this all browser based we need to do this manually. If you’ve got total control over the server though you can use
express-jwt
to do this automatically. This is a good way to learn what that module is doing though!
The Secret
A JWT is signed with a secret value. We’ll want to come up with one and add that to the “Secrets” in Launchpad, like we did MONGO_URL
. I’m storing it as JWT_SECRET
with a value of super-secret
.
Now, using that secret we’ll want to generate a new JWT and return it on the user object when they successfully login or sign up. First I’ll add jwt
to the User
type in our type definitions.
Make sure you add import jwt from 'jsonwebtoken';
to the top of your file.
Generating the Token
Then, upon successful login, I’ll create a JWT. We’re passing the user’s id as part of the payload and secrets
is coming from context.
We’ll do the same for sign up.
Adding the Current User to Context
Finally (for this part at least) we need to add the current user to context.
Remember, if you’re not using Launchpad you can use express-jwt
to do this automatically. I also need to note that I’m basing this function a bit off of this launchpad which does the same thing but for the Auth0 service.
First, create a new function called getUser
and check if we’ve got a potentially valid authorization token in the header.
Then we’ll try to verify the token that was passed.
Then we’ll attempt to fetch the user from our database using the _id
that we put into the jwt payload initially. ObjectId
is coming from the mongdb
module.
Finally, let’s go ahead and call this getUser
function from the context
function.
We can also update the currentUser
query to return the user from context.
Let’s go ahead and check this out…
And there you have it! Runnable source code is available below
This was just the first part of this series, in part 2 we’ll connect our React Native app to it.
Part 2 is available here:
My name is Spencer. I teach people to build 🔥 apps with React Native. Interested in more high quality tutorials? Sign up for my email list.