When starting with GraphQL you need to familiarize yourself with a lot of new concepts: like schema’s and resolvers. In this tutorial I will show how easy authentication can be if you harness the power of GraphQL and Express. Combined you have best of both worlds: querying data like a champ and using traditional API calls for mundane stuff like user creation and authentication.
Getting started: boilerplate app
If you want to code along, you can check out this repo and clone the start of the tutorial release with this command:
git clone git@github.com:DirkWolthuis/graphql-express-auth-tutorial.git --branch v1.0
In this project I have setup webpack to compile the app.js
file inside the dist
directory. It serves a basic Express + GraphQL app. After cloning the project you need to run npm install
to install the dependencies. Finished installing? You can run npm start
to start the app. It will server a GraphQL endpoint on http://localhost:3000/graphql
. Feel free to try it out: you can query users, user, todos, todo
. The goal of this tutorial is to protect the GraphQL endpoint with JSON Web Token authentication, in other words: if a user is not authenticated he or she can’t access the data from the GraphQL endpoint.
Step 1: creating a authentication endpoint
In this step you are going to create a Express endpoint to authenticate and retrieve a JWT (token). To test this you need a tool like Postman (it’s free), to test your endpoint. First we will create a new post endpoint, just above where we start the app.
/*
Express routes/endpoints
*/
app.post('/get-token', (req, res) => {})
/*
Starting the app
*/
app.listen(port, () =>
console.log(
`🔥🔥🔥 GraphQL + Express auth tutorial listening on port ${port}!`
)
)
I use the /get-token
route for my endpoint, but you are free to use whatever name you like. In this function we need to three things:
- Check if the user that is trying to authenticate exists
- Check if the password matches the password in the database (or in our case, the
mock.js
file - Create and return the token
/_
Express routes/endpoints
_/
app.post('/get-token', async (req, res) => {
const { email, password } = req.body
const user = users.find(user => user.email === email)
if (user) {
//we use bcrypt to compare the hash in the database (mock.js) to the password the user provides
const match = await bcrypt.compare(password, user.password)
if (match) {
//we create the JWT for the user with our secret
//inside the token we encrypt some user data
//then we send the token to the user
const token = jwt.sign({ email: user.email, id: user.id }, SECRET_KEY)
res.send({
success: true,
token: token,
})
} else {
//return error to user to let them know the password is incorrect
res.status(401).send({
success: false,
message: 'Incorrect credentials',
})
}
} else {
//return error to user to let them know the account there are using does not exists
res.status(404).send({
success: false,
message: `Could not find account: ${email}`,
})
}
})
A simple function that checks the user and the password and returns errors with messages if the user makes a faulty request. Time to test our endpoint! Let’s fire up Postman. Create a new post request on to your chosen route. For me it’s localhost:3000/get-token
. Choose body and select the raw
, with the setting on JSON
. Now add a JSON object with the following:
{
"email": "hallo+spam@ikbendirk.nl",
"password": "Test123!"
}
Send the request and bam! We get our token.
Step 2: protecting the GraphQL endpoint
In the next step we’ll make sure only authenticated user (users with a valid token) can access data via the GraphQL endpoint. To create this feature will use Apollo context. Apollo context let’s you put information into a context object that is accessible by the resolvers. And it’s created on each request, with makes it easy to use for authentication. First let’s setup our context function.
/*
Creating our Apollo context function
*/
const context = ({ req }) => {}
/*
Creating the Apollo server
*/
const server = new ApolloServer({ typeDefs, resolvers, context })
We added our context to the Apollo server. Now we need to do three thing:
- Get the token from the request
- Check if the token is valid
- Throw an error if it’s invalid
const context = ({ req }) => {
// get the user token from the headers
const token = req.headers.authorization || ''
const splitToken = token.split(' ')[1]
try {
jwt.verify(splitToken, SECRET_KEY)
} catch (e) {
throw new AuthenticationError(
'Authentication token is invalid, please log in'
)
}
}
The the code above we get the token from the headers, verify it and return a error if it’s not valid. The AuthenticationError is a specific Apollo error to signal that Authentication has failed: therefore you need to import it from the apollo-server-express
package.
const {
ApolloServer,
gql,
AuthenticationError,
} = require('apollo-server-express')
Let’s check if our solution worked. Go to the GraphQL endpoint http://localhost:3000/graphql
and check if you can still access your data. Great our app throws an error when you try to access it without a token.
Now for the final piece: let’s try to add the token we retrieved earlier. On the GraphQL endpoint you have a tab called HTTP HEADERS
. Let’s add our token there:
{
"Authorization": "Bearer YOUR_TOKEN_HERE"
}
And there you go: authentication with GraphQL, Apollo and Express.
Want the code? Checkout Github.