Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CORS Headers Options verb #1020

Closed
Primajin opened this issue Mar 3, 2020 · 19 comments · Fixed by #1031
Closed

CORS Headers Options verb #1020

Primajin opened this issue Mar 3, 2020 · 19 comments · Fixed by #1031

Comments

@Primajin
Copy link

Primajin commented Mar 3, 2020

User story.
As a frontend developer, I want to connect to the local mock server, so that I can see mocked responses in my browser.

Is your feature request related to a problem?
I can not connect to the mock server via a secure browser, as the CORS Headers are not set up correctly to allow any origin and any verb (OPTIONS, GET, POST, PATCH, etc.)

Describe the solution you'd like
The stoplight client mock server is sending out widely set CORS headers to allow any origin and any verb - or it allows me to configure it myself.

Additional context
Screenshot 2020-03-03 at 09 55 02

@XVincentX
Copy link
Contributor

@Primajin Would it be possible to share the OpenAPI document you're trying to mock or at least a minimal reproducible example?

@Primajin
Copy link
Author

Primajin commented Mar 3, 2020

Uhh I would need to set something up for this - my hunch was that it was just forgotten to add OPTIONS as part of allowed cors methods.
Commonly options is only used for pre-flight browser requests, but for us we do actual active options calls.

We would need it to be set to

Access-Control-Allow-Methods: DELETE, GET, OPTIONS, PATCH, POST, PUT

So that I can connect to the mock server via browser and test out all the endpoints.
It works fine with Insomnia / Postman as they don't care for CORS 😕

@XVincentX
Copy link
Contributor

That's why I was asking for your document — I am assuming it has an explicit OPTION verb you're handling, right?

@Primajin
Copy link
Author

Primajin commented Mar 3, 2020

Oh I see now it actually does not 🤔🙈 But even if I add one, the mock server does not add that to the allowed methods - it does show up in Routes though

@XVincentX
Copy link
Contributor

That is really weird then, the CORS option are enabled by default in Studio. I'll triple check.

@Primajin
Copy link
Author

Primajin commented Mar 3, 2020

Maybe it's really just that one method missing? The other standard methods like GET seem to work fine.

@XVincentX XVincentX transferred this issue from stoplightio/studio Mar 3, 2020
@XVincentX XVincentX changed the title CORS Headers CORS Headers Options verb Mar 3, 2020
@XVincentX
Copy link
Contributor

I am a little bit confused — how can you effectively perform an OPTIONS request to your API when you have CORS enabled? Wouldn't the CORS handler catch all the requests anyway, preventing your custom handler to be executed?

@Primajin
Copy link
Author

Primajin commented Mar 7, 2020

So normally we request via a library called Axios from the browser to our API. The API has cors headers set in a way that allows our company url + localhost to connect.

When developing new endpoints I wanted to use the stoplight mockserver so that we can already start working on UI implementation while our API developers still work on the real endpoint.

We are using the options call to check what fields the current user can request and then start the normal get requests as anyone does.

But the options call fails against the mock server and the browser shows a CORS error - that's why I thought there might be something missing.

If I use postman or insomnia then this problem doesn't occur because those don't respect the cors headers that are set server side - while a common browser respects those.

@XVincentX
Copy link
Contributor

If I use postman or insomnia then this problem doesn't occur because those don't respect the cors headers that are set server side - while a common browser respects those.

That is indeed what they should do, since it's just that it's a browser-only check and these are two desktop applications. This is not their lack, it's just the way it is.

What is still not clear to me is that how can Prism differentiate between YOUR OPTIONS handler and the one that Prism "auto generates" to handle your CORS?

It would really help me a lot to get a sample OpenAPI Spec to try this out, because the whole thing is still not clear to me.

@Primajin
Copy link
Author

Primajin commented Mar 7, 2020

Alright then let me tinker something for you in the next week 👍

@Primajin
Copy link
Author

Primajin commented Mar 9, 2020

OK here we go:
Open Stoplight Studio App locally with this file:

openapi: 3.0.0
info:
  title: cors-me
  version: '1.0'
  contact:
    name: Jannis Hell
    email: [email protected]
servers:
  - url: 'http://localhost:3000'
paths:
  /test:
    get:
      summary: Your GET endpoint
      tags: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  test:
                    type: string
                    default: GET worked
      operationId: get-get
    parameters: []
    post:
      summary: ''
      operationId: post-test
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  test:
                    type: string
                    default: POST worked
    options:
      summary: ''
      operationId: options-test
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  test:
                    type: string
                    default: OPTIONS worked
components:
  schemas: {}

Then Open a browser on this page: https://codesandbox.io/s/stoplight-test-cors-9kt99

It will do a GET, POST and OPTIONS call against http://127.0.0.1:3100/test where the mock server should be running.
GET and POST work in the browser - OPTIONS not.

Screenshot 2020-03-09 at 09 59 27

Doing an OPTIONS over Insomnia also does not return the body as configured.
Screenshot 2020-03-09 at 10 00 22

Screenshot 2020-03-09 at 10 17 24

Hope this helps

@XVincentX
Copy link
Contributor

Hey,

Ok I've gone through your use case and I now understand what's going on

Yes, effectively we are not allowing the OPTIONS verb in the CORS header, but that is for a reason. Even if we'd allow that, you wouldn't get the desired result.

https://github.com/stoplightio/prism/blob/master/docs/guides/cors.md

Essentially, if you enable CORS Prism will take control of the whole OPTIONS verb no matter what. Once it has processed the CORS request, it will return a 204 and not call the internal handler: https://github.com/stoplightio/prism/blob/master/packages/http-server/src/server.ts#L160-L170

The solution to this might be a little bit complicated, since it would require to check the OPTIONS verb and then still forward the request internally for its processing — but I am not entirely sure this is how all the other web servers behave.

If you want to use such verb I would suggest you to use a proxy before hitting Prism (what are you using for your frontend application? If you're on create-react-app it should be easy: https://create-react-app.dev/docs/proxying-api-requests-in-development/)

I'll close this while I think a little bit more about it, but this might be something we can't solve.

Feel free to keep the discussion here though, I still want to figure out a solution so we can at least update our documentation.

@Primajin
Copy link
Author

Hmm OK, that's sad, how about adding a message / ℹ️ button there somewhere to make clear to the user, that whatever one set's up in studio besides a 204 with an empty body on the options call can not be mocked by the built in server?

My confusion in the beginning came from reading https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS that states that a successfull response can have a body and the response code is not limited to 204 - and I can set it up in stoplight that way, so I had guessed that the mockserver handles it consistently just as any other request.

Yes we are using create react app - so we can proxy that to a different server, but that would mean we can't work together with the API developers via stoplight on OPTIONS calls but would have to wait until they deployed their changes on the options call to an internal test server that can handle it correctly.

@XVincentX
Copy link
Contributor

button there somewhere to make clear to the user, that whatever one set's up in studio besides a 204 with an empty body on the options call can not be mocked by the built in server?

The documentation should explain that already (see cors.md file).

hat states that a successfull response can have a body and the response code is not limited to 204

Ok interesting, it seems that maybe we can melt the two responses together (and the browser would understand that it's both to accept the request and return its response.

I will check it out and see if we can make it happen.

Yes we are using create react app - so we can proxy that to a different server, but that would mean we can't work together with the API developers via stoplight on OPTIONS calls but would have to wait until they deployed their changes on the options call to an internal test server that can handle it correctly.

My idea was that you could in meantime proxy the request from Create-React-App to Prism directly so you would request stuff from the same domain — and so you could disable the cors (--cors=false) and then send the requests there directly.

@XVincentX XVincentX reopened this Mar 10, 2020
@Primajin
Copy link
Author

My idea was that you could in meantime proxy the request from Create-React-App to Prism directly so you would request stuff from the same domain — and so you could disable the cors (--cors=false) and then send the requests there directly.

So for the browser also a different port from localhost to localhost is CORS so I need to fiddle around a bit to see how I can trick it to think it's still the same domain.

@XVincentX
Copy link
Contributor

Hmm I fail to understand the difficulty. If CRA is serving your app on localhost:3000 you can set it up to do localhost:3000/api to localhost:4010 and on that 4010 port there would be Prism responding for you. Am I making sense?

@Primajin
Copy link
Author

Ahh yes of course, sorry my bad. Will try that out

@XVincentX
Copy link
Contributor

@Primajin Can you try out this branch and see if that fixes your use case? I've been reading the spec and apparently there's a reliable way to detect a preflight request.

I've tried with your codesandbox and it seems ok, however I'd like to get back from you first.

@Primajin
Copy link
Author

Lovely this works both in Insomnia and in the Browser ❤️ 🚀

Screenshot 2020-03-11 at 14 42 38
Screenshot 2020-03-11 at 14 42 26
Screenshot 2020-03-11 at 14 39 21

Thank you very much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants