Robert Mosolgo

Authorization in GraphQL

A GraphQL system works differently from a “traditional” RESTful JSON API. Instead of authenticating during controller actions, you can authenticate users with “query context.”

UPDATE 23 Jan 2017: For more resources on authorization with graphql-ruby, see:

Query Context

GraphQL execution systems should allow the consumer to pass some arbitrary data “through” the query, so it is accessible at any time during execution. For example, you could take some information from an HTTP request, pass it into the query, then use that information during field resolution.

You can see this idea at work in graphql-js 0.2.4:

This way, any value that you pass to execute is passed along to any field resolution.

graphql-ruby also implements this idea:

Using Query Context for Authorization

To implement authorization in GraphQL, you could use query context. There are roughly two approaches:

Pass a permission indicator into the query.

Before executing the query, determine the permission level of the current user, then pass that into the query as context. That way, each field can test the permission level to determine how to resolve.

For example, in Ruby:

1
2
3
4
# pass the permission level in the context hash
permission = current_user.permission
query = GraphQL::Query.new(MySchema, query_string, context: {permission: permission})
query.result

Inside a field, you could access context[:permission], for example:

1
2
3
4
5
6
7
8
9
10
11
GraphQL::Field.new do |f|
  # ...
  f.resolve -> (obj, args, context) do
    # Check the permission level which was passed as context
    if context[:permission] == "admin"
      object.secret_info
    else
      nil
    end
  end
end

This allows you to access permission information without abusing the global scope.

Pass the user object into the query.

If your authentication scheme is more complex, you can pass the user object in to the query context.

For example, in Ruby:

1
2
3
# Pass `current_user` in the context hash
query = GraphQL::Query.new(MySchema, query_string, context: {user: current_user})
query.result

That way, fields can access the user object at resolve-time:

1
2
3
4
5
6
7
8
9
10
11
GraphQL::Field.new do |f|
  # ...
  f.resolve -> (object, args, context) {
    # Check the user which was passed as context
    if context[:user].can?(:read, object)
      objects.secret_info
    else
      nil
    end
  }
end

If you pass the user object into query context, you can use fine-grained authentication when resolving fields.