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:
Query.user
allows returning only the caller userUser.group
returns the object only if the caller is in that groupQuery.allUsers
returns only users in the caller's groupLet'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: