Hasura Authentication Explained
Tags: graphql, hasura, auth
Published: 2019-04-08 06:35:25

---
title: Hasura Authentication Explained
published: true
description: In this blog post I will explain various authentication options that can be used with Hasura
tags: GraphQL, Hasura, Auth
canonical_url: https://blog.hasura.io/hasura-authentication-explained
---

As you may know from various other blog posts found on [blog.hasura.io](blog.hasura.io), Hasura supports various types of authentication and in the following blog post I want to lay out what are your authentication options when using Hasura in production.

![](https://thepracticaldev.s3.amazonaws.com/i/8qbcqnju140d1idkzj1v.jpg)

We will talk about the following things:

  * [Securing GraphQL endpoint](#secure-endpoint)    
  * [Auth Webhooks](#auth-webhook)
    - [How to get started with custom webhook](#custom-webhook)
    - [Setting up Hasura with auth webhook](#custom-webhook-1)
  * [Auth using JWT](#jwt-root)
    - [Custom JWT server](#custom-jwt-server)
    - [Auth0](#auth0)
    - [Firebase](#firebase)
    - [AWS Cognito](#cognito)
  * [Hasura Permission system](#permissions)


## Securing your GraphQL endpoint <a name="secure-endpoint"></a>


When creating a new instance of Hasura engine, you've probably seen **Secure your endpoint** on top of the console:

![hasura secure endpoint pic](https://thepracticaldev.s3.amazonaws.com/i/6hty5icfrafqyq6z3hrp.png)

This link leads to the docs section describing how to secure your GraphQL endpoint by passing an environment variable `HASURA_GRAPHQL_ADMIN_SECRET`. Whether you are using Docker, Heroku or anything else, that will be the first step you will do.

So now if you try to access the console, you will get a simple "login" page that will ask you to specify your secret

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

The name `ADMIN_SECRET` suggest that if you pass it in your request headers, you will be giving **admin** permissions to API consumer. That's why it's important that you use it only from server to server interactions such as using it from serverless functions etc.

So that is not an actual authentication. That's just securing your endpoint. Now, what about Authentication and what is the right authentication method for you?

You may want to use Firebase, Auth0, Firebase Functions, Cognito, Your custom auth server, some unknown auth provider e.t.c

So before looking at different Auth implementations and having lots of blog posts links and sample apps down below, let's divide our authentication type to two major types

## Auth webhooks <a name="auth-webhook"></a>

So what is a Webhook? In a nutshell, whenever you set a specific environment variable for Hasura engine, that includes custom URL, all request headers (unless your webhook is configured to use GET) will be passed to this custom URL. In your Auth webhook, you can do whatever you want and it must return either `200` or `401` status codes. Along with `200` status code, you send a bunch of variables prefixed by `X-HASURA-*` that can be used in Hasura permission system, that we will discuss later on


### How we get started with our custom webhook? <a name="custom-webhook"></a>

Let's look at the case study of [passport-js](https://github.com/hasura/graphql-engine/tree/master/community/boilerplates/auth-webhooks/passport-js) webhook boilerplate example we have available.

We will start by cloning [passport-js](https://github.com/hasura/graphql-engine/tree/master/community/boilerplates/auth-webhooks/passport-js) and following the [guide](https://github.com/hasura/graphql-engine/tree/master/community/boilerplates/auth-webhooks/passport-js#deploy-with-heroku) to deploy it on Heroku

Now after it's deployed you can run it with npm start and sending `/login` or `/signup` requests and getting back the username and token

```bash
curl -H "Content-Type: application/json" \                           
     -d'{"username": "test123456", "password": "test123"}' \
     https://passport-auth-hasura.herokuapp.com/login
```

and the result will be:

```bash
{
  "id": 3,
  "username": "test123456",
  "token": "dd26537df94305f35dca9605b9fade7b"
}%
```

Now it's time to setup Hasura engine on the database we've just created 

```bash
docker run -d -p 8081:8080 -e HASURA_GRAPHQL_DATABASE_URL=DATABASE_URL \
-e HASURA_GRAPHQL_ENABLE_CONSOLE=true \
-e HASURA_GRAPHQL_ADMIN_SECRET=secret \
hasura/graphql-engine:v1.0.0-alpha41

```

When accessing the console you will see the following:

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

as you can see `users` table was created when we ran knex migrations when set our passport-js boilerplate, but we want to use it also from our GraphQL API, so make sure you click `Track` button and now you will be able to query your users:

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

So how do we log in? And how do we define permissions?

the passport-js example uses LocalStrategy, so a person must authenticate with username and password, as a result, it will return 

```
{
  "id": 3,
  "username": "test123456",
  "token": "dd26537df94305f35dca9605b9fade7b"
}
```

So how do we structure our client app?

The idea is that we need to try to login similar that how we logged in from the console by hitting the following endpoint  https://<your-heroku-deployed-passport-app>.herokuapp.com/login

and as a result, get token.
Now we need to pass that token in every request to Hasura as Authorization header like so:

`"Authorization": "Bearer dd26537df94305f35dca9605b9fade7b"`

### Setting up Hasura with auth webhook <a name="custom-webhook-1"></a>

Now what is left to do is to configure the webhook env variable and set permissions. To add auth webhook stop our docker container and add `HASURA_GRAPHQL_AUTH_HOOK` env variable.

```bash
docker run -d -p 8081:8080 \
-e HASURA_GRAPHQL_DATABASE_URL=DATABASE_URL \
-e HASURA_GRAPHQL_ENABLE_CONSOLE=true \
-e HASURA_GRAPHQL_ADMIN_SECRET=secret \
-e HASURA_GRAPHQL_AUTH_HOOK=https://<heroku-app-name>.herokuapp.com/webhook \
hasura/graphql-engine:v1.0.0-alpha41

```

Now what will happen is when you pass Authorization header, it will be passed to custom auth webhook and processed by it. 
Let's take a look at passport-js code:

```javascript
exports.getWebhook = async (req, res, next) => {
  passport.authenticate('bearer', (err, user, info) => {
    if (err) { return handleResponse(res, 401, {'error': err}); }
    if (user) {
      handleResponse(res, 200, {
        'X-Hasura-Role': 'user',
        'X-Hasura-User-Id': `${user.id}`
      });
    } else {
      handleResponse(res, 200, {'X-Hasura-Role': 'anonymous'});
    }
  })(req, res, next);
}

```

As you can see we pass either `anonymous` role or `user.id`.
Now we can set our permissions accordingly in the Permissions tab.


Let's take a look at a different use case - firebase functions [auth hook](https://github.com/hasura/graphql-engine/blob/master/community/boilerplates/auth-webhooks/firebase-cloud-functions/index.js)

The idea is the same - return `X-Hasura-User-Id` and `X-Hasura-Role` to be used in Hasura permissions system. But specifically in this auth webhook we also validate with firebase that the `id_token` we've been passed is a correct one

There are other auth-webhooks boilerplate that you can check [here](https://github.com/hasura/graphql-engine/blob/master/community/boilerplates/auth-webhooks/nodejs-express/server.js).

Let's take a look at auth flow for webhooks

![](https://docs.hasura.io/1.0/_images/webhook-auth1.png)

- Pass headers to Hasura
- Headers are forwarded to your custom auth webhook
- response returns 200 or 401.
- In case of 200 it has to return `X-Hasura-*` variables to be used in Hasura Permission system

You can read a bit more about it in [docs](https://docs.hasura.io/1.0/graphql/manual/auth/webhook.html)


## Auth using JWT <a name="jwt-root"></a>

JSON Web Tokens is an open standard and Hasura supports it out of the box through a configuration option. You need to pass `HASURA_GRAPHQL_JWT_SECRET` with the following value:

```
{
  "type": "<standard-JWT-algorithms>",
  "key": "<optional-key-as-string>",
  "jwk_url": "<optional-url-to-refresh-jwks>",
  "claims_namespace": "<optional-key-name-in-claims>",
  "claims_format": "json|stringified_json"
}
```
- standard-JWT-algorithms - Hasura supports HS256, HS384, HS512, RS256, RS384, RS512 algorithms  
- `key` - Public key for your JWT encryption. 
- `jwk_url` - provider [JWK](https://tools.ietf.org/html/rfc7517) url. This is used for some providers to expire a key.
- `claims_namespace` - by default Hasura engine looks under "https://hasura.io/jwt/claims" namespace to find `x-hasura-*` prefixed variables encoded in the token. If you pass `claims_namespace` as part of `HASURA_GRAPHQL_JWT_SECRET`, then Hasura engine will look for `x-hasura-` variables under this namespace

For example this:

```json
  "https://hasura.io/jwt/claims": {
    "x-hasura-allowed-roles": ["editor","user", "mod"],
    "x-hasura-default-role": "user",
    "x-hasura-user-id": "1234567890",
    "x-hasura-org-id": "123",
    "x-hasura-custom": "custom-value"
  }
```

If provided `"claims_namespace": "customClaim"` to Hasura engine, Hasura engine will expect that after decoding it needs to search in `customClaim` for all `x-hasura-*` variables

There are other options described in docs how to use JWT, but in a nutshell, it will look like this:

![](https://docs.hasura.io/1.0/_images/jwt-auth1.png)


**Any** Auth server that returns JWT token have to pass JWT with `x-hasura-*` claims under either configured or `https://hasura.io/jwt/claims` namespace. 

JWT will be decoded by the engine following configuration provided in `HASURA_GRAPHQL_JWT_SECRET` and all `x-hasura-*` claims will be forwarded to Permission system. 

Note: `x-hasura-default-role` and `x-hasura-allowed-roles` are mandatory

---

Now let's take a look at the case study of the simplest JWT token use case

Let's go to [https://jwt.io/](https://jwt.io/) and choose algorithm as `RS256`

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


Let's change Payload data to be:

```json
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "iat": 1516239022,
    "myAmazingAuth": {
    "x-hasura-allowed-roles": ["editor","user", "mod"],
    "x-hasura-default-role": "user",
    "x-hasura-user-id": "1234567890",
    "x-hasura-org-id": "123",
    "x-hasura-custom": "custom-value"
  }
}
```

And so our`HASURA_GRAPHQL_JWT_SECRET` will be 

```json
{
  "type":"RS256",
  "claims_namespace": "myAmazingAuth",
  "key": "copypasted public key"
}
```

Now we will head to the console and pass our token as `Authorization` Bearer token. 
Our `x-hasura-*` claims will be extracted from the token and passed to Permissions dialog where you will be able to set roles, and get really granular access even to specific columns. We will talk about Permission system in a bit


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

Let's take a look at common authentication techniques we can use.

### Custom JWT server <a name="custom-jwt-server"></a>

You can run your own custom JWT server which will handle token generation along with Hasura custom claims. There is a really great blog post describing [that](https://blog.hasura.io/add-authentication-and-authorization-to-next-js-8-serverless-apps-using-jwt-and-graphql/)


Server is passport server with jwt and you can check the code [here](https://github.com/hasura/graphql-engine/tree/master/community/boilerplates/auth-servers/passportjs-jwt-roles)


### Auth0 <a name="auth0"></a>

Auth0 uses JWK urls as well as firebase, so there is a cool tool you can use to auto-generate your config for either auth0 or firebase. You can check it [here](https://hasura.io/jwt-config) 

Also for Auth0 you need to configure custom claims in Rules field under Auth0 dashboard

There is a blog post written about Auth0. You can check it [here](https://auth0.com/blog/using-jwts-for-graphql-authorization-with-hasura/)


### Firebase <a name="firebase"></a>

Firebase also uses JWK so `HASURA_GRAPHQL_JWT_SECRET` will look like that 

```json
{"type":"RS512", "jwk_url": "https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com"}
```

We also need to add custom claims to Firebase, so we will be able to include `X-Hasura-*` variables in our encoded token. 

There is a great [blog post](https://blog.hasura.io/authentication-and-authorization-using-hasura-and-firebase/) describing the usage of Firebase for authentication.

### Cognito <a name="cognito"></a>

With AWS Cognito there are several steps you need to do to make it work, so even though I won't dive deeper in how to do that in this particular blog post, More detailed blog post will follow. The main idea of using Cognito is similar to Auth0 or Firebase. You need to define custom claims somewhere. For Cognito, you cannot define that in interface, but you can create custom Lambda when generating your token like so:

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

Our Lambda in this example will be super simple:

```javascript
exports.handler = (event, context, callback) => {
    event.response = {
        "claimsOverrideDetails": {
            "claimsToAddOrOverride": {
                "https://hasura.io/jwt/claims": JSON.stringify({
                    "x-hasura-allowed-roles": ["anonymous","user", "admin"],
                    "x-hasura-default-role": "anonymous",
                    "x-hasura-user-id": event.request.userAttributes.sub,
                    "x-hasura-role": event.request.userAttributes.sub === "18cc0fe3-ad0b-44f8-a622-fd470c7eeb78" ? "admin": "user",
                    "x-hasura-custom": "custom-value"
                })
            }
        }
    }
    callback(null, event)
}
```

That's how we add custom claims. Now you can notice that we pass our claims as stringified JSON. This is done because Cognito does not support nested custom claims. Also, you can see that I am checking for specific user if its and admin or not and if it is I return an admin role. 

On Hasura side when setting `HASURA_GRAPHQL_JWT_SECRET` I need to pass `jwk_url` as well as `claims_format` as stringified JSON

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

And that's basically it. Your Cognito token will be decoded and passed to Hasura permission system

# Hasura Permission System <a name="permissions"></a>

Hasura has a really granular method of evaluating permissions. In sections above, I laid out different methods of authenticating with Hasura and while these methods were different from the other all of them result in the same outcome. `X-Hasura-*` variables are passed to Hasura permission system. 

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

The first layer of permissions is roles. Roles are defined based on `x-hasura-default-role` and `x-hasura-allowed-roles` variables passed to the permission system. 

Second layer is custom checks based on `x-hasura-user-id` or any other custom variable passed to permission system

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


As you can see in this example, we are checking post content to be "custom_value" and only if it is, we enable user to select fields. What it will mean is that user will be able to see posts only when posts content is equal to "custom value"

Alternatively, we can set something like that:

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

And that will mean that user will be able to see only his/her posts.


# Summary

As you can see from the summary above, Hasura supports lots of different Authentication techniques and is aligned with best practices in industry. In addition to that Hasura permissions system gives you a really granular level of access control used, which is a must in production apps.