elixirgraphqlabsinthe

Passing information from resolver to post-resolution middleware


Im trying to pass from information from my resolver function to my middleware such that I can set a cookie in the response.

The use-case is I want to generate an Oauth2 authorization link which allows the client to begin an Oauth2 flow with a third party. I want to generate a "state" object which I can set as a cookie in the response.

I've tried calling

   %{resolution | context: state}

within the resolver but unfortunately that doesn't seem to pass that state down.

Here is a simplified example of my resolver function

def get_oauth_link(_parent, _args, resolution) do
    state = random_string(10)

    part_one = "https://github.com/login/oauth/authorize"
    part_two = "?client_id=XXX"
    part_three = "&redirect_uri=http://localhost:3000/login/callback"
    part_four = "&state=" <> state
    part_five = "&scope=user:email"

    url = part_one <> part_two <> part_three <> part_four <> part_five

    # try to add the state to the resolution to 
    %{resolution | context: state}

    {:ok, url}
  end

And then in my schema

    @desc "Get oauth link"
    field :oauthlink non_null(:string) do
      resolve(&Resolvers.OauthResolver.get_oauth_link/3)

      # middleware after resolution to set a cookie with the provided state
      middleware fn resolution, _ ->
        # Here i want to extract the state generated in
        # the resolver and add to the context 
        IO.inspect(resolution, label: "Inside resolution")
      end
    end

Im expecting to be able to set the cookie in the "absinthe_before_send" method as documented here: https://hexdocs.pm/absinthe_plug/Absinthe.Plug.html#module-before-send

Whats the best way of doing this? The approach above seems intuitive to me but the state is not available in the post-resolution middleware.


Solution

  • You cannot change the resolution in a resolver function.

    You can, however, change the resolution in the middleware. That's what middlewares are for. The post resolver middleware resolution will have a value field which contains result from the resolver function (without the :ok or :error atom).

    What you can do is return both state and url from your resolver function, then in your post resolution middleware, extract the state from the resolution value field and reset the resolution value to the url. Like this:

       def get_oauth_link(_parent, _args, resolution) do
        state = random_string(10)
    
        part_one = "https://github.com/login/oauth/authorize"
        part_two = "?client_id=XXX"
        part_three = "&redirect_uri=http://localhost:3000/login/callback"
        part_four = "&state=" <> state
        part_five = "&scope=user:email"
    
        url = part_one <> part_two <> part_three <> part_four <> part_five
    
        {:ok, %{url: url, state: state}} # return state and url
      end
    
    
       field :oauthlink, non_null(:string) do
          resolve(&Resolvers.OauthResolver.get_oauth_link/3)
          middleware fn %{value: %{state: state, url: url}=resolution, _ ->        
            resolution
            |> Map.put(:context, state) # set context using state
            |> Map.put(:value, url) # reset value to url
          end
       end