pact-foundation / pact-reference

Reference implementations for the pact specifications
https://pact.io
MIT License
91 stars 46 forks source link

Adding custom header is not able to overwrite existing header #430

Open SiimMardus opened 1 month ago

SiimMardus commented 1 month ago

What happens:

Given a pact with request header authorization: some_token When verifying pact via Pact FFI function And using pactffi_verifier_add_custom_header to replace authorization value with other_token **Then pact verifier makes request with authorization: some_token**

What should happen:

Given a pact with request header authorization: some_token When verifying pact via Pact FFI function And using pactffi_verifier_add_custom_header to replace authorization value with other_token **Then pact verifier makes request with authorization: other_token**


An alleged fix was mentioned in this discussion: https://github.com/pact-foundation/pact-reference/issues/275#issuecomment-1559332072 (cc @rholshausen ) It was difficult to track down the version in which the fix was included, but since:

... I tried 0.4.17, but the headers can still not be overwritten (even tried the current latest version 0.4.20 for good measure, but no luck).


For context, here is where we are attempting to use this feature: https://github.com/salemove/pact_erlang/blob/9945d0bd1ea882812e62db61171bd8671b60b2db/c_src/pactffi_nif.c#L707.

The headers get nicely added when I do something like:

pactffi_verifier_add_custom_header(verifierhandle, "foo", auth_header);
pactffi_verifier_add_custom_header(verifierhandle, "bar", "baz");
...

Additionally, replacing headers added by this function itself works:

pactffi_verifier_add_custom_header(verifierhandle, "foo", "bar");
pactffi_verifier_add_custom_header(verifierhandle, "foo", "baz");
// The outcome - `foo: baz` is included in headers

Let me know if there is any extra context I could provide from my side to track down the problem quicker.

SiimMardus commented 3 weeks ago

I actually found a solid workaround for the problem I was trying to solve with this.

The issue I had was that I wanted to use a real authentication token when verifying the provider. Consumer obviously used a fake token, which was not an issue on its side, but I wanted to replace with with a real token in provider so that the auth flow would be covered as well.

How I solved the problem with existing pact features: Matchers.fromProviderState in combination with state change callback

On the consumer side:


const authToken = "my-fake-token"
const headers = {
  Accept: 'application/json',
  Authorization: fromProviderState(
    `Bearer \${authToken}`,
    regex(/^Bearer \/^[A-Za-z0-9-_]*\.[A-Za-z0-9-_]*\.[A-Za-z0-9-_]*$/, `Bearer ${authToken}`)
  )
}

What this does:

On the Provider side (I am using elixir, but it's quite universal)

def state_change(conn, params) do
  case params do
    %{"state" => "some state"} ->
      # Using a helper function to generate real auth token
      authToken = generateAuthToken()

      conn
      |>put_status(:ok)
      |>json(%{authToken: authToken})
    _ ->
      conn
      |>put_status(:ok)
  end
end

What this does:

Maybe this might help someone trying to find a good solution for authentication in pact verifier as well.