Jacob Ruiz

View Original

Role-based permissions in GraphQL

Disclaimer: These are just my personal notes to help me articulate concepts I’m learning. They’re not meant to be used as a tutorial.

Today I’m going to walk through how I built a v1 of a role-based permission system in my GraphQL server. This is a bare-bones implementation to demonstrate the concepts, with improvements to come later.

Imagine we want our app to be subscription-based. We will have 2 types of users: “Basic” users and “Premium” users. Premium users will be able to access premium content.

How do we build this in GraphQL?

There are 4 main things that need to happen:

  1. Database: Add role field to User model

  2. Auth Token/Cookie: Include a role property on the user

  3. Mutation: premiumMutation - check that context.me.role === ‘PREMIUM’ before returning premium content

  4. Mutation: updateRole(newRole: String!): User!

Let’s go one by one:

1. Database

Update the User model to include a role:

See this content in the original post

2. Auth Token/Cookie: Include a role property on the user

We have a function called getMe in our index.js file that runs on every request. It does the following:

  • Takes the request as the only argument

  • Looks at req.cookies.token

  • If the token is verified, it returns the user object inside the token

    • We use this user to populate the me property on our context that becomes available in our resolvers

      • We can then do authentication on a resolver-level by simply checking that context.me exists.

I won’t go too much into detail about this mechanism, as this post is just about roles, not token/cookie-based auth. The only important thing to note is that the getMe function needs to be able to access user.role in the token.

To do this, we can update our createToken function that gets used in our login and signup resolvers, to include the role property:

See this content in the original post

So we pass this function a full user object (returned from the database) and it strips out all the sensitive info and just puts some safe and useful properties into the token.

As a result, the token in our auth cookie now contains the role property, which we can verify in jwt.io like this:

Ok so now the role is in our database, in our auth cookie, and in our resolvers (via context.me) now we can check the user role in our resolvers to make decisions about what content to show.

3. Mutation: premiumMutation

Let’s show an example of checking the user role in a mutation:

See this content in the original post

First, it checks if you’re logged in.

Then, it checks if you’re a Premium member.

Then, if those two checks have passed, it returns your premium content.

4. Mutation: updateRole

To test this system, we need a way to update the role on the user.

We can create a new User mutation that takes the newRole as the only argument, and returns a User. (We don’t need a userId argument because we’re getting that from context.me in our resolvers).

See this content in the original post

We’re doing a few key things:

  1. Make sure they’re logged in to update the role

  2. Update the user’s role in the database

  3. Reset the cookie/jwt to reflect the new role

  4. Return the updated user

Possible Improvements:

Those are the basics of setting up a role-based permission system in a GraphQL server. There are some key improvements that I hope to make tomorrow:

  • Role validation

    • We don’t want the client to pass any old string for the role. It should strictly be set to either BASIC or PREMIUM.

    • 2 ways to do this:

      • With enums in GraphQL

      • Sequelize validation rules on the model

  • Return the role to the client from the GraphQL API (not just with the auth token) by resolving the role field on the User type.