Exploring the Future of Serverless with Sheen Brisals: Why It's Becoming the New Norm
Since its public release in 2015 by Facebook, GraphQL has gained a lot of popularity among developers due to reasons like inbuilt types, reduced payloads, and roundtrips (making the apps faster), easy mocking capabilities, better developer experience, auto-generating documentation. Also, a vast ecosystem of open source libraries and tools has grown around it. All this together makes it often a better choice over REST APIs.
Due to this, a lot of products and companies have started providing GraphQL endpoints to interact with their services. Github’s public GraphQL API service is one of them. One can even build an entire product leveraging these services - Hasura, or Appsync for example. Sometimes though it’s the case that the product or service isn’t built explicitly for building multi-user multi-role apps on top of it - leaving the challenge to the developer using it.
Motivation:
We were developing apps on top of a distributed ledger service, Vendia, and faced similar challenges (Though they have solved this now.).
In our case, We had 3 levels of fine-grained access control layers, “Super admin”, “Admin” and the “Default User”, Each had its permissions/privileges. Whenever a user logs into our application we want the user to retrieve a role. Based on that role the user is assigned a set of permissions that allows this user to access the specific parts of our API. Similarly, if a user has authenticated successfully but does not have any roles attached to them, then we want to provide that user with a Default role, which will have a very basic set of permissions attached to it.
RBAC:
Role-based access control (RBAC) is a method of restricting system access based on the roles of individual authorized users. Using RBAC, you can manage who has access to specific resources and the actions a user can perform within that resource. Wikipedia Link.
Vendia:
Vendia is a platform that lets organizations or enterprises share data and code across clouds, regions, and technology stacks. It lets you create Uni’s or universal applications. Each Uni will have a bunch of nodes that can interact with the Uni, and the uni can also decide which node has access to what data and what it can do to it. A good analogy will be thinking of Uni’s as a global database that is shared by different organizations, and nodes as different organizations sharing the data. Since the whole service revolved around nodes and not users of that node, there wasn’t any way to directly build a user app on top of it where different users of the same node (organization) have different roles.
Solution:
The solution is to add some kind of a proxy middle layer that checks if the user has enough permissions to read or modify a resource and if it’s the case forward the request to the corresponding node. With normal REST API’s this is straightforward since the URL and params have enough data to check if the user has permissions. But with GraphQL the problem gets a bit more complex since it’s a whole querying language in itself, and quite a bit more complex than parsing URL params.
One way to combat this is to create a middle REST layer that maps the user request, which will be REST, to the GraphQL request for the node. But it is preposterous since all GraphQL goodness is lost in the process.
To solve this problem we created a javascript library that exposes the GraphQL request to the developer as a request object, which then can be used to decide if the requesting user has enough permissions, and then forward the request to the data node.
Let’s say the user is requesting a document that belongs to a particular organization with the following query
query {
document(id: "1234", organization: "XYZ") {
result
}
}
The library parses that query and adds an object as given below to the request object.
Building an RBAC middleware for Graphql Services{
"type":"query",
"queryObjects":[
{
"operationName":"document",
"variables":{"id":"1234", organization: "XYZ"},
"selectedFields":{"result":1}
}
]
}
Now in the request, the code can check if the user belongs to that particular organisation - “XYZ” here and if the user has enough permissions to read that document from that object and then either forward or reject that request.
Conclusion:
You can dive into the example here. The library does not support all the use cases as of now. Please free to add your own patterns and also PRs are welcome.