GraphQL/ Rails 422 Unprocessable Entity – Saving token to Session

GraphQL/ Rails 422 Unprocessable Entity – Saving token to Session

I’m currently working on changing the rails backend of a project from REST to graphql and I’m running into an error with authentication following their tutorial – https://www.howtographql.com/graphql-ruby/4-authentication/
I’m using the GraphiQL engine to test all my requests and receiving error status 422 unprocessable Entity, User Must Exist
Which makes sense because the mutation I am executing is to create a new color – which has a belongs_to relationship to User.
About halfway through the page linked above ^^^ it says this:

With the token that the signinUser mutation provides, apps can
authenticate subsequent requests. There are a couple of ways this can
be done. In this tutorial, we are just going to use the built-in
session, since this doesn’t add any requirements to the client application. The GraphQL server should be able to get the token from
the session header on each request, detect what user it relates to,
and pass this information down to the resolvers.

I’m able to successfully return an auth token through the signinUser method like the docs show previously on the same page – the method that it posts to also saves the token to this supposed session in this method here (also from the same link posted above ^^^) :
def call(_obj, args, ctx)
input = args[:email]

return unless input

user = User.find_by email: input[:email]

return unless user
return unless user.authenticate(input[:password_digest])

crypt = ActiveSupport::MessageEncryptor.new(ENV[“SECRET_BASE_KEY”])

token = crypt.encrypt_and_sign(“user-id:#{ user.id }”)

puts “please **********************************”
p ctx[:session]

ctx[:session][:token] = token

puts “please **********************************”
p ctx[:session]

OpenStruct.new({
user: user,
token: token
})
end

Related:  What does the “operation name” reference?

You’ll be able to see in my desperate struggle that I p’d out the session right before the method returns and not surprisingly saw that it contained the token for that users sign in.
However, when I proceeded to execute the mutation to create a color, my expectation was that the session would still contain that token and I’d be able to commit that color successfully. That was not the case and when I p’d out the session for this request, it return an empty hash.
I cant find any information about how the built in graphql session works – and I’m brand new to graphql in general.

My main questions would be – is the graphql session supposed to be caching token information? Why is it that the information is not carrying over to requests following signinUser ? Should I even bother with trying to use the auth in this tutorial since the docs claim that this authentication method is not a long term solution?

I know this is a lot but would really appreciate an extra brain on this.
Thanks in advance!
PS. I understand the tutorial uses links and I am using colors here – that is intentional and I have done my best to make sure that semantic differences were not causing any errors.

Rails version – 5.2.2 (using api only)
graphql – 1.7.4
graphiql rails – 1.4.4

Solutions/Answers:

Solution 1:

Same as REST APIs GraphQL does not store any information between two subsequent requests, You have to pass authentication token returned in sign in mutation to all subsequent requests where you want current user-related information.

Related:  In Nuxt, should I use asyncData or default Apollo queries?

You should do something like below in graphql_controller.rb

class GraphqlController < ApplicationController
  def execute
    variables = ensure_hash(params[:variables])
    query = params[:query]
    operation_name = params[:operationName]
    context = {
      current_user: current_user
    }
    result = GraphqlTutorialSchema.execute(query, variables: variables, context: context, operation_name: operation_name)

    render json: result
  end

  private

  # set current user here
  def current_user
    # you can token here
    token = request.headers['Authorization']
    return nil unless token
    # find current user from this token
  end

  # Handle form data, JSON body, or a blank value
  def ensure_hash(ambiguous_param)
    # ...code
  end
end

Solution 2:

A colleague of mine pointed out that “session” is part of rails and should create a cookie that would be accessible from the next request.

I mentioned that I was using Rails version – 5.2.2 (using api only) – well when you use the -api flag when initializing a new rails project, it adds these lines to application.rb

# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.

# Only loads a smaller set of middleware suitable for API only apps.
# Middleware like session, flash, cookies can be added back manually.
# Skip views, helpers and assets when generating a new resource.
config.api_only = true

Notice this line in particular – Middleware like session, flash, cookies can be added back manually.


I commented out config.api_only = true and this added cookies back to the application/ allowed me to make the next request with an existing user.

You can also add these lines I found from “Lysender” on his post – Rails 5 – API Only – Enable Cookies and Sessions if you’d prefer not to remove the api-only feature.

config.api_only = true
config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore, key: '_coookie_name', expire_after: 30.days

Related:  Use enum as query argument in Graphql

References