Access Control

There are two considerations for incoming sync requests:


When you add a client, you also associate a secret with that client. You can use the default or provide your own and you can update a client secret at any time. By updating a secret, old secrets become invalid.

This secret is used to add an authorization header, generated with HMAC-SHA256. With this header, the server can assert:

For more info about HMAC, see Wikipedia or Ruby’s OpenSSL::HMAC support.

The Authorization header takes the form:

"GraphQL::Pro #{client_name} #{hmac}"

graphql-ruby-client adds this header to outgoing requests by using the provided --client and --secret values.


Incoming operations are validated. If you’re using GraphQL::Pro’s visibility authorization, you must determine whether the current client can see the types and fields which are used in the operation.

You can implement authorization for incoming queries with the authorize(..., operation_store:) option, which accepts a auth strategy class, for example:

authorize(:pundit, operation_store: OperationStoreStrategy)
# Or:
authorize(MyAuthStrategy, operation_store: OperationStoreStrategy)

This strategy class is used only for incoming persisted operations. The strategy class may use ctx[:current_client_name], which is added by the OperationStore.

Here’s an example strategy class which allows "stafftools" apps to use view: :admin fields, but hides those fields from everyone else:

class OperationStoreStrategy
  def initialize(ctx)
    @client_name = ctx[:current_client_name]

  # Only stafftools apps can save queries with `:admin` fields
  # Anyone can save queries with `:public` fields.
  def allowed?(gate, _obj)
    case gate.role
    when :admin
      @client_name == "stafftools"
    when :public
      raise "Unexpected auth role: #{gate.role}"

If you don’t specify a strategy, the default is to fail all view: checks. This way, private fields are not disclosed via OperationStore requests.