Type Field Authorization in GraphQL Rails with Pundit

This article explains how to authorize a specific field of a graphql type in rails with pundit gem for open-source verision.

Type Field Authorization in GraphQL Rails with Pundit
Type Field Authorization in GraphQL Rails with Pundit

GraphQL-rails has PRO feature of pundit integration which has easy methodology but its a PRO feature and paid.

Still they provide authorized? methods in open-source version but we have to do it manually. You can learn more about field authorization here.

Pundit is a great object oriented authorization for Rails applications. It helps to separate the authorization logic from controllers so that authorization logic can be changed with time according to the current requirements of the system.

Field Authorization

In GraphQL rails, we want some fields to only be shown to some specific users rather than all users. Here we will first authorize the field of type if success then will show the value of the field according to type, otherwise nil.

I assume that pundit gem is installed and initialized in your code along with definitely GraphQL.

Let’s take an example that a user has a post, and only the owner of the post can see the number of views on the post. But as we have one post type in graphql so people other than owner have access to that field. So we will be authorizing the field for the owner of the post only.

# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy

  # only if user is the owner of the post
  def owner?
    user.present? && record.user == user
  end

  class Scope < Scope
    def resolve
      scope.all
    end
  end
end

Now that we defined the post policy with owner role, we define the post type with number of view field authorization.

# app/graphql/types/post_type.rb
module Types
  class PostType < Types::BaseObject
    field :id, ID, null: false
    # .... remaining fields
	field :number_of_views, Integer, null: true, description: 'number of times user has viewed your post' do
      def authorized?(object, args, context)
        Pundit.policy(context[:current_user], object)&.owner?
      end
    end

    # resolvers
    def number_of_views
	  object.views.count
    end
  end
end

authorized? returns true or false. So if authorization fails then nil is shown, otherwise value is shown. So now in future if owner? definition needs to be changed then it can be done easily. Also we can create the same method name of number_of_views in pundit policy to restriction flow completely in pundit policy.


Happy Coding!