Add Authentication and Authorization to Next.js 8 Serverless Apps using JWT and GraphQL
Tags: nextjs, graphql, jwt, authentication
Published: 2019-12-03 11:58:56

---
title: Add Authentication and Authorization to Next.js 8 Serverless Apps using JWT and GraphQL
published: true
date: 2019-04-04 07:18:38 UTC
tags: nextjs, graphql, jwt, authentication
canonical_url: https://blog.hasura.io/add-authentication-and-authorization-to-next-js-8-serverless-apps-using-jwt-and-graphql/
---

## TL;DR

- Deploy a Node.js Express JWT service for authenticating requests to Hasura GraphQL Engine.
- Authorization using JWT and Hasura GraphQL permissions
- A [sample Next.js 8 app](https://github.com/hasura/graphql-engine/tree/master/community/sample-apps/nextjs-8-serverless/with-apollo-jwt) with login,signup and articles listing page
- Deploy to Now.sh using **serverless** target

![](https://blog.hasura.io/content/images/2019/04/nextjwthasura.png)<figcaption>Next.js 8 + JWT + Hasura GraphQL</figcaption>

## Tech Stack

- [Next.js 8](https://nextjs.org) for building the sample serverless react app
- [Apollo Client](https://www.apollographql.com/docs/react/) for GraphQL querying
- [Node.js server](https://github.com/hasura/graphql-engine/tree/master/community/boilerplates/auth-servers/passportjs-jwt-roles) for JWT authentication
- [Hasura GraphQL Engine](https://github.com/hasura/graphql-engine) for GraphQL APIs with permissions

Let's get the backend up and running before configuring the Next.js 8 app.

## Deploy Hasura

[Hasura](https://hasura.io/) is an [open-source](https://github.com/hasura/graphql-engine) engine that gives you realtime GraphQL APIs on new or existing Postgres databases, with built-in support for stitching custom GraphQL APIs and triggering webhooks on database changes.

Follow the instructions in the [docs](https://docs.hasura.io/1.0/graphql/manual/getting-started/heroku-simple.html) to deploy Hasura. Note the Heroku URL for GraphQL Endpoint. You will be configuring this in the app later.

Apply the migrations by following the instructions in this [section](https://github.com/hasura/graphql-engine/tree/master/community/sample-apps/nextjs-8-serverless/with-apollo-jwt/README.md#create-the-initial-tables) to create the necessary database schema and permissions.

Now the backend is ready! You will be able to instantly query using Hasura GraphQL APIs. The endpoint will look like (https://myapp.herokuapp.com/v1/graphql). We will come back to this during the integration with the Next.js app.

## Run JWT Server

We will now run the JWT server locally to handle signup and login requests from the Next.js app.

```
#clone the repo
git clone https://github.com/hasura/graphql-engine

# Change directory
cd community/boilerplates/auth-servers/passportjs-jwt-roles

# Install NPM dependencies
npm install

# Generate the RSA keys
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout > public.pem

# print the keys in an escaped format
awk -v ORS='\\n' '1' private.pem
awk -v ORS='\\n' '1' public.pem
```

Now, get the database URL from Heroku by heading to the settings page of the app.

![](https://blog.hasura.io/content/images/2019/04/heroku-database-url.png)

```
export DATABASE_URL=postgres://postgres:@localhost:5432/postgres?ssl=true

# Then simply start your app
npm start
```

Replace DATABASE\_URL with the value obtained from Heroku config. Do note that you need to append `?ssl=true` at the end of the `DATABASE_URL`.

### Configure JWT with Hasura

Now that the JWT server is ready, we need to configure Hasura to use the JWT Secret. The env `HASURA_GRAPHQL_JWT_SECRET` should be set like this:

```
{ "type": "RS256", "key": "<AUTH_PUBLIC_KEY>" }
```

Where `<AUTH_PUBLIC_KEY>` is your RSA public key in PEM format, with the line breaks escaped with "\n". This was generated just above.

In order to configure the JWT Secret we also need to set the admin secret. Head over to [docs](https://docs.hasura.io/1.0/graphql/manual/deployment/heroku/securing-graphql-endpoint.html#add-the-hasura-graphql-admin-secret-env-var) to set this up on Heroku.

## Run the Next.js App

Both the JWT server and Hasura GraphQL Engine has been setup and configured.

Now let's run the Next.js app by running the following commands:

```
# Change directory
cd community/sample-apps/nextjs-8-serverless/with-apollo-jwt/app
```

Configure the Heroku app url in `lib/init-apollo.js`.

Configure the Auth Server URL in `pages/signup.js` and `pages/login.js`. For example,

```
// inside pages/signup.js
const apiUrl = 'http://localhost:8080/signup'

// inside pages/login.js
const apiUrl = 'http://localhost:8080/login'
```

Now install the dependenices, build the serverless bundle and run the app using the following commands.

```
# Install dependencies
yarn install

# Build the app
yarn run build

# Run the app
yarn run dev
```

Once you run the app, you should be getting a screen like the one below:

![](https://blog.hasura.io/content/images/2019/04/nextjs-app-jwt.png)

## Authenticated Query

Apollo Client has been configured with an Auth Middleware which sets the Authorization header (if available).

```
const authMiddleware = new ApolloLink((operation, forward) => {
    // add the authorization to the headers
    operation.setContext({
      headers: {
        authorization: getToken(),
      }
})
```

We call a utility function called `getToken()` which gets the auth token stored in the cookie and returns the token.

```
const getToken = () => {
  let token = null;
  if (typeof document !== 'undefined') {
    token = 'Bearer ' + cookie.get('token')
  }
  return token
}
```

The actual implementation of signup and login sets the cookie with the token.

In the file `utils/auth.js`, we handle the logic of what needs to be done once the server returns the token after successful signup/login.

## Authorization using JWT

Now that the user is logged in, we would like to fetch only the articles written by the same user. The permissions have been configured in such a way that only the user who wrote the article will be able to fetch the data.

Head to the Heroku app URL to open Hasura console and navigate to                   **Data-\>article-\>Permissions** to see the permissions defined for the `user` role.

![](https://blog.hasura.io/content/images/2019/04/article_table_permissions.png)

The permission check looks like:

```
{ "user_id": {"_eq": "X-Hasura-User-Id"}}
```

This means that when a request is being sent with `Authorization: Bearer <token>` from the client, it will look for the `X-Hasura-User-Id` value from the token payload and filter it for the `user_id` column, ensuring that only logged in users get the data and also **get only their data**. The user has permissions to access all columns.

## Protected Routes using HOC

Though we have handled authorized queries, we would like to restrict access to pages like `/articles` at the route level because it is available only for logged in users. We again make use of an HOC to handle this.

When a navigation is triggered on a protected route, we call a utility helper method `auth` which will fetch the token from the cookie using the `nextCookie` module. In case the token is not available, then it will redirect to `/login` page.

```
export const withAuthSync = WrappedComponent =>
  class extends Component {
    static displayName = `withAuthSync(${getDisplayName(WrappedComponent)})`

    static async getInitialProps (ctx) {
      const token = auth(ctx)

      const componentProps =
        WrappedComponent.getInitialProps &&
        (await WrappedComponent.getInitialProps(ctx))

      return { ...componentProps, token }
    }

    constructor (props) {
      super(props)

      this.syncLogout = this.syncLogout.bind(this)
    }

    componentDidMount () {
      window.addEventListener('storage', this.syncLogout)
    }

    componentWillUnmount () {
      window.removeEventListener('storage', this.syncLogout)
      window.localStorage.removeItem('logout')
    }

    syncLogout (event) {
      if (event.key === 'logout') {
        console.log('logged out from storage!')
        Router.push('/login')
      }
    }

    render () {
      return <WrappedComponent {...this.props} />
    }
  }

export const auth = ctx => {
  const { token } = nextCookie(ctx)

  /*
   * This happens on server only, ctx.req is available means it's being
   * rendered on server. If we are on server and token is not available,
   * means user is not logged in.
   */
  if (ctx.req && !token) {
    ctx.res.writeHead(302, { Location: '/login' })
    ctx.res.end()
    return
  }

  // We already checked for server. This should only happen on client.
  if (!token) {
    Router.push('/login')
  }

  return token
}
```

We wrap the protected components like `articles` using the `withAuthSync` HOC which will take care of the redirects on the server.

After logging in, when you visit the articles page, you should be seeing the articles written by you.

![](https://blog.hasura.io/content/images/2019/04/nextjs-articles-page.png)

Don't be surprised to see data being empty. You would need to insert articles tagging your user id in the Hasura console.

## Deploy on Now

We can deploy this Next.js app on `now.sh` with the `serverless` target.

Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download)):

```
npm install -g now
now
```

Do note that, the JWT server has to be deployed as well. The JWT server url has to be configured inside the Next.js app.

I have put together a boilerplate so that you can get started quickly!

Check it out on [github](https://github.com/hasura/graphql-engine/tree/master/community/sample-apps/nextjs-8-serverless/with-apollo-jwt).

Take it for a spin and let us know what you think. If you have any questions or run into any trouble, feel free to reach out to us on [twitter](https://twitter.com/hasurahq), [github](https://github.com/hasura/graphql-engine) or on our [discord server](https://discord.gg/vBPpJkS).