tyk icon indicating copy to clipboard operation
tyk copied to clipboard

bug: CORS options_passthrough on Non OPTIONS requests

Open asoorm opened this issue 6 years ago • 7 comments

When Enable CORS: false and Options Passthrough: true. In the scenario where the gateway is short-circuiting the proxy, for example due to failed auth, the response headers do not contain required CORS headers.

It is expected by Browsers that the SAME CORS headers returned from an OPTIONS request will be returned on a POST request for example.

Steps to Recreate:

Browser sends preflight OPTIONS request to GW, GW transparently reverse proxy preflight request to upstream. CORS headers returned.

$ curl -X OPTIONS https://tyk-gateway.dev:8080/httpbin/post -H 'Origin: abc.com' -k -v
> OPTIONS /httpbin/post HTTP/1.1
> Host: tyk-gateway.dev:8080
> User-Agent: curl/7.54.0
> Accept: */*
> Origin: abc.com
>
< HTTP/1.1 200 OK
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS
< Access-Control-Allow-Origin: abc.com
< Access-Control-Max-Age: 3600
< Allow: POST, OPTIONS
< Connection: close
< Content-Length: 0
< Content-Type: text/html; charset=utf-8
< Date: Fri, 02 Mar 2018 13:15:42 GMT
< Server: meinheld/0.6.1
< Via: 1.1 vegur
< X-Powered-By: Flask
< X-Processed-Time: 0
< X-Ratelimit-Limit: 0
< X-Ratelimit-Remaining: 0
< X-Ratelimit-Reset: 0
<

Browser sends actual request to Gateway. But with invalid credentials - which means 4xx failure.

$  curl -X POST https://tyk-gateway.dev:8080/httpbin/post -H 'Origin: abc.com' -H 'Authorization: Bearer JUNK' -k -v
> POST /httpbin/post HTTP/1.1
> Host: tyk-gateway.dev:8080
> User-Agent: curl/7.54.0
> Accept: */*
> Origin: abc.com
> Authorization: Bearer JUNK
>
< HTTP/1.1 403 Forbidden
< Content-Type: application/json
< Date: Fri, 02 Mar 2018 13:18:32 GMT
< Content-Length: 37
<
{
    "error": "Key not authorized"
}

This is because the gateway has short-circuited the connection and never hits the upstream in order to get the CORS headers.

Possible Workarounds / Thoughts / Considerations:

1. Enable CORS in the gateway as well as options_passthrough.

This will enable, in the event of a non-simple and non-options request for Tyk to return it's own CORS headers.

$ curl -X POST https://tyk-gateway.dev:8080/httpbin/post -H 'Origin: https://abc.com' -H 'Authorization: Bearer JUNK' -k -v
> POST /httpbin/post HTTP/1.1
> Host: tyk-gateway.dev:8080
> User-Agent: curl/7.54.0
> Accept: */*
> Origin: https://abc.com
> Authorization: Bearer JUNK
>
< HTTP/1.1 403 Forbidden
< Access-Control-Allow-Origin: https://abc.com
< Content-Type: application/json
< Vary: Origin
< Date: Fri, 02 Mar 2018 13:23:47 GMT
< Content-Length: 37
<
{
    "error": "Key not authorized"
}

Pro: Simple solution. Con: Need to configure to return exactly same CORS headers as was provided by the initial OPTIONS request, otherwise browser will complain.

2. Given certain conditions, perform OPTIONS request to upstream and merge CORS headers with error response

Pro: Should be correct every time Con: Complexity from code perspective, error handling for CORS request, multiple OPTIONS requests to upstream

3. Cache initial OPTIONS request When an OPTIONS request is performed, it is implied that a follow-up request will be made. For a short period of time, we can cache the headers of the preflight response.

When real request is sent, if short-circuiting, merge CORS response headers from cache with error response.

Pro: Most elegant solution Con: Respecting CORS cache TTL, increased code complexity, possible edge cases.

asoorm avatar Mar 02 '18 13:03 asoorm

I think your second solution (certain conditions create an options request) is the most spec compliant.

IMO, the gateway should act just like any other user agent, that basically means that when options pass through is enabled, it should be constructing pre-flights on behalf of the inbound request and sending those on.

We can either make these conditional (I.e. on errors only), or make them constant, so they run on every request type.

Either way the pre-flight should go from gateway to upstream based on the real request made by the downstream user-agent. This basically means implementing the pre-flight spec into an http client.

When to trigger this client can then be a secondary option.

lonelycode avatar Mar 20 '18 01:03 lonelycode

What about other response codes? they would just be blocked? For instance:

  • When rate limit exceeded
  • When global rate limit exceeded 429
  • When quota exceeded
  • When access denided
  • Access to this API has been disallowed
  • Key has expired, please renew 401

Sorry, haven't thoroughly checked, this is off the top of my head

letzya avatar Mar 20 '18 11:03 letzya

I'm facing the same issue when Tyk block the request and return a forbidden response. No CORS Headers because request never hit the upstream.

Meg4mi avatar Dec 11 '18 15:12 Meg4mi

I also have this issue with rate-limited 429 responses. Regarding solution 1, I recall that with "CORS": {"enable": true, "options_passthrough": true} we got header duplication in the response, which wreaked havoc with the browser.

altaurog avatar Apr 04 '19 12:04 altaurog

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs, please add comments to this ticket if you would like it to stay open. Thank you for your contributions.

stale[bot] avatar Mar 25 '20 19:03 stale[bot]

not stale (ZD ref 11458) added TT-955

chris-fewtrell avatar Nov 02 '20 14:11 chris-fewtrell

Related: https://community.tyk.io/t/cors-settings-of-oidc-401-unauthorized-response/5227

armujahid avatar Feb 17 '22 12:02 armujahid