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:
Database: Add
role
field toUser
modelAuth Token/Cookie: Include a
role
property on theuser
Mutation:
premiumMutation
- check thatcontext.me.role === ‘PREMIUM’
before returning premium contentMutation:
updateRole(newRole: String!): User!
Let’s go one by one:
1. Database
Update the User model to include a role:
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 ourcontext
that becomes available in our resolversWe 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:
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:
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).
We’re doing a few key things:
Make sure they’re logged in to update the role
Update the user’s role in the database
Reset the cookie/jwt to reflect the new role
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
orPREMIUM
.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 theUser
type.