0

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.

2 Answers2

2

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
Badu
  • 1,052
  • 8
  • 8
1

I don't think you can change resolution like you are doing.

To be honest, I can't understand why you can't use only the middleware for setting or not it. Why you can't generate only that on the middleware?

However, I can see a way of doing that.

@desc "Return api version"
field :version, :version_payload do
  resolve(fn _, _ -> {:ok, %{version: OnCallAPI.version(), other: "Marcos"}} end)

  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

If you check the console, you sill see the other on the values. Like this:

...
private: %{},
root_value: %{},
schema: OnCallAPIWeb.Schema,
source: %{},
state: :resolved,
value: %{other: "Marcos", version: "0.30.0-rc"}
Marcos Tapajós
  • 546
  • 2
  • 8