This is part 3 of a 3 part tutorial. You can find part 1 here and part 2 here.

In the last tutorial in this series, we looked at how to add authentication using JWT to a GraphQL server. In this tutorial, we’ll be looking at how to add authentication to GraphQL using Auth0.

What we’ll be building

To demonstrate things, we’ll be building a simple todo app that is secured with Auth0. The todo app will comprise of a GraphQL server and a frontend app which will consume the GraphQL server. This tutorial will be grouped into two sections. In the first section we’ll build the GraphQL server, then in the second section we’ll build the frontend app.

demo

We won’t be using any database in this tutorial so as to make things simple and focus on the goal of the tutorial. The todo app will use in-memory persistence. If you would like to use a database, you can look at the database setup section in the last tutorial in the series.

Creating an Auth0 account

This tutorial assumes you already have an Auth0 account. If you don’t, you can sign up for a free one.

Creating Auth0 API

We need to create an Auth0 API which will be used in our GraphQL server. Login to your Auth0 dashboard and create a new Auth0 API with the details as in the image below:

new api

Take note of the details, as we’ll be using them later in the tutorial.

Creating the GraphQL server

Create a new folder called graphql-auth0-server then open the folder in the terminal and run the command below:

    npm init

Then we’ll update the dependencies section of package.json as below:

    // package.json

    "dependencies": {
      "apollo-server-express": "^1.3.2",
      "body-parser": "^1.18.2",
      "cors": "^2.8.4",
      "dotenv": "^4.0.0",
      "express": "^4.16.2",
      "express-jwt": "^5.3.0",
      "graphql": "^0.12.3",
      "graphql-tools": "^2.19.0",
      "jwks-rsa": "^1.2.1"
    }

These are all the dependencies for our GraphQL server. We’ll go over each of them as we begin to use them. Enter the command below to install them:

    npm install

Next, let’s create a server.js file and paste the code below into it:

    // server.js

    const express = require('express')
    const bodyParser = require('body-parser')
    const cors = require('cors')
    const { graphqlExpress } = require('apollo-server-express')
    const schema = require('./data/schema')
    const jwt = require('express-jwt')
    var jwks = require('jwks-rsa')
    require('dotenv').config()

    const PORT = 3000

    // create our express app
    const app = express()

    // enable CORS
    app.use(cors())

    // auth middleware
    const auth = jwt({
      secret: jwks.expressJwtSecret({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 5,
        jwksUri: `${process.env.AUTH0_ISSUER}.well-known/jwks.json`
      }),
      audience: process.env.AUTH0_AUDIENCE,
      issuer: process.env.AUTH0_ISSUER,
      algorithms: ['RS256']
    })

    // graphql endpoint
    app.use(
      '/api',
      bodyParser.json(),
      auth,
      graphqlExpress(req => ({
        schema,
        context: {
          user: req.user
        }
      }))
    )

    app.listen(PORT, () => {
      console.log(`The GraphQL server is running on http://localhost:${PORT}/api`)
    })

First, we pull in the dependencies and our GraphQL schema (which we’ll create shortly). express is the Node.js framework, body-parser is used to parse the incoming request body, graphqlExpress is the express implementation of Apollo server which will be used to power our GraphQL server. cors will be used to enable CORS on our GraphQL server since we’ll be consuming it from a client app running on a different domain from the GraphQL server. We’ll use express-jwt to validate a JSON Web Token (JWT) and set req.user to the attributes encoded in the JWT. jwks-rsa is a library that will allow us to retrieve RSA public keys from a JWKS (JSON Web Key Set) endpoint. dotenv will be used to read from our .env file.

Using the cors middleware, we enable CORS on the server. Then we define an authentication middleware using express-jwt. This will check if there is an Authorization header with a valid token on incoming requests made to the endpoint. If present, it will decode it then add a user object to the request. Otherwise, user will be null. jwks-rsa is use to retrieve the RSA public keys from our JWKS endpoint (which is our {Auth0 domain}/.well-known/jwks.json ). The RSA public key is then passed as secret to jwt. We set the audience, issuer and algorithms to those of the Auth0 API we created earlier.

Next, we define the route (in this case /api) for our GraphQL server. Then we add body-parser and auth middleware created above to the route. Also, we add graphqlExpress passing along our schema and user as context to GraphQL. Adding the auth middleware makes the route secured.

Finally, we start the server and listen on a specified port (as specified when creating our Auth0 API).

We are using the dotenv package to read environment variables from a .env file. Let’s create the .env file within the project’s root directory and paste the snippet below into it:

    // .env

    AUTH0_ISSUER=YOUR_API_ISSUER
    AUTH0_AUDIENCE=YOUR_API_AUDIENCE

Update YOUR_API_ISSUER and YOUR_API_AUDIENCE with your Auth0 API details we created above. AUTH0_ISSUER should be your Auth0 domain and AUTH0_AUDIENCE should be the identifier we set when we created the Auth0 API.

Defining GraphQL schema

Now, let’s define our GraphQL schema. Create a folder name data and, within this folder, create a schema.js file then paste the code below into it:

    // data/schema.js

    const { makeExecutableSchema } = require('graphql-tools')
    const resolvers = require('./resolvers')

    // Define our schema using the GraphQL schema language
    const typeDefs = `
      type Todo {
        userId: ID!
        title: String!
      }

      type Query {
        myTodos: [Todo]
      }

      type Mutation {
        addTodo (title: String!): Todo
      }
    `

    module.exports = makeExecutableSchema({ typeDefs, resolvers })

We define a Todo type which has userId and title fields. The userId will be the ID of the user that created the todo, and title will be the title of the todo. The myTodos query will be used to fetch todos of the currently authenticated user. Finally, we define an addTodo mutation which will be used to add a new todo, accepting the title of the todo. We don’t need to pass the ID of the user creating the todo, the userId will be the ID of the currently authenticated user.

Writing resolver functions

Within the data folder, create a resolvers.js file and paste following code into it:

    // data/resolvers.js

    // sample data
    const todos = []

    const resolvers = {
      Query: {
        // fetch authenticated user todos
        myTodos (_, args, { user }) {
          // make sure user is logged in
          if (!user) {
            throw new Error('You are not authenticated!')
          }

          // return only the authenticated user todos
          return todos.filter(todo => todo.userId === user.sub)
        }
      },

      Mutation: {
        // Add new todo
        addTodo (_, { title }, { user }) {
          // make sure user is logged in
          if (!user) {
            throw new Error('You are not authenticated!')
          }

          // add new todo to list of todos
          todos.push({
            userId: user.sub,
            title
          })

          // return the newly added todo
          return todos.find(todo => todo.userId === user.sub && todo.title === title)
        }
      }
    }

    module.exports = resolvers

Firstly, we define a todos variable as an empty array. This will be used as our list of todos. Then we define the myTodos resolver. We make sure the user is authenticated by checking if there is a user object available. If the user is authenticated, we filter the list of todos to only those where the userId matches the authenticated user ID. The sub claim on the JWT is used as the user ID, since Auth0 will send the user_id property as sub in the JWT. In the addTodo mutation, we also checked to make sure the user is authenticated before proceeding to add a new todo. To add a new todo, we simple push a new object containing the user ID and the title of the todo the user entered to the todos array. Lastly, we return the newly added todo as the response of the addTodo mutation.

That’s all for our GraphQL server. Now, let’s move to creating the frontend app.

Creating Auth0 client

Again, log in to your Auth0 dashboard and create a new Auth0 client. Give the client a name and select Single Page Web Application as the client type as in the image below:

new client

You should take note of your application keys which can be found in the client settings. We’ll be needing the Client ID and Domain.

Next, we’ll configure a callback URL for our app where Auth0 redirects the user after they are authenticated. Add http://localhost:8080/callback to the Allowed Callback URLs field in Client Settings.

Create new Vue app

The frontend app will be a Vue app. So, let’s create a new Vue app using the Vue CLI:

    vue init webpack graphql-auth0-client

Once the app is created, cd into the app directory and run the command below to install the app dependencies:

    npm install

Next, add the line below to index.html so we can make use of Bulma CSS framework:

    // index.html

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css" />

Adding authentication with Auth0

Create a new services folder inside the src directory. Then, within the services folder, create an Auth.js file and paste the code into it:

    // src/services/Auth.js

    import auth0 from 'auth0-js'
    import EventEmitter from 'EventEmitter'
    import router from './../router'

    export default class Auth {
      authenticated = this.isAuthenticated()
      authNotifier = new EventEmitter()

      constructor () {
        this.login = this.login.bind(this)
        this.setSession = this.setSession.bind(this)
        this.logout = this.logout.bind(this)
        this.isAuthenticated = this.isAuthenticated.bind(this)
      }

      auth0 = new auth0.WebAuth({
        domain: '{AUTH0_DOMAIN}',
        clientID: '{AUTH0_CLIENT_ID}',
        redirectUri: '{AUTH0_CALLBACK_URL}',
        audience: '{AUTH0_AUDIENCE}',
        responseType: 'token id_token',
        scope: 'openid'
      })

      login () {
        this.auth0.authorize()
      }

      handleAuthentication () {
        this.auth0.parseHash((err, authResult) => {
          if (authResult && authResult.accessToken && authResult.idToken) {
            this.setSession(authResult)
            router.replace('/')
          } else if (err) {
            router.replace('/')
          }
        })
      }

      setSession (authResult, profile) {
        // Set the time that the access token will expire at
        const expiresAt = JSON.stringify(
          authResult.expiresIn * 1000 + new Date().getTime()
        )

        localStorage.setItem('graphql_auth0_access_token', authResult.accessToken)
        localStorage.setItem('graphql_auth0_id_token', authResult.idToken)
        localStorage.setItem('graphql_auth0_expires_at', expiresAt)

        this.authNotifier.emit('authChange', { authenticated: true })
      }

      logout () {
        // Clear access token and ID token from local storage
        localStorage.removeItem('graphql_auth0_access_token')
        localStorage.removeItem('graphql_auth0_id_token')
        localStorage.removeItem('graphql_auth0_expires_at')

        this.authNotifier.emit('authChange', false)

        // navigate to the home route
        router.replace('/')
      }

      isAuthenticated () {
        // Check whether the current time is past the
        // access token's expiry time
        const expiresAt = JSON.parse(
          localStorage.getItem('graphql_auth0_expires_at')
        )

        return new Date().getTime() < expiresAt
      }
    }

Using our Auth0 client details, we create a new instance of Auth0 WebAuth. AUTH0_AUDIENCE should be the identifier we set when we created the Auth0 API, which is http://localhost:3000. Let’s quickly go over each of the methods above:

  • login will redirect the user to use Auth0 hosted login page for user login.
  • handleAuthentication, as it name implies, will be used to handle user authentication once they are redirected from the hosted login page. It looks for an authentication result in the URL hash and processes it with the parseHash method from auth0.js.
  • setSession will be used to set the user’s access_token, id_token, and a time at which the access_token will expire.
  • logout will remove the user’s tokens from browser local storage.
  • isAuthenticated checks whether the expiry time for the access_token has passed

You’ll notice we imported some packages we are yet to install. Let’s do that now:

    npm install auth0-js EventEmitter --save

Adding user login

Next, let’s add the login link which allow users to be redirect to login using Auth0 hosted login page. Update src/App.vue as below:

    // src/App.vue

    <template>
      <div>
        <nav class="navbar is-primary">
          <div class="container">
            <div class="navbar-brand">
              <router-link
                class="navbar-item"
                to="/">
                  GraphQL Auth0
              </router-link>
            </div>
            <div class="navbar-menu">
              <div class="navbar-end">
                <a
                  class="navbar-item"
                  v-if="!authenticated"
                  @click="login()">
                    Log In
                </a>
                <a
                  class="navbar-item"
                  v-else
                  @click="logout()">
                    Log Out
                </a>
              </div>
            </div>
          </div>
        </nav>
        <div class="container">
          <router-view
            :auth="auth"
            :authenticated="authenticated">
          </router-view>
        </div>
      </div>
    </template>

    <script>

    import Auth from '@/services/Auth'

    const auth = new Auth()
    const { login, logout, authenticated, authNotifier } = auth

    export default {
      name: 'App',
      data () {
        return {
          auth,
          authenticated
        }
      },
      created () {
        authNotifier.on('authChange', authState => {
          this.authenticated = authState.authenticated
        })
      },
      methods: {
        login,
        logout
      }
    }
    </script>

We show users the link to login. Once they are logged in, we show them the link to logout. Inside the script section, we import the Auth.js file then make a new instance of it and extract our methods from it. auth and authenticated are passed as data to the component. Once the component is created, we listen for the authChange event (using the EventEmiiter package we installed earlier) and set the authenticated data accordingly. This is used to determine whether a user is currently logged in or not. The login and logout methods makes use of the appropriate methods respectively pulled in from the Auth.js file.

Creating the app homepage

Users will be redirected to the homepage and they will be shown an appropriate message to login or not. Create a Home component inside src/components and paste the code below into it:

    // src/components/Home.vue

    <template>
      <div>
        <div v-if="authenticated">
          <p>You are logged in!</p>

          <p>
            <router-link to="addtodo">Add new todo</router-link>
            or view your list of <router-link to="mytodos">todos</router-link>
          </p>
        </div>
        <h4 v-else>
          You are not logged in! Please <a @click="auth.login()">Log In</a> to continue.
        </h4>
      </div>
    </template>

    <script>
    export default {
      name: 'Home',
      props: ['auth', 'authenticated']
    }
    </script>

This component accepts auth and authenticated that were passed from the App component as props. If a user is logged in, the user is shown links to add a new todo and view his/her list of todos respectively.

Next, let’s update the route file as below:

    // src/router/index.js

    import Vue from 'vue'
    import Router from 'vue-router'
    import Home from '@/components/Home'

    Vue.use(Router)

    export default new Router({
      mode: 'history',
      routes: [
        {
          path: '/',
          component: Home
        }
      ]
    })

Note: Because Auth0 will be passing the authenticated user access_token as URL hash, we need to set Vue Router to use history mode.

Adding a callback component

Recall we said once a user is authenticated, the user will be redirected to the callback URL we defined when we created our Auth0 client. So, let’s create a Callback component that we’ll use to handle the redirect. Create a callback.vue file inside src/components and paste the code below into it:

    // src/components/Callback.vue

    <template>
      <div>Loading...</div>
    </template>

    <script>
    export default {
      name: 'Callback',
      props: ['auth'],
      created () {
        this.auth.handleAuthentication()
      }
    }
    </script>

We display a loading… (you can use a spinner here) message to the user while the user is been authenticated. This makes use of the handleAuthentication method we talked about earlier. Then the user is redirected to the homepage.

Next, let’s add the callback route:

    // src/router/index.js

    import Callback from '@/components/Callback'

    // add these to the routes array
    {
      path: '/callback',
      component: Callback
    }

Installing Vue Apollo

To interact with our GraphQL server, we’ll be making use of Apollo client. To use Apollo client in a Vue app, we need to install the Vue Apollo plugin which is an Apollo/GraphQL integration for Vue.js. So, let’s install it alongside its dependencies:

    npm install vue-apollo@next graphql apollo-client apollo-link apollo-link-context apollo-link-http apollo-cache-inmemory graphql-tag --save

Next, let’s setup the plugin to be used in our Vue app. Update src/main.js as below:

    // src/main.js

    import Vue from 'vue'
    import { ApolloClient } from 'apollo-client'
    import { HttpLink } from 'apollo-link-http'
    import { setContext } from 'apollo-link-context'
    import { InMemoryCache } from 'apollo-cache-inmemory'
    import VueApollo from 'vue-apollo'
    import App from './App'
    import router from './router'

    Vue.config.productionTip = false

    const httpLink = new HttpLink({
      uri: 'http://localhost:3000/api'
    })

    const authLink = setContext((_, { headers }) => {
      // get the authentication token from localstorage if it exists
      const token = localStorage.getItem('graphql_auth0_access_token')
      // return the headers to the context so httpLink can read them
      return {
        headers: {
          ...headers,
          authorization: token ? `Bearer ${token}` : null
        }
      }
    })

    // Create the apollo client
    const apolloClient = new ApolloClient({
      link: authLink.concat(httpLink),
      cache: new InMemoryCache()
    })

    // Install the vue plugin
    Vue.use(VueApollo)
    const apolloProvider = new VueApollo({
      defaultClient: apolloClient
    })

    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router,
      apolloProvider,
      components: { App },
      template: '<App/>'
    })

We pull in the necessary dependencies. Then we create an httpLink using the URL to our GraphQL server. We create an auth middleware, which will add an Authorization header containing the user access token or null to every requests. Next, we create the Apollo client passing along the authLink and httpLink. We also specify we want an in memory cache. Then we install the Vue plugin passing to it the Apollo client we created. Lastly, we make use of apolloProvider by adding it in our Vue instance.

Displaying user todos

Once a user is logged, the user should be able to view a list of his/her todos. For this, we’ll create a MyTodos component inside src/components and paste the code below into it:

    // src/components/MyTodos.vue

    <template>
      <div class="section">
        <h2 class="title">My Todos</h2>
        <ul>
          <p v-if="myTodos.length === 0">No todos!</p>
          <li
            v-else
            v-for="(todo, key) in myTodos"
            :key="key">
            {{ todo.title }}
          </li>
        </ul>
      </div>
    </template>

    <script>
    import { MY_TODOS_QUERY } from '@/graphql'

    export default {
      name: 'MyTodos',
      props: ['authenticated'],
      data () {
        return {
          myTodos: []
        }
      },
      beforeRouteEnter (to, from, next) {
        next(vm => {
          return vm.authenticated ? next() : next('/')
        })
      },
      apollo: {
        myTodos: {
          query: MY_TODOS_QUERY
        }
      }
    }
    </script>

We simply loop through the user’s todos and display them in a list. If the user has not added any we’ll display an appropriate message. Within the apollo object, we define the GraphQL query (MY_TODOS_QUERY, which we’ll create in a bit) to fetch the user’s todos. It is important to note that our data myTodos must have the same name as the query object (myTodos) defined inside the apollo object.

This component accepts authenticated as props. To make sure only authenticated users can access the component, we define a beforeRouteEnter guard which checks if a user is authenticated or not. If the user is authenticated, the user will be allowed to access the component. Otherwise, the user is redirected to the homepage.

Next, let’s create the MY_TODOS_QUERY GraphQL query. Create a graphql.js file inside src and paste the code below into it:

    // src/graphql.js

    import gql from 'graphql-tag'

    export const MY_TODOS_QUERY = gql`
      query MyTodosQuery {
        myTodos {
          title
        }
      }
    `

We make use of the graphql-tag package to parse our GraphQL query. We are returning only the title of the todo.

Lastly, let’s add the route:

    // src/router/index.js

    import MyTodos from '@/components/MyTodos'

    // add these to the routes array
    {
      path: '/mytodos',
      component: MyTodos
    }

Users should be able to see a list of the todos:

my todos screen

Adding new todo

Now, let’s add ability for users to add new todo. For this, we’ll create a AddTodo component inside src/components and paste the code below into it:

    // src/components/AddTodo.vue

    <template>
      <div class="section">
        <div class="columns">
          <div class="column is-6">
            <h2 class="title">Add Todo</h2>
            <form method="post" @submit.prevent="addTodo">
              <div class="field">
                <label class="label">Title</label>
                <div class="control">
                  <input
                    type="text"
                    class="input"
                    v-model="title">
                </div>
              </div>
              <div class="control">
                <button class="button is-primary">Add</button>
              </div>
            </form>
          </div>
        </div>
      </div>
    </template>
    <script>
    import { ADD_TODO_MUTATION, MY_TODOS_QUERY } from '@/graphql'

    export default {
      name: 'AddTodo',
      props: ['authenticated'],
      data () {
        return {
          title: ''
        }
      },
      beforeRouteEnter (to, from, next) {
        next(vm => {
          return vm.authenticated ? next() : next('/')
        })
      },
      methods: {
        addTodo () {
          this.$apollo
            .mutate({
              mutation: ADD_TODO_MUTATION,
              variables: {
                title: this.title
              },
              update: (store, { data: { addTodo } }) => {
                // read data from cache for this query
                const data = store.readQuery({ query: MY_TODOS_QUERY })
                // add new todo from the mutation to existing todos
                data.myTodos.push(addTodo)
                // write data back to the cache
                store.writeQuery({ query: MY_TODOS_QUERY, data })
              }
            })
            .then(response => {
              this.$router.replace('/mytodos')
            })
        }
      }
    }
    </script>

We display a single field form to add new todo. Once the form is submitted, the addTodo method will be called. This method makes use of the Vue Apollo plugin mutate(). We pass to it the mutation to add a new todo (ADD_TODO_MUTATION, which we’ll create in a bit). We also pass to it the required variables (in this case title, which is gotten from the form). Because Apollo client caches (in memory in our case) its queries, we need a way to update the cache whenever we perform mutation actions. Hence the need for the update function which we use to update the store by adding the newly added todo to the cache. First, we fetch the data from the cache matching our query (MY_TODOS_QUERY), then we add the new todo to the myTodos array. Lastly, we write the new data back to cache. Once the todo is added successfully, we redirect the user to /mytodos route.

Just as we did with the MyTodos component, we make sure only authenticated users can add new todo.

Next, let’s create the ADD_TODO_MUTATION GraphQL mutation. Add the code below to src/graphql.js:

    // src/graphql.js

    export const ADD_TODO_MUTATION = gql`
      mutation AddTodoMutation($title: String!) {
        addTodo(title: $title) {
          title
        }
      }
    `

Again, we return only the title of the todo from the mutation.

Lastly, let’s add the route:

    // src/router/index.js

    import AddTodo from '@/components/AddTodo'

    // add these to the routes array
    {
      path: '/addtodo',
      component: AddTodo
    }

Users should be able to add new todo:

add new todo

The complete code for the server and the client is available on GitHub.

Conclusion

We have seen how to add authentication with Auth0 to GraphQL. This tutorial shows how to build a simple todo app, but I hope you get the concept. You can also use hosted GraphQL backend services like Graphcool and Scaphold, which has out of the box integration for Auth0.

This brings to the end the handling authentication in GraphQL series. I hope you found the series helpful.

This is part 3 of a 3 part tutorial. You can find part 1 here and part 2 here.