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
rolefield toUsermodelAuth Token/Cookie: Include a
roleproperty on theuserMutation:
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
meproperty on ourcontextthat becomes available in our resolversWe can then do authentication on a resolver-level by simply checking that
context.meexists.
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
BASICorPREMIUM.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
rolefield on theUsertype.