tyk
tyk copied to clipboard
bug: CORS options_passthrough on Non OPTIONS requests
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.
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.
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
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.
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.
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.
not stale (ZD ref 11458) added TT-955
Related: https://community.tyk.io/t/cors-settings-of-oidc-401-unauthorized-response/5227