How do you put a request body in a Plug.Conn?

I’m writing a Plug which verifies a signature in the header of a request. The signature is the SHA256 HMAC hash of the request body signed with a secret. I’m having trouble testing this because I don’t know how to put the request body in the %Conn{} to run it through the plug. Setting up the headers are easy with put_req_header/3. Is there an equivalent for setting the body?

I’m trying to setup my connection like this in my test:

setup %{conn: conn} do
  body = %{"some" => "message"}
  signature = Plug.Crypto.MessageVerifier.sign(Poison.encode!(body), "s3cret", :sha256)
  conn =
    conn
    |> put_req_header("accept", "application/json")
    |> put_req_header("content-type", "application/json")
    |> put_req_header("x-request-signature", signature)
    |> put_request_body???
  {:ok, conn: conn}
end
1 Like

Would something like this conn = %{conn | body_params: body} work for you?

setup %{conn: conn} do
  body_params = %{"some" => "message"}
  signature =
    body_params
    |> Poison.encode!()
    |> Plug.Crypto.MessageVerifier.sign("s3cret", :sha256)
  conn =
    conn
    |> put_req_header("accept", "application/json")
    |> put_req_header("content-type", "application/json")
    |> put_req_header("x-request-signature", signature)
    |> put_body_params(body_params)
  {:ok, conn: conn}
end

defp put_body_params(conn, body_params) do
  %{conn | body_params: body_params}
end

Or as a workaround you might put your body in conn.private.

1 Like

I can see that working for a test. I think part of my problem is I got off track when Plug.Conn.read_body/2 returned nothing. This isn’t due to setting the body incorrectly, however. It’s part of this long issue where the request body can only be read once - and that happens when Plug.Parsers is called.

https://github.com/phoenixframework/phoenix/issues/459

I will try to resolve read_body/2 and then try your suggestion. Thanks!

This ended up being the solution to my problem: How to: Plug which verifies header signature signed using request body

1 Like

I tried to use this solution to populate a %Plug.Conn{}'s body_params key; however, the conn.body_params was set to %{} after calling conn = post(conn, "/path") in my test. However, when I simply wrote it thus:

post(conn, “/path”, %{“my_params” => “param_val”, …})

Then it worked like a charm–conn.body_params was populated.

1 Like