Modelling teams and user security with Hasura
Tags: hasura, graphql
Published: 2019-04-01 09:25:07

---
title: Modelling teams and user security with Hasura
published: true
description: An overview of how to use the security model of Hasura to define users and teams.
tags: hasura, graphql
---

When first designing the user security for [Lineup Ninja](https://lineup.ninja) I was keen for users to be able to be members of multiple teams (or organisations) from one login. Some of our clients are agencies and work with multiple different clients and need to be able to change between them easily and without remembering loads of different logins.

We recently migrated to [Hasura](https://hasura.io) from [Firebase RTDB](https://firebase.google.com/docs/database/) and this article details how I modelled the security to support this 'multiple team' configuration in Hasura.

The relationships look like this:

![User-<Membership>-Team](https://thepracticaldev.s3.amazonaws.com/i/ubkbcexmixpuxbbh5w98.png)

Which is to say each user can have zero or more membership records. Each membership record belongs to a user and a team. Each team has at least one member.

The membership record details the permissions that member has on the team, it has boolean properties like

* `read_team`
* `write_team`
* `read_event`
* `write_event`

and so on.

At Lineup Ninja we write awesome software to help event planners manage their events. A core component of an event is a 'Session', which could be a presentation a band on stage, or perhaps a breakout discussion. For the user to be able to write to a Session they must have the `write_event` permission for that Session.

Sessions belong to an Event, which belongs to a Team. We can traverse this relationship to authenticate the User.

Here's the ERD through to the session:

![User-<Membership>-Team-<Event-<Session](https://thepracticaldev.s3.amazonaws.com/i/n1ndm57spvubozxwg9b0.png)

When the User makes a request for a Session object it will look like this

```
query  {
  session(where: {id: {_eq:"1234"}}) {
    id
    name
    description
  }
}
```

Additionally the request will be sent with a JWT that contains the User's ID as one of their claims:

```
{
...
  "https://hasura.io/jwt/claims": {
    "x-hasura-allowed-roles": [
      "user"
    ],
    "x-hasura-default-role": "user",
    "x-hasura-user": "bdb04fa3-4de3-4434-8d7f-75b10fe2669a",

  },
...
}
```

In Hasura security is applied per table. You start with creating a role, in this case `user`, then you apply insert, select, update and delete permissions for that role.

The permission for each type of operation consist of 'checks' and the fields you want to expose to that role. If you wish you can expose only a subset of fields to some users, making it easy to store both admin and user facing data in the same table.

The 'check' as a tree of relationships and logic tests. It can traverse the relationships in the schema and ultimately perform a check against the user's ID. You can build up the configuration in the UI, or you can import via migration files.

For the Session example the check looks like this:

```
{
    "event": {
        "team": {
            "memberships": {
                "_and": [
                    {
                        "event_write": {
                            "_eq": true
                        }
                    },
                    {
                        "user": {
                            "id": {
                                "_eq": "x-hasura-user"
                            }
                        }
                    }
                ]
            }
        }
    }
}
``` 

This is the fully denormalised way to perform this check. If the table you are checking is 'further away' from the team then you might want to consider a relationship directly from the table to the Team, like this:

![User-<Membership>-Team-<Event-<Session-Team](https://thepracticaldev.s3.amazonaws.com/i/grdejplrjqp1uwl5y923.png)

This skips traversing the Event table when performing the security check, which should help performance a smidge, more so if you have to traverse many relationships to perform the check.

You'll notice in the rule above we are checking the User's ID by traversing the User relationship then checking the ID. This is unnecessary as the User's ID is a property on the Membership table itself, indeed it is this FK that is used to create the relationship. So we can save a bit of compute by checking the user_id value on the Membership table.

Putting both these changes in place we can update the security rule like so:

```
{
    "team": {
        "memberships": {
            "_and": [
                {
                    "event_write": {
                        "_eq": true
                    }
                },
                {
                    "user_id": {
                        "_eq": "x-hasura-user"
                    }
                }
            ]
        }
    }
}
```

And that's pretty much it!

If you wanted to take things one step further you could extend things to create a 'Role Based Access Control' (RBAC) style pattern. This is a particularly useful structure if you have large teams to manage.

You could implement this by adding a Roles table, defining the permissions of the role, then a Member Roles table, linking each Membership record to the users Roles in that Team (assuming you wanted a User to have multiple Roles). That would look like this:

![User-<Membership>-Team-<Event-<Session-Team Membership-<MemberRoles-Roles](https://thepracticaldev.s3.amazonaws.com/i/4ipbv10joxfp6001hhdp.png)

Then add a relationship from Membership->Roles using the 'Member Roles' joining the table and update the Hasura check like so:

```
{
    "team": {
        "memberships": {
            "_and": [
                {
                    "roles": {
                        "event_write": {
                            "_eq": true
                        }
                    }
                },
                {
                    "user_id": {
                        "_eq": "x-hasura-user"
                    }
                }
            ]
        }
    }
}
```

[One thing I would like Hasura to add](https://github.com/hasura/graphql-engine/issues/1919) is the ability to directly check values in the users token. For example I would like to add the Team id and permission directly to the token like this

```
    "x-hasura-team": "1234",
    "x-hasura-event-write": "true",
```

Then simplify the rule to something like this

```
{
    "_and": [
        {
            "team_id": {
                "_eq": "x-hasura-team"
            }
        },
        {
            "x-hasura-event-write": {
                "_eq": true
            }
        }
    ]
}

```

This currently isn't possible because you can't express the check that `x-hasura-event-write` is `true`, or at least not without adding a column with the value `true` for every row in every table, which I'd obviously like to avoid!

I'd like to see this because, obviously it's more performant as it only needs to check the data in the row it is accessing, and it would have made the migration from Firebase a little easier as this is how I had initially implemented the checks :-)

I hope this brief run through was interesting, let me know if you have any comments!

P.S. I'll be at [GraphQL Asia](https://www.graphql-asia.org) on the 12/13th April. If you're going, get in contact and let's say hi!