pact-foundation / pact-mock_service

Provides a mock service for use with Pact
https://pact.io
MIT License
73 stars 69 forks source link

How to handle CORS issues with hard-coded domain #97

Open soundstep opened 5 years ago

soundstep commented 5 years ago

Pasting from an original discussion with @bethesque

I got an issue, not caused by pact itself but more as a conceptual question caused by the way the pact mock service is used.

This page does not cover everything: https://github.com/pact-foundation/pact-mock_service/wiki/Using-the-mock-service-with-CORS

In case you make a request from a browser, the Origin header will be sent, the pact mock service can just return Access-Control-Allow-Origin: * for it to work.

However, if you make request with withCredentials, current browsers will not take a wildcard for answer (https://stackoverflow.com/questions/42803394/cors-credentials-mode-is-include).

That's why most backend framework instead takes the Origin value and send it back as Access-Control-Allow-Origin, for example for it could look like:

withRequest: {
  headers: {
    'Origin': 'http://mydomain.com',
willRespondWith: {
  headers: {
    'Access-Control-Allow-Origin': 'http://mydomain.com',

Unfortunately, doing this will prevent you making request against the pact mock service from let's say, a localhost.

The solution could be to add a matcher and with a wildcard:

willRespondWith: {
    headers: {
        'Access-Control-Allow-Origin': Pact.Matchers.somethingLike('*'),

This will make you able to request from a localhost domain, to the pact mock service, but the only thing you want to make sure is that 'Access-Control-Allow-Origin contains http://mydomain.com so that your app can call your service, and this doesn't do the job.

Question:

How can we validate having a proper url in an Access-Control-Allow-Origin header as we can't make request against the pact-mock-service.

Update:

An executable demo can be found there: https://github.com/soundstep/pact-ruby-standalone-e2e-example/blob/master/DEMO.md

More in-code explanation there: https://github.com/soundstep/pact-ruby-standalone-e2e-example/blob/master/demo/tests/cors-error/test.js#L26

bethesque commented 5 years ago

Have you tried using a pact term? Pact.term(generate: "http://localhost", matcher: /http/). It won't make a strong assertion on the provider side, but at least it should run.

soundstep commented 5 years ago

I can run it this way with a wildcard: https://github.com/soundstep/pact-ruby-standalone-e2e-example/blob/master/demo/tests/cors-success/test.js

But that is far from ensuring that a client from a certain domain would be able to access to an API.

Can you think to anything else we could do, or does that sound like something that should be verified by something else than pact?

If yes, does that mean that CORS would be better removed completely from pact interactions?

So many questions if we can't verify everything with Pact.

I can think to two other things:

Any more thoughts?

bethesque commented 5 years ago

But that is far from ensuring that a client from a certain domain would be able to access to an API.

Is that something that belongs in a contract test? To me, it sounds like something that belongs in the functional test of the provider itself.

So many questions if we can't verify everything with Pact.

Pact can never verify everything. It's just one tool in your toolbox that will give you coverage in specific areas, and you'll need to be aware of the areas that it doesn't cover, and ensure that they are covered adequately elsewhere (eg. performance testing, functional testing, unit testing)

If yes, does that mean that CORS would be better removed completely from pact interactions?

I think what you've done is ok. Having a matcher to say "this header will be there" should be enough. It's the browser's business to populate it, so I wouldn't be too hung up on the value.

soundstep commented 5 years ago

Thanks @bethesque we will probably add functional test for this specifically in our backend services. Cheers.