GraphQL API authorization management implementation

How authentication, impersonation and delegation have been implemented in our GraphQL API

Arnaud Bezançon
WorkflowGen
5 min readFeb 20, 2017

--

GraphQL implementation can be smooth when it comes to basic “to-do list” features, but the inherent complexity of real-world business scenarios make API design and development much more difficult.

Authorization management is a sensitive subject, especially when graph algorithms are involved

The security context has to stay consistent for all fields and sub-fields defined in a query. The viewer must only see data as authorized according to their access rights. This seems to be an obvious requirement, but with GraphQL, you have to keep in mind that you cannot anticipate all possible queries or how deep they are.

In the following GraphQL query, we retrieve requests and actions related to many different users according to their roles and permissions, but ultimately the viewer can only access the information they are authorized to see.

The challenge of authorization management implementation in GraphQL is to not display unauthorized information to the viewer while also making sure that the viewer has access to all the data they’re allowed to see!

Indeed, multiple resolver functions are called in the above query, and it can be easy to restrict data visibility too much when you have to manage the authorizations of the user identity corresponding to one resolver call and the viewer’s access rights. So you have to fight against security holes and black holes at the same time!

Authentication

Authentication is probably to easiest part of the implementation, since we have decided to manage it outside of GraphQL.

The main reason is to support multiple authentication methods (basic over HTTPS, OAuth 2, JWT, customer-based solutions).

The GraphQL API Node.js application injects the authenticated user identity into the request object, which is used by express-graphql for the context argument in the resolver functions. This means that all of the GraphQL resolvers have access to the authenticated user information and can check the corresponding authorizations.

In our implementation, authorization management is done by our workflow service façade; the resolver just calls the façade to perform the corresponding operation. A null value is returned for each unauthorized data.

This architecture keeps the resolver’s layer as thin as possible, with the business logic located behind the façade.

Impersonation

WorkflowGen supports user impersonation for API usage. This means that an authorized developer account can impersonate another user to perform GraphQL queries and mutations.

In this case as well, impersonation is managed outside GraphQL. We actually consider impersonation to be in the same technical layer as authentication.

Impersonation should thus be transparent to the GraphQL layer.

The request object, exposed in the resolver’s context argument, contains the (real) authenticated user and the impersonated user (by default, we use HTTP request header parameters). If the authenticated user has impersonation rights, the impersonated identity is the GraphQL viewer.

Delegation

You can execute some workflow operations “on behalf of” another user if this user has delegated one or more process roles to you for a specified period of time.

On queries and mutations that support delegation, an onBehalfOf argument can be used to specify the delegator user ID:

List of actions to do by the delegatee (the viewer, in this case) on behalf of the delegator with the user ID “VXNlcjoy”:

When an “onBehalfOf” argument is set, delegation mode is implicit for the sub queries until a new user field is found.

Delegation mode introduces a new level of complexity in authorization management with GraphQL. The function called by a resolver has to manage three identities:
- The viewer
- The current user contextual to the query
- The delegator specified by the onBehalfOf argument

In the above query:
- The viewer is the authenticated user
- “mikelee” is the user contextual to the query user(userName: “mikelee”)
- “VXNlcjoy” is the delegator ID used to display the requests delegated to “mikelee”.
We can rephrase like this: I (the viewer) want to see the requests delegated by “VXNlcjoy” to “mikelee”.

Due to the nature of GraphQL, the resolver functions have to propagate those identities to the sub queries:

The service façade operation takes care of the possible combinations to perform authorization verifications.

Performance impacts

In addition to authorization management itself, we have to manage performance impacts, especially the number of SQL queries generated. This means that the implementation of authorization management has to be done in concert with performance optimization.

Caching is a great solution, but it has to be carefully implemented when all of the information is contextual to viewer authorizations.

In our current implementation, we manage caching only within a user request in order to avoid any security issues, but we’re considering caching some “public” data among multiple requests in the future.

Conclusion

Most of the effort in implementing the GraphQL API is related to security. This is something very important to consider when your solution has a lot of contextual information that depends on user access rights, roles and delegations, for example.

GraphQL opens up a new era in application development and system integration, but it also brings with it new challenges for API developers in terms of security and scalability.

The emerging GraphQL ecosystem will definitely provide tools and solutions in this direction.

--

--

Arnaud Bezançon
WorkflowGen

Full Stack Architect, Creator of WorkflowGen, Advantys co-founder and CTO.