Saturday, September 9, 2017

Lists and pagination authorization in GraphQL business layer

Leave a Comment

In Dan Schafer's excellent "GraphQL at Facebook" talk from React Europe he goes over how centralizing authorization in business layer models avoids the problem of having to duplicate authorization logic for every edge that leads to an authorized node.

Three layer

This works fine for something like Todo.getById(1) which in my case eventually ends up querying a database for SELECT * from todos WHERE id=1 and then verifying authorization with checkCanSee(resultFromDatabase).

However, let's say my todos table now contains 100,000 todos from multiple users, performing authorization purely in the business layer becomes impractical as I'd need to fetch every todo, filter the result using the shared authorization logic and then slicing that to perform pagination.

Am I wrong thinking that the only way to solve this is by letting authorization logic reside in the persistence layer itself?

2 Answers

Answers 1

I think one of the takeaways from Dan’s talk is the difference in how authorization is handled with GraphQL, as opposed to a typical REST endpoint.

In REST, each resource is typically associated with a single endpoint. When a request is made to that endpoint, it makes sense to check whether the requestor is authorized before processing the request. With GraphQL we may be fetching multiple resources within the same request, so this behavior is no longer desirable. As Dan puts it:

We don’t want to completely blow up the request if you can’t see one of [the requested resources].

So the preferred approach with GraphQL is to implement some kind of per-node mechanism for authorization, and to only return the resources the requester is authorized to see. And that is exactly what the example in the talk shows – one way of doing that.

If you store your to-dos in a SQL database table, it would make perfect sense for your code to just make a query like SELECT * from todos WHERE creator_id=${viewer.id} and omit using a function like checkCanSee altogether.

Similarly, you can bake pagination right into your query with limit-offset, cursors, etc. And yes, since you’re now letting your DB do the heavy lifting, you could say that we’ve moved into the persistence layer. However, it’s still up to your business logic to take the request, sanitize the inputs, construct an appropriate query and return the results in a form GraphQL can use.

I can’t speak for Dan, but I imagine his intent was not to suggest this was the only (or even optimal) way to implement authorization for a node. I think the bigger point is that if you are, for example, fetching:

{   header   todos  {     description   }   quoteOfTheDay } 

even an unauthorized client should still get a response back from the server that it can then use to render a page for the end-user (even if that response includes an empty array of to-dos).

Answers 2

You can query based on authorization results. In your Todo example:

  1. Ask the authorization server whose todos you're allowed to see,
  2. SELECT * FROM todos WHERE owner IN [<permitted owners]]
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment