Apigee OPTIONS Response for Preflight/CORS

on Monday, February 12, 2018

Apigee comes with the ability to add CORS headers to responses right out of the box. This really isn't that useful though. And, it instills a false sense that it’s actually providing valuable CORS information so the developer doesn't have to think about it.

image

CORS is really implemented into browsers to prevent requests from going to unauthorized endpoints. To do this, many browsers (like Chrome) use a “Preflight” request to pull back a couple of headers which let the browser know that a web service does allow requests from other “origins” (or, DNS names). Essentially CORS headers state:

These websites can use this web service. And, this web service allows these methods (GET, POST, etc) to be called from that website for the resource in question. (With web services, a lot of the time, “These websites” is actually “All websites.”)

Back to Apigee’s initial setup: The problem with adding CORS headers on all responses is that the Preflight request isn’t going to match one of the normal endpoints on an API. So, the response will most likely be a 404 Not Found. And, browsers will consider that an error, and they won’t allow the real request to go through.

This is a big deal for https://editor.swagger.io/. The basic Swagger UI Tester uses fetch, which does the Preflight request/check. The swagger editor and tester are used all over the place and most browsers will try to do a Preflight check which will result in this error message (the image is from Chromes developer tools):

image

In Chrome’s Network tab it will look like this:

image

Take note that the Preflight request is asking the server not only if it’s okay if http://editor.swagger.io is calling, but it wants to know if the ‘ucsb-api-version’ header is acceptable. This means the preflight request doesn’t actually send across any security information. It’s asking if it’s okay to send across security information.

So, if you want to have an Apigee web service that is compatible with the standard Swagger UI editor and tester you need to watch for the OPTIONS preflight request and return an acceptable response. Luckily, this can be done by taking the original CORS headers response and turning it into a PreFlow response. Start out by creating a Shared Flow that looks for OPTIONS requests:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SharedFlow name="default">
    <Step>
        <Name>OPTIONS-CORS-Headers-Response</Name>
        <Condition>request.verb = "OPTIONS"</Condition>
    </Step>
</SharedFlow>

Then add a RaiseFault Policy that will return all the CORS headers and successful status code:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="OPTIONS-CORS-Headers-Response">
    <DisplayName>OPTIONS CORS Headers Response</DisplayName>
    <Properties/>
    <FaultResponse>
        <Set>
            <Headers>
                <Header name="Access-Control-Allow-Origin">*</Header>
                <Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept, ucsb-api-key, ucsb-api-version, authorization</Header>
                <Header name="Access-Control-Max-Age">3628800</Header>
                <Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
            </Headers>
            <Payload contentType="text/plain"/>
            <StatusCode>200</StatusCode>
            <ReasonPhrase>OK</ReasonPhrase>
        </Set>
    </FaultResponse>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>

Now, all you need to do is add the Shared Flow as the very first Step in your API Proxies’ Preflow Proxy Endpoint steps. The Shared Flow step must come before the API Key verification step because the OPTIONS request will not contain security authorization information. It should look something like this:

image

Once this is all setup, preflight requests from https://editor.swagger.io/ will pass without error. And now you should get a successful response:

image

But, this isn’t a perfect solution. There are still faults with it because your API Proxy is now blindly stating that it will take requests from almost anywhere and for a variety of different METHOD types.

The best possible solution would be to allow for OPTIONS Preflight requests to be detected by looking for the OPTIONS method and checking if the 3 required headers exist. If all of those conditions are met, then flow the request down to the resource server and let it determine what the exact CORS response it can serve. But, that’s all configuration for another day.

0 comments:

Post a Comment


Creative Commons License
This site uses Alex Gorbatchev's SyntaxHighlighter, and hosted by herdingcode.com's Jon Galloway.