Skip to content

Authentication

Quail follows a Bring Your Own Auth approach — it never touches authentication. Here’s how to wire up the built-in Rails 8 authentication generator with Quail.

Start by scaffolding Rails authentication:

Terminal window
bin/rails generate authentication
bin/rails db:migrate

Pass the current user into the GraphQL context from your controller:

app/controllers/graphql_controller.rb
class GraphqlController < ApplicationController
include Quail::ControllerHelpers
def execute
result = AppSchema.execute(
params[:query],
variables: normalize_request_params(params[:variables]),
context: {
current_user: Current.user
},
operation_name: params[:operationName]
)
render json: result
rescue StandardError => e
raise e unless Rails.env.development?
handle_error_in_development(e)
end
end

Access context[:current_user] in custom queries and mutations:

app/graphql/queries/my_articles.rb
class MyArticles < Quail::Query
type [:article], null: false
def resolve
context[:current_user].articles
end
end
app/graphql/mutations/publish_article.rb
class PublishArticle < Quail::Mutation
argument :id, ID, required: true
field :article, ArticleResource.graphql_type, null: true
field :errors, [String], null: false
def resolve(id:)
article = context[:current_user].articles.find(id)
article.update!(published_at: Time.current)
{ article: article, errors: [] }
rescue ActiveRecord::RecordNotFound
{ article: nil, errors: ["Article not found"] }
end
end

Authenticate WebSocket connections for subscriptions using the session cookie:

app/channels/application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
private
def find_verified_user
session = request.session
user = User.find_by(id: session[:user_id])
user || reject_unauthorized_connection
end
end
end

Then pass the authenticated user through in your GraphQL channel:

app/channels/graphql_channel.rb
class GraphqlChannel < Quail::Channel
private
def context_for_subscription
{
channel: self,
current_user: connection.current_user
}
end
end

Generate the custom channel with rails generate quail:channel if you haven’t already.

This pattern works with the default Rails 8 session-based auth. If you use Devise, JWT, or another auth library, adapt the wiring to match — the context plumbing is the same.