javaspringgraphqlgraphql-java

GraphQL field level authorization and nullability


We've implemented field level authorization in the type resolvers of our server, where a field is only returned with a value if the user has access to it, otherwise "null" is returned (with information in the extensions). The reason why we are not just returning an error (with an empty data response) is that we have web apps that are used by a variety of users with different access levels, and we don't want (and in some circumstances can't) write queries fitting for every access pattern. Our client has separate logic to deal with authorization, so that the missing information is handeled correctly.

Now we also wanted to add nullability information to our GraphQL schema, but that turned out to be difficult with the above way of handling missing access. If a field technically is never returned null from our data, we would like mark it as non-nullable in the schema as well. But since missing authorization can make the field null while resolving GraphQL, it is not working well in all situations.

I think my only options are:

Has anyone else dealt with field-level authorization, common queries and nullability in GQL and has some other suggestions how to handle this?


Solution

  • Here are some thoughts I hope you find useful.

    Nullability

    Nullability on output types is generally regarded as not the best idea in many cases. I'll quote Production Ready GraphQL here (the book is worth its weight in gold to anyone designing GraphQL schemas, can not recommend it enough):

    So here are a few guidelines that I use for nullability when designing schemas:

    • For arguments, non-null is almost always better to allow for a more predictable and easy to understand API. (If you’re adding an argument, nullable is the best choice to avoid breaking existing clients)
    • Fields that return object types that are backed by database associations, network calls, or anything else that could potentially fail one day should almost always be nullable.
    • Simple scalars on an object that you know will already have been loaded on the parent at execution time are generally safe to make non-null.

    One reason nullability on the output is dangerous is that if a non-null field does end up being null, the whole parent object is nulled too. And if that parent is also non-null... well, you can easily end up bombing your entire object graph for no good reason. This article also has a decent summary of the situation and best practices.

    GraphqlFieldVisibility

    Here's the pseudo-code for this:

    //Do this on each request*
    GraphQLCodeRegistry codeRegistry = schema.getCodeRegistry().transform(c -> c.fieldVisibility(new AuthVisibility(currentUser)));
    GraphQL runtime = GraphQL.newGraphQL(schema.transform(s -> s.codeRegistry(codeRegistry)));
    

    Then implement AuthVisibility using the current user (or whatever else is applicable) to decide whether a field is visible to that user. I e.g. made use of Spring Security for this, and invoked the same logic it normally invokes to decide if the underlying resolver method can be called by the current user. I can probably dig out some of that code should you need it.

    *You can also somehow cache the resulting GraphQL object per user session, but it's probably not needed, as the transformations involved are trivial.