Using Amplify CLI for adding Auth to your Hasura GraphQL engine
Tags: hasura, aws, architecture, authentication
Published: 2019-04-22 06:34:13

---
title: Using Amplify CLI for adding Auth to your Hasura GraphQL engine
published: true
description: Using AWS Cognito with Amplify CLI and Hasura for your backend
tags: Hasura, AWS, Architecture, Authentication
---
![](https://thepracticaldev.s3.amazonaws.com/i/u629ietrbn5h7bfbhuyd.png)

Let's assume you already played around with [Hasura](hasura.io) open source GraphQL engine and you were wondering how to add an authentication layer with AWS Cognito. Couple of words about Hasura if you are not familiar. Hasura is free and open source engine that auto generates GraphQL API on top of new or **existing** Postgres database. If this is the first time you hear about it, I suggest to check my introductory article here:

{%twitter 1092751461824585728 %}

and if you were wondering about how to use Authentication with Hasura you can read about it here:

{%twitter 1115141766003470336 %}

In last article I showed AWS Cognito as one of solutions how to create your authentication for your app and pass Auth token to Hasura. In this article I want to explain how you do that with `aws-amplify` CLI as well as how you connect your users to actual cognito user pool users. 

In a nutshell we will cover

1. How to deploy Hasura
2. How to setup aws amplify with aws cognito
3. How to setup a AWS lambda to update our database after user is created in cognito
4. Customise cognito JWT creation to enable hasura permission rules

### Table of Contents

* [Creating and deploying our engine](#deploy)    
* [Setting up Hasura CLI](#cli)    
* [Setting our database structure](#db)    
* [Setting our Auth with Amplify CLI](#amplify)    
* [Creating your Frontend](#frontend)    
* [Adding a new User pool user into Hasura](#aws-hasura)    
 - [Pre Sign-Up Lambda](#signup)    
 - [Pre token generation Lambda](#token)  
 - [Create Roles in Hasura Permission system](#roles)  


# Creating and deploying our engine <a name="deploy"></a>

Let's start by regular step of [Deploying to Heroku](https://heroku.com/deploy?template=https://github.com/hasura/graphql-engine-heroku)

![Screenshot Image](https://thepracticaldev.s3.amazonaws.com/i/y0pjwywxbfkmxyk670s4.png)

> We also can use AWS RDS to deploy our engine which is described in detail [here](https://dev.to/lineup-ninja/deploying-hasura-on-aws-with-fargate-rds-and-terraform-4gk7)

Now before we setting any auth or continue in general we will secure our endpoint by setting `HASURA_GRAPHQL_ADMIN_SECRET` 

![Screenshot Image](https://thepracticaldev.s3.amazonaws.com/i/iktqe8d1ti4zqewg3wew.png)


# Setting up Hasura CLI <a name="cli"></a>

We want changes done in our console to be reflected as migrations files and stored in source control as explained in detail [here](https://www.youtube.com/watch?v=fRHZQAEd-Q0)

What we will do is set `HASURA_GRAPHQL_ENABLE_CONSOLE` to `false` in our heroku config vars 

![Screenshot Image](https://thepracticaldev.s3.amazonaws.com/i/2c9gzimsvtklpe5rv3hz.png)

Now it's time to init our hasura project with through [Hasura CLI](https://docs.hasura.io/1.0/graphql/manual/hasura-cli/install-hasura-cli.html#install-hasura-cli)

`hasura init --endpoint https://aws-sample.herokuapp.com`

And then 
```bash
cd hasura
hasura console --admin-secret=<your admin secret>
```

Console will open up on localhost and whatever you do in the console will be saved in local migration files that you can store in source control.


# Setting our database structure <a name="db"></a>

Now when we have our engine set up, we need to create database structure. We will create simple blog cms so we will have only `posts` and `users` table. Important part is that `users` table `id` have to be of a text format so we can wire it up with AWS user pool properly. 

Posts table will look like this:
![Screenshot](https://thepracticaldev.s3.amazonaws.com/i/f4jp8k1b70dwhrnwebfb.png)

User table will look like this:
![Screenshot](https://thepracticaldev.s3.amazonaws.com/i/gvwwap9xwx1cquibsq91.png)

I also added object relationship from posts userId to user object

![Screenshot](https://thepracticaldev.s3.amazonaws.com/i/car8i8gc0se9n94e9bs0.png)

so we can query our users from posts query like so

```graphql
query {
  posts {
    title
    content
    user {
      name
    }
  }
}
```

Now when our backend ready it's time to set up our frontend and authentication. 
Now I don't know what frontend framework you use, so I will use React for this example, but the process is similar in Vue and Angular. I hardly will touch Frontend here. My main goal is so you will understand the flow.


# Setting our Auth with Amplify CLI <a name="amplify"></a>

```bash
npm install -g @aws-amplify/cli
amplify configure
```

This will open up the AWS console. Once you are logged in to the console you can jump back to the command line. Then we need to specify AWS region. 

In the next step we will need to specify a new AWS IAM user we about to create.
It will open up IAM dashboard with the user we already set up with the one we specified through CLI. 

![](https://thepracticaldev.s3.amazonaws.com/i/espqxi0ua5uyx72b0b6o.png)
![](https://thepracticaldev.s3.amazonaws.com/i/nenub6vtojh9bgy6ywjq.png)


Next we will need to set up AWS permissions. I will just click Next, Review and will create a user. You will be presented with a screen with Access key and secret which you should type in amplify cli 

![](https://thepracticaldev.s3.amazonaws.com/i/9iul2x2ed9gx4bg4cy8k.png)

![](https://thepracticaldev.s3.amazonaws.com/i/q9hyp95zc3lf1loc50vc.png)


# Creating your Frontend <a name="frontend"></a>

We will use Amplify auth module with React, but you can choose your own favorite framework. Check Amplify [Vue docs](https://aws-amplify.github.io/docs/js/vue) and [Angular docs](https://aws-amplify.github.io/docs/js/angular) for more detail.

we will start by creating our frontend with 

```bash
npx create-react-app react-frontend
```

and then run 

```bash
amplify init
```

![](https://thepracticaldev.s3.amazonaws.com/i/0332ylnvy4gyf8n8baez.png)

To add basic authentication with User pools run `amplify add auth` with default configuration. After adding auth we need to deploy it to AWS. This can be done by running `amplify push` which will update our resources in the cloud and will add Cognito User Pool

It's time to add auth to our React app:


```bash
yarn add aws-amplify aws-amplify-react
```

In our `App.js` file let's wrap our app with Amplify withAuthenticator HOC

```react
import Amplify, { Auth } from 'aws-amplify';
import awsmobile from './aws-exports';
import { withAuthenticator } from 'aws-amplify-react'; // or 'aws-amplify-react-native';

Amplify.configure(awsmobile);


export default withAuthenticator(App, true);
```

What we will be presented is AWS auth screen where we will have sign up, sign in etc. We won't cover how to customize everything for your app, so let's just use the default one

![](https://thepracticaldev.s3.amazonaws.com/i/nxfdjba4xnmrgay6wedz.png)

Now let's add some behavior on sign up/sign in. 
Whenever our App is authenticated, basically our App component will be rendered. 
So if I will add this code to our App.js:

```react
import { Auth } from 'aws-amplify'

//....rest of code

componentDidMount(){
    Auth.currentAuthenticatedUser().then(user => {
      console.log(user)
    })
}

//rest of code
```

you will be able to get user data. 

So far we have authentication, but there is a problem. We need to add this auth data to our Hasura.

# Adding a new User pool user into Hasura <a name="aws-hasura"></a>

If we open our AWS console on User pool tab we will be able to see that we can create custom Triggers for different stages in authentication process. We will need to create at least two triggers. Pre sign-up and pre token generation.

![](https://thepracticaldev.s3.amazonaws.com/i/pnq4quf6k5jbwr0hpcbq.png)

Let's start with sign up trigger

## Pre Sign Up Lambda <a name="signup"></a>

Let's go to AWS Lambda console and create a new lambda. You will have to create it locally first because we will have `node-fetch` dependency, so we will need to upload a zip to AWS console. Our lambda `index.js` file will look like this:

```javascript
const fetch = require('node-fetch');

const adminSecret = process.env.ADMIN_SECRET;
const hgeEndpoint = process.env.HGE_ENDPOINT;

const query = `
mutation addUser($id: String!, $phone:String!, $email: String!){
  insert_users(objects:[{
    email: $email
    id: $id
    phone: $phone
  }]) {
    returning {
      email
    }
  }
}
`

exports.handler = (event, context, callback) => {
  
try {
  const qv = {
    email: event.request.userAttributes.email, 
    phone: event.request.userAttributes.phone_number,
    id: event.userName
  };
  
  fetch(hgeEndpoint + '/v1alpha1/graphql', {
      method: 'POST',
      body: JSON.stringify({query: query, variables: qv}),
      headers: {'Content-Type': 'application/json', 'x-hasura-admin-secret': adminSecret},
  })
  .then(res => res.json())
  .then(json => {
      callback(null, event);
  });


} catch (e) {
  callback(null, false)
}
  

};
```

What we are trying to do here is to insert a new user to database by executing mutation. You can notice that we pass our Admin Secret at headers, but this is fine for server to server communication. What is happening here is that our `id` will be cognito `username` 

Let's try to Sign Up now.

![](https://thepracticaldev.s3.amazonaws.com/i/nxfdjba4xnmrgay6wedz.png)

We will get notification mail with our verification code

![](https://thepracticaldev.s3.amazonaws.com/i/wx0e4vf08qbweric6kud.png)

Now we can sign in and see our Auth token in the console.

The cool part is also that we will have our user created in Postgres. Let's check our console:

![](https://thepracticaldev.s3.amazonaws.com/i/1xcpe7thr04wmyep7dr5.png)


## Pre Token Generation lambda <a name="token"></a>

We are getting our token, but it's not enough. If we want to use `JWT_TOKEN` with Hasura we need to do several things. (I explained JWT auth in details [here](https://dev.to/hasurahq/hasura-authentication-explained-2c95)):

### Set `HASURA_GRAPHQL_JWT_SECRET`

Set `HASURA_GRAPHQL_JWT_SECRET` as env variable in our Heroku. Our config will be:

```json
{
"type":"RS256",
    "jwk_url": "https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-    known/jwks.json",
"claims_format": "stringified_json"
}
```

> Note that {userPoolId} should be substituted to your userPoolId taken from AWS console as well as {region} to correct region you are using



### Create our Pre Token generation lambda.

For our pre token generation lambda we need to add custom claims. We will pass username as user id so we will be able to reference it in Hasura permission system

```javascript
exports.handler = (event, context, callback) => {
    event.response = {
        "claimsOverrideDetails": {
            "claimsToAddOrOverride": {
                "event": JSON.stringify(event.userName),
                "https://hasura.io/jwt/claims": JSON.stringify({
                    "x-hasura-allowed-roles": ["anonymous","user", "admin"],
                    "x-hasura-default-role": event.userName === "vnovick" ? "admin": "user",
                    "x-hasura-user-id": event.userName
                })
            }
        }
    }
    callback(null, event)
}
```


Let's copy `signInUserSession.idToken.jwtToken` from `Auth.currentAuthenticatedUser()` response and paste it in Hasura console as `Authorization` `Bearer` token 

Now all custom claims will be passed to Hasura permissions system and we will be able to reference them there.

## Create Roles in Hasura Permission system <a name="roles"></a>

Let's see how it's done. We will create two additional roles - `anonymous` without any permissions and a `user` role.

I want user to be able to query only their own user data as well as see only their posts. We can set up permissions in the console accordingly:

![](https://thepracticaldev.s3.amazonaws.com/i/chrhls55ko0vk2luzh8a.png)

`user` role will be able to see only their own data. 

Let's double check that by querying all users:

![](https://thepracticaldev.s3.amazonaws.com/i/xony4qi9e89psbjwqq4d.png)



# Summary

This blog post is only a brief overview of what you can do with Hasura and AWS Cognito, but I hope you get the idea of main auth workflow you should stick to. As you can see, because we use Lambda triggers and plug them into Cognito auth process we can do any level of complexity. We can validate if our user exists, add custom variables to hasura custom claims and so on. 

After reading this blog post I will appreciate if you can give me feedback what parts you want me to cover more in depth so I can either write another more advanced blog post or lead you to resources explaining these parts.