DEV Community

Auth By Example
Auth By Example

Posted on

GraphQL resolvers often check the top-level object and skip the nested ones

A common GraphQL setup puts the permission check in the root resolver. project(id: 7) checks that you're a member of project 7 and returns it. Every nested field resolves on its own after that.

So a query like project(id: 7) { linkedProjects { name members { email } } } can walk right out of the project you were allowed to see. A linked project might belong to another team, and its members resolver never asked whether you can see that team.

What fixes most of it:

  • Put the check in each type's resolver, or in the data loader that fetches that type, keyed on the object being returned.
  • Treat any field that returns another object as a new authorization question, even when the parent passed.

To test it, sign in as a user who belongs to one project and write a query that follows every relation two or three levels deep. Any object from outside their projects that comes back is a hole. Introspection gives you the full list of relations to try.

Top comments (2)

Collapse
 
williamsj04 profile image
Jessica Williams •

Good point about nested resolvers. Putting the check in the data loader is a nice touch because every path to that type then goes through the same rule. A test that walks the relations from the schema automatically would also help catch new fields that slip in without a check.

Collapse
 
authbyexample1 profile image
Auth By Example •

Yes, the schema walk is a good idea. You could fail the build when a field returns an object type and its resolver or loader has no check registered, so a new relation can't ship unguarded. You'd still want a few hand-written tests for the tricky cases, like a field that's visible to the owner but not to other members of the same workspace.