⚡️ Pro Feature ⚡️ This feature is bundled with GraphQL-Pro.

Authorization Framework

NOTE: A new Pundit integration and CanCan integration are available. They leverage GraphQL-Ruby’s new built-in auth system and has better support for inheritance and customization. If possible, use those instead!

GraphQL::Pro provides a comprehensive, unified authorization framework for the GraphQL runtime.

Fields and types can be authorized at runtime, rejected during validation, or hidden entirely. Default authorization can be applied at schema-level

GraphQL::Pro integrates out-of-the-box Pundit support and CanCan support and supports custom authorization strategies


To use authorization, specify an authorization strategy in your schema:

MySchema = GraphQL::Schema.define do
  # ...
  authorization :pundit
  # or:
  # authorization :cancan
  # authorization CustomAuthClass

(See below for details on these strategies.)

Then, provide a current_user: in your execution context:

# Authenticate somehow:
current_user = User.find(session[:current_user_id])
# Then pass the user as `current_user:`
result = MySchema.execute(query_string, context: { current_user: current_user })

current_user will be used by the authorization hooks as described below.

Fallback Authorization

You can specify a fallback auth configuration for the entire schema:

MySchema = GraphQL::Schema.define do
  # Always require logged-in users to see anything:
  authorization(..., fallback: { view: :logged_in })

This rule will be applied to fields which don’t have a rule of their own or a rule on their return type.

Current User

You can customize the current_user: context key with authorization(..., current_user: ...):

MySchema = GraphQL::Schema.define do
  # Current user is identified as `ctx[:viewer]`
  authorization :pundit, current_user: :viewer

The authorization will use the specified key to find the current user in ctx.

Runtime Authorization

When a resolve function returns an object or list of objects, you can assert that the current user has permission to access that object. The authorize keyword defines a runtime permission.

You can specify a permission at field-level, for example:

# Only allow access to this `balance` if current user is the owner:
field :balance, AccountBalanceType, authorize: :owner

# This is the same:
field :balance, AccountBalanceType do
  authorize :owner
  # ...

Also, you can specify authentication at type-level, for example:

AccountBalanceType = GraphQL::ObjectType.define do
  name "AccountBalance"
  # Only billing administrators can see
  # objects of this type:
  authorize :billing_administrator
  # ...

Field-level and type-level permissions are additive: both checks must pass for a user to access an object.

Type-level permissions are applied according to an object’s runtime type (unions and interfaces don’t have authorization checks).

If an object doesn’t pass permission checks, it is removed from the response. If the object is part of a list, it is removed from the list. You can override this behavior with the unauthorized_object hook.

Authorize Values by Parent

You can also limit access to fields based on their parent objects with parent_role:. For example, to restrict a student’s GPA to that student:

StudentType = GraphQL::ObjectType.define do
  name "Student"
  field :name, !types.String
  field :gpa, types.Float do
    # only show `Student.gpa` if the
    # student is the viewer:
    authorize parent_role: :current_user

This way, you can serve a subset of fields based on the object being queried.

Unauthorized Object

When an object fails a runtime authorization check, the default behavior is:

You can override this behavior by providing a schema-level unauthorized_object function:

MySchema = GraphQL::Schema.define do
  unauthorized_object ->(obj, ctx) { ... }
# OR
MySchema = GraphQL::Schema.define do

The function is called with two arguments:

Within the function, you can:

Access Authorization

You can prevent access to fields and types from certain users. (They can see them, but if they request them, the request is rejected with an error message.) Use the access: keyword for this feature.

# Non-owners may _see_ these,
# but they may not request them:
field :telephone_number, types.String, access: :owner

AddressType = GraphQL::ObjectType.define do
  name "Address"
  access :owner
  # ...

When a user requests access to an unpermitted field, GraphQL returns an error message. You can customize this error message by providing an unauthorized_fields hook:

MySchema = GraphQL::Schema.define do
  # ...
  unauthorized_fields ->(irep_nodes, ctx) {
    GraphQL::AnalysisError.new("Sorry, you're not allowed to see that!")

The hook should return a GraphQL::AnalysisError. It is called with:

Visibility Authorization

You can hide fields and types from certain users. If they request these types or fields, the error message says that they don’t exist at all.

The view keyword specifies visibility permission:

# These types and fields are
# invisible to non-admins:

# field-level:
field :social_security_number, types.String, view: :admin

# type-level:
PassportApplicationType = GraphQL::ObjectType.define do
  name "PassportApplication"
  view :admin
  # ...


NOTE: A new Pundit integration is available. It leverages GraphQL-Ruby’s new built-in auth system and has better support for inheritance and customization. If possible, use that one instead!

GraphQL::Pro includes built-in support for Pundit:

MySchema = GraphQL::Schema.define do

Now, GraphQL will use your *Policy classes during execution. To find a policy class:

You can also specify a custom policy name. Use the pundit_policy_name: option, for example:

# A pundit policy:
class TotalBalancePolicy
  def initialize(user, obj)
    # ...
  def admin?
    # ...

field :balance, AccountBalanceType, authorize: { role: :admin, pundit_policy_name: "TotalBalancePolicy" }

The permission is defined as a hash with a role: key and pundit_policy_name: key. You can pass a hash for view: and access: too. For parent_role:, you can specify a name with parent_pundit_policy_name:.

For :pundit, methods will be called with an extra ?, so

view: :viewer
# => will call the policy's `#viewer?` method

Policy Namespace

If you put your policies in a namespace, provide that namespace as authorize(..., namespace:), for example:

authorize(:pundit, namespace: Policies)

Now, policies will be looked up by name inside Policies::, for example:

AccountType = GraphQL::ObjectType.define do
  name "Account"
  access :admin # will use Policies::AccountPolicy#admin?
  # ...

Policy Scopes

When a resolve function returns an ActiveRecord::Relation, the policy’s Scope class will be used if it’s available.

See Scoping for details.


NOTE: A new CanCan integration is available. It leverages GraphQL-Ruby’s new built-in auth system and has better support for inheritance and customization. If possible, use that one instead!

GraphQL::Pro includes built-in support for CanCan:

MySchema = GraphQL::Schema.define do

GraphQL will initialize your Ability class at the beginning of the query and pass permissions to the #can? method.

field :phone_number, PhoneNumberType, authorize: :view
# => calls `can?(:view, phone_number)`

For compile-time checks (view and access), the object is always nil.

field :social_security_number, types.String, view: :admin
# => calls `can?(:admin, nil)`


When a resolve function returns an ActiveRecord::Relation, the relation’s accessible_by method will be used to scope the relation.

See Scoping for details.

Custom Ability Class

By default, GraphQL looks for a top-level Ability class. You can specify a different class with the ability_class: option. For example:

MySchema = GraphQL::Schema.define do
  authorization(:cancan, ability_class: Permissions::CustomAbility)

Now, GraphQL will use Permissions::CustomAbility#can? to determine permissions.

Custom Authorization Strategy

You can provide custom authorization logic by providing a class:

MySchema = GraphQL::Schema.define do
  # Custom authorization strategy class:

A custom strategy class must implement #initialize(ctx) and #allowed?(gate, object). Optionally, it may implement #scope(gate, relation). For example:

class MyAuthStrategy
  def initialize(ctx)
    @user = ctx[:custom_user]

  def allowed?(gate, object)
    if object.nil?
      # This is a compile-time check,
      # so no object is available:
      if gate.role == :admin
      # This is a runtime check,
      # so we can use this specific object
      @user.can?(gate.role, object)

  def scope(gate, relation)
    # Filter an ActiveRecord::Relation
    # according to `@user` and `gate`
    # ...

gate is the permission setting which responds to:

object is either:

For list types, each item of the list is authorized individually.


Database query objects (ActiveRecord::Relations and Mongoid::Criterias) get special treatment. They get passed to scope handlers so that they can be filtered at database level (eg, SQL WHERE) instead of Ruby level (eg, .select).

ActiveRecord::Relations can be scoped with SQL by authorization strategies. The Pundit integration uses policy scopes and the CanCan integration uses accessible_by. Custom authorization strategies can implement #scope(gate, relation) to apply scoping to ActiveRecord::Relations.

Mongoid::Criterias are supported in the same way by Pundit policy scopes) and custom strategy’s #scope(gate, relation) methods, but they aren’t supported by CanCan (which doesn’t support Mongoid, as far as I can tell!).