You are viewing the preview version of this book
Click here for the full version.

Resolver-based access control

Try it yourself

You can find code example for this chapter here.

While directives in the schema are great to define access control in a well-documented way, they have a serious limitation: they are all-or-nothing. A user not in a given Cognito group will be always denied access, for example. But when a user should be able to access a field in some cases but not in others, directives fall short. For example, in our example data model, a user can fetch itself through the Query.user field but not other users.

To implement this, we'll need to write code in the resolvers. This provides a flexible way as the resolver runtime supports arbitrary code. With this approach, it is possible to check the arguments, the source objects, and the caller identity, fetch extra data from a database, and even drop items from the result. We'll see examples for all of these in this chapter.

We'll implement three checks:

  • The Query.user allows returning only the caller user
  • The User.group returns the object only if the caller is in that group
  • The Query.allUsers returns only users in the caller's group

Query.user

Let's start with the easiest one! By default, the Query.user field can fetch any user as long as the caller has access to this field. As we've seen in the Entry points chapter, the first of the two legs of security in GraphQL is what objects are accessible from the Query type. In this case, we want to limit it to the current user.

For this, the resolver needs two pieces of information: the username argument and the name of the current user. The former is available under the ctx.args.username while the latter under the ctx.identity.

The implementation:

There is more, but you've reached the end of this preview
Get a subscription to get access to the full book.