# How to authenticate using GraphQL Cookies and JWT

> Learn how to add login to a GraphQL API powered by Apollo: send credentials, return a JWT, store it in a cookie, and use that token for every later request.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2019-08-21 | Topics: [GraphQL](https://flaviocopes.com/tags/graphql/) | Canonical: https://flaviocopes.com/graphql-auth-apollo-jwt-cookies/

In this tutorial I'll explain how to handle a login mechanism for a [GraphQL](https://flaviocopes.com/graphql/) API using [Apollo](https://flaviocopes.com/apollo/).

We'll create a private area that depending on your user login will display different information.

In detail, these are the steps:

- Create a login form on the client
- Send the login data to the server
- Authenticate the user and send a JWT back
- Store the [JWT](https://flaviocopes.com/jwt/) in a cookie
- Use the JWT for further requests to the GraphQL API

The code for this tutorial is available on [GitHub](https://flaviocopes.com/github/) at [https://github.com/flaviocopes/apollo-graphql-client-server-authentication-jwt](https://github.com/flaviocopes/apollo-graphql-client-server-authentication-jwt)

Let's start.

> WARNING! This tutorials is old. Apollo is now using `@apollo/xxx` rather than `apollo-xxx`, do your research until I update it :)


## I start up the client application

Let's create the client side part using `create-react-app`, run `npx create-react-app client` in an empty folder.

Then call `cd client` and `npm install` all the things we'll need so that we don't need to go back later:

```sh
npm install apollo-client apollo-boost apollo-link-http apollo-cache-inmemory react-apollo apollo-link-context @reach/router js-cookie graphql-tag
```

## The login form

Let's start by creating the login form.

Create a `Form.js` file in the `src` folder, and add this content into it:

```js
import React, { useState } from 'react'
import { navigate } from '@reach/router'

const url = 'http://localhost:3000/login'

const Form = () => {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')

  const submitForm = event => {
    event.preventDefault()

    const options = {
      method: 'post',
      headers: {
        'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
      },
      body: `email=${email}&password=${password}`
    }

    fetch(url, options)
    .then(response => {
      if (!response.ok) {
        if (response.status === 404) {
          alert('Email not found, please retry')
        }
        if (response.status === 401) {
          alert('Email and password do not match, please retry')
        }
      }
      return response
    })
    .then(response => response.json())
    .then(data => {
      if (data.success) {
        document.cookie = 'token=' + data.token
        navigate('/private-area')
      }
    })
  }

  return (
    <div>
      <form onSubmit={submitForm}>
        <p>Email: <input type="text" onChange={event => setEmail(event.target.value)} /></p>
        <p>Password: <input type="password" onChange={event => setPassword(event.target.value)} /></p>
        <p><button type="submit">Login</button></p>
      </form>
    </div>
  )
}

export default Form
```

Here I assume the server will run on `localhost`, on the HTTP protocol, on port `3000`.

I use [React](https://flaviocopes.com/react/) Hooks, and the [Reach Router](https://flaviocopes.com/react-reach-router/).
There's no Apollo code here. Just a form and some code  to register a new cookie when we get successfully authenticated.

Using the [Fetch API](https://flaviocopes.com/fetch-api/), when the form is sent by the user I contact the server on the `/login` REST endpoint with a POST request.

When the server will confirm we are logged in, it will store the JWT token into a cookie, and it will navigate to the `/private-area` URL, which we haven't built yet.

## Add the form to the app

Let's edit the `index.js` file of the app to use this component:

```js
import React from 'react'
import ReactDOM from 'react-dom'
import { Router } from '@reach/router'
import Form from './Form'

ReactDOM.render(
  <Router>
    <Form path="/" />
  </Router>
  document.getElementById('root')
)
```

## The server side

Let's switch server-side.

Create a `server` folder and run `npm init -y` to create a ready-to-go `package.json` file.

Now run

```sh
npm install express apollo-server-express cors bcrypt jsonwebtoken
```

Next, create an app.js file.

In here, we're going to first handle the login process.

Let's create some dummy data. One user:

```js
const users = [{
  id: 1,
  name: 'Test user',
  email: 'your@email.com',
  password: '$2b$10$ahs7h0hNH8ffAVg6PwgovO3AVzn1izNFHn.su9gcJnUWUzb2Rcb2W' // = ssseeeecrreeet
}]
```

and some TODO items:

```js
const todos = [
  {
    id: 1,
    user: 1,
    name: 'Do something'
  },
  {
    id: 2,
    user: 1,
    name: 'Do something else'
  },
  {
    id: 3,
    user: 2,
    name: 'Remember the milk'
  }
]
```

The first 2 of them are assigned to the user we just defined. The third item belongs to another user. Our goal is to log in the user, and show only the TODO items belonging to them.

The password hash, for the sake of the example, was generated by me manually using `bcrypt.hash()` and corresponds to the `ssseeeecrreeet` string. More info on bcrypt [here](https://flaviocopes.com/javascript-bcrypt/). In practice you'll store users and todos in a database, and password hashes are created automatically when users register.

## Handle the login process

Now, I want to handle the login process.

I load a bunch of libraries we're going to use, and initialize Express to use [CORS](https://flaviocopes.com/cors/), so we can use it from our client app (as it's on another port), and I add the middleware that parses urlencoded data:

```js
const express = require('express')
const cors = require('cors')
const bcrypt = require('bcrypt')
const jwt = require('jsonwebtoken')
const app = express()
app.use(cors())
app.use(express.urlencoded({
  extended: true
}))
```

Next I define a `SECRET_KEY` we'll use for the JWT signing, and I define the `/login` POST endpoint handler. There's an `async` keyword because we're going to use `await` in the code. I extract the email and password fields from the request body, and I lookup the user in our `users` "database".

If the user is not found by its email I send an error message back.

Next, I check if the password does not match the hash we have, and send an error message back if so.

If all goes well, I generate the token using the  `jwt.sign()` call, passing the `email` and `id` as user data, and I send it to the client as part of the response.

(While debugging, you can paste the token into my [JWT decoder](https://flaviocopes.com/tools/jwt-decoder/) to check the payload and expiry — it runs entirely in the browser.)

Here's the code:

```js
const SECRET_KEY = 'secret!'

app.post('/login', async (req, res) => {
  const { email, password } = req.body
  const theUser = users.find(user => user.email === email)

  if (!theUser) {
    res.status(404).send({
      success: false,
      message: `Could not find account: ${email}`,
    })
    return
  }

  const match = await bcrypt.compare(password, theUser.password)
  if (!match) {
    //return error to user to let them know the password is incorrect
    res.status(401).send({
      success: false,
      message: 'Incorrect credentials',
    })
    return
  }

  const token = jwt.sign(
    { email: theUser.email, id: theUser.id },
    SECRET_KEY,
  )

  res.send({
    success: true,
    token: token,
  })
})
```

I can now start the Express app:

```js
app.listen(3000, () =>
  console.log('Server listening on port 3000')
)
```

## The private area

At this point client-side I add the token to the cookies and moves to the `/private-area` URL.

What's in that URL? Nothing! Let's add a component to handle that, in `src/PrivateArea.js`:

```js
import React from 'react'

const PrivateArea = () => {
  return (
    <div>
      Private area!
    </div>
  )
}

export default PrivateArea
```

In `index.js`, we can add this to the app:

```js
import React from 'react'
import ReactDOM from 'react-dom'
import { Router } from '@reach/router'
import Form from './Form'
import PrivateArea from './PrivateArea'

ReactDOM.render(
  <Router>
    <Form path="/" />
    <PrivateArea path="/private-area" />
  </Router>
  document.getElementById('root')
)
```

I use the nice `js-cookie` library to work easily with cookies. Using it I check if there's the `token` in the cookies. If not, just go back to the login form:

```js
import React from 'react'
import Cookies from 'js-cookie'
import { navigate } from '@reach/router'

const PrivateArea = () => {
  if (!Cookies.get('token')) {
    navigate('/')
  }

  return (
    <div>
      Private area!
    </div>
  )
}

export default PrivateArea
```

Now in theory we're all good to go and use the GraphQL API! But we have no such thing yet. Let's make that thing.

## The GraphQL API

Server-side I do everything in a single file. It's not that big, as we have little things in place.

I add this to the top of the file:

```js
const {
  ApolloServer,
  gql,
  AuthenticationError,
} = require('apollo-server-express')
```

which gives us all we need to make the Apollo GraphQL server.

I need to define 3 things:

- the GraphQL schema
- resolvers
- the context

Here's the schema. I define the `User` type, which represents what we have in our users object. Then the `Todo` type, and finally the `Query` type, which sets what we can directly query: the list of todos.

```graphql
const typeDefs = gql`
  type User {
    id: ID!
    email: String!
    name: String!
    password: String!
  }

  type Todo {
    id: ID!
    user: Int!
    name: String!
  }

  type Query {
    todos: [Todo]
  }
`
```

The `Query` type has one entry, and we need to define a resolver for it. Here it is:

```js
const resolvers = {
  Query: {
    todos: (root, args) => {
      return todos.filter(todo => todo.user === id)
    }
  }
}
```

Then the context, where we basically verify the token and error out if invalid, and we get the `id` and `email` values from it. This is how we know **who** is talking to the API:

```js
const context = ({ req }) => {
  const token = req.headers.authorization || ''

  try {
    return { id, email } = jwt.verify(token.split(' ')[1], SECRET_KEY)
  } catch (e) {
    throw new AuthenticationError(
      'Authentication token is invalid, please log in',
    )
  }
}
```

The `id` and `email` values are now available inside our resolver(s). That's where the `id` value we use above comes from.

We need to add Apollo to Express as a middleware now, and the server side part is finished!

```js
const server = new ApolloServer({ typeDefs, resolvers, context })
server.applyMiddleware({ app })
```

## The Apollo Client

We are ready to initialize our Apollo Client now!

In the client-side `index.js` file I add those libraries:

```js
import { ApolloClient } from 'apollo-client'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloProvider } from 'react-apollo'
import { setContext } from 'apollo-link-context'
import { navigate } from '@reach/router'
import Cookies from 'js-cookie'
import gql from 'graphql-tag'
```

I initialize an HttpLink object that points to the GraphQL API server, listening on port 3000 of localhost, on the `/graphql` endpoint, and use that to set up the `ApolloClient` object.

An HttpLink provides us a way to describe how we want to get the result of a GraphQL operation, and what we want to do with the response.

```js
const httpLink = createHttpLink({ uri: 'http://localhost:3000/graphql' })

const authLink = setContext((_, { headers }) => {
  const token = Cookies.get('token')

  return {
    headers: {
      ...headers,
      authorization: `Bearer ${token}`
    }
  }
})

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache()
})
```

If we have a token I navigate to the private area:

```js
if (Cookies.get('token')) {
  navigate('/private-area')
}
```

and finally I use the `ApolloProvider` component we imported as a parent component and wrap everything in the app we defined. In this way we can access the `client` object in any of our child components. In particular the PrivateArea one, very soon!

```js
ReactDOM.render(
  <ApolloProvider client={client}>
    <Router>
      <Form path="/" />
      <PrivateArea path="/private-area" />
    </Router>
  </ApolloProvider>,
  document.getElementById('root')
)
```

## The private area

So we're at the last step. Now we can finally perform our GraphQL query!

Here's what we have now:


```js
import React from 'react'
import Cookies from 'js-cookie'
import { navigate } from '@reach/router'

const PrivateArea = () => {
  if (!Cookies.get('token')) {
    navigate('/')
  }

  return (
    <div>
      Private area!
    </div>
  )
}

export default PrivateArea
```

I'm going to import these 2 items from Apollo:

```
import { gql } from 'apollo-boost'
import { Query } from 'react-apollo'
```

and instead of

```js
  return (
    <div>
      Private area!
    </div>
  )
```

I'm going to use the `Query` component and pass a GraphQL query. Inside the component body we pass a function that takes an object with 3 properties: `loading`, `error` and `data`.

While the data is not available yet, `loading` is true and we can add a message to the user. If there's any error we'll get it back, but otherwise we'll get our TO-DO items in the `data` object and we can iterate over them to render our items to the user!

```js
  return (
    <div>
      <Query
        query={gql`
          {
            todos {
              id
              name
            }
          }
        `}
      >
        {({ loading, error, data }) => {
          if (loading) return <p>Loading...</p>
          if (error) {
            navigate('/')
            return <p></p>
          }
          return <ul>{data.todos.map(item => <li key={item.id}>{item.name}</li>)}</ul>
        }}
      </Query>
    </div>
  )
```

## Use an HttpOnly cookie for better security

Now that things are working, I want to change a little bit how the code works and add the use of HTTPOnly cookies. This special kind of cookie is more secure because we can't access it using [JavaScript](https://flaviocopes.com/javascript/), and as such it can't be stolen by 3rd part scripts and used as a target for attacks.

Things are a bit more complex now so I added this at the bottom.

All the code is available on GitHub at [https://github.com/flaviocopes/apollo-graphql-client-server-authentication-jwt](https://github.com/flaviocopes/apollo-graphql-client-server-authentication-jwt) and all I described up to now is available [in this commit](https://github.com/flaviocopes/apollo-graphql-client-server-authentication-jwt/commit/0035b0e557c7c565fe7e2c861c7d30dcc704f477).

The code for this last part is available [in this separate commit](https://github.com/flaviocopes/apollo-graphql-client-server-authentication-jwt/commit/b6b3841b6095bdbac43891ce8ef36a3ec317eb38).

First, in the client, in `Form.js`, instead of adding the token to a cookie, I add a `signedin` cookie.

Remove this

```js
document.cookie = 'token=' + data.token
```

and add

```js
document.cookie = 'signedin=true'
```

Next, in the `fetch` options we must add

```js
credentials: 'include'
```

otherwise `fetch` won't store in the browser the cookies it gets back from the server.

Now in the `PrivateArea.js` file we don't check for the token cookie, but for the `signedin` cookie:

Remove

```js
if (!Cookies.get('token')) {
```

and add

```js
if (!Cookies.get('signedin')) {
```

Let's go to the server part.

First install the `cookie-parser` library with `npm install cookie-parser` and instead of sending back the token to the client:

```js
res.send({
  success: true,
  token: token,
})
```

Only send this:

```js
res.send({
  success: true
})
```

We send the JWT token to the user as an HTTPOnly cookie:

```js
res.cookie('jwt', token, {
  httpOnly: true
  //secure: true, //on HTTPS
  //domain: 'example.com', //set your domain
})
```

(in production set the secure option on HTTPS and also the domain)

Next we need to set the CORS middleware to use cookies, too. Otherwise things will break very soon when we manage the GraphQL data, as cookies just disappear.

Change

```js
app.use(cors())
```

with

```js
const corsOptions = {
  origin: 'http://localhost:3001', //change with your own client URL
  credentials: true
}


app.use(cors(corsOptions))
app.use(cookieParser())
```

Back to the client, in `index.js` we tell Apollo Client to include credentials (cookies) in its requests. Switch:

```js
const httpLink = createHttpLink({ uri: 'http://localhost:3000/graphql' })
```

with

```js
const httpLink = createHttpLink({ uri: 'http://localhost:3000/graphql', credentials: 'include' })
```

and remove the `authLink` definition altogether:

```js
const authLink = setContext((_, { headers }) => {
  const token = Cookies.get('token')

  return {
    headers: {
      ...headers,
      authorization: `Bearer ${token}`
    }
  }
})
```

as we don't need it any more. We'll just pass `httpLink` to `new ApolloClient()`, since we don't need more customized authentication stuff:

```js
const client = new ApolloClient({
  link: httpLink,
  cache: new InMemoryCache()
})
```

Back to the server for the last piece of the puzzle! Open `index.js` and in the `context` function definition, change

```js
const token = req.headers.authorization || ''
```

with

```js
const token = req.cookies['jwt'] || ''
```

and disable the Apollo Server built-in CORS handling, since it overwrites the one we already did in Express, change:

```js
const server = new ApolloServer({ typeDefs, resolvers, context })
server.applyMiddleware({ app })
```

to

```js
const server = new ApolloServer({ typeDefs, resolvers, context,
  cors: false })
server.applyMiddleware({ app, cors: false })
```
