CORS on error

In the Apigee docs (and via the new auto-cors stuff when creating an apiproxy) the recommendation is to put the AssignMessage for the CORS headers in the Target - Preflow - Response.

In the case of a 4xx error coming from the backend, the Target Repsonse flow gets called followed immediately by an 'error' tick and the AM-Cors step is skipped causing our error responses to not have CORS. What I'm seeing in my searching is that we should be returning CORS headers in every response. But it doesn't seem Apigee feels this way because of the recommendations. What is your opinion?

I'm going to try to fix this by adding a FaultRule to every apiproxy with a DefaultFaultRule set to AlwaysEnforce and have it add the CORS. Wondering if you think this is a good approach.


What do you think about removing CORS from all of my flows and just adding it as a step in Post Client Flow?

Solved Solved
1 5 1,868
1 ACCEPTED SOLUTION

Hi @Michael Glenney,

You're on the right track, you need to "add CORS" on errors too.

For the DefaultFaultRule it goes outside of FaultRules:

<ProxyEndpoint name="default">
    <DefaultFaultRule name="DefaultFaultRule">
        <Step>
            <Name>FC-add-cors</Name>
        </Step>
        <AlwaysEnforce>true</AlwaysEnforce>
    </DefaultFaultRule>
    <FaultRules/>

You may want to consider using a Shared Flow and Flow Hook, so you don't have to repeat in each proxy. You'll still need to deal with the OPTIONS request in your proxies tho.

9612-screen-shot-2020-01-29-at-111008-am.png

Other considerations:

  • Create a SharedFlow and refer to it in all proxies (e.g. FC-add-cors).
  • Create a SharedFlow (e.g. post-proxy) which includes the "add-cors" SharedFlow (and any other Shared Flows) and use it in the FlowHook for the "Post-proxy" location (recommended).
  • Do the same for a pre-proxy FlowHook to capture incoming headers to be set in the response.

Hope that helps.

View solution in original post

5 REPLIES 5

Update. I tried adding a DefaultFaultRule and it doesn't seem to work though I may have the syntax wrong. I couldn't find docs for how to apply the AlwaysEnforce option

    <FaultRules>
        <DefaultFaultRule name="default-fault">
            <Step>
                <Name>add-cors</Name>
            </Step>
            <AlwaysEnforce>true</AlwaysEnforce>
        </DefaultFaultRule>
    </FaultRules>

This was added to the TargetEndpoint XML. I assume that's the right spot since the target is returning the error that is causing this.

Hi @Michael Glenney,

You're on the right track, you need to "add CORS" on errors too.

For the DefaultFaultRule it goes outside of FaultRules:

<ProxyEndpoint name="default">
    <DefaultFaultRule name="DefaultFaultRule">
        <Step>
            <Name>FC-add-cors</Name>
        </Step>
        <AlwaysEnforce>true</AlwaysEnforce>
    </DefaultFaultRule>
    <FaultRules/>

You may want to consider using a Shared Flow and Flow Hook, so you don't have to repeat in each proxy. You'll still need to deal with the OPTIONS request in your proxies tho.

9612-screen-shot-2020-01-29-at-111008-am.png

Other considerations:

  • Create a SharedFlow and refer to it in all proxies (e.g. FC-add-cors).
  • Create a SharedFlow (e.g. post-proxy) which includes the "add-cors" SharedFlow (and any other Shared Flows) and use it in the FlowHook for the "Post-proxy" location (recommended).
  • Do the same for a pre-proxy FlowHook to capture incoming headers to be set in the response.

Hope that helps.

Thank you very much for taking the time to educate me. For now I added it to each step but we will be using the flow hook model ASAP.

It's a little frustrating that the DefaultFaultRule goes at the same level as the FaultRules yet Apigee didn't warn me on save that what I was doing didn't fit the model. Maybe validation is harder than it seems in my head. And technically the docs are right so in the end it's my fault.

I'm also curious to find out why you suggest it be put in the Post-proxy flow hook as opposed to Post-target

EDIT (more questions :))

One issue I thought of before, when considering using flow hooks for this, is that "proper" CORS integration would be to set Allow-Headers and Allow-Methods per endpoint to properly express what is allowed for that API. And I've seen many people recommend to NOT use * for Allow-Origin unless you have to (many apps hitting your API). Not sure how valid all that is because I'm no CORS expert, but wondering your take on that.

I could likely set those fields as variables in each API proxy that needs it specific (with sane defaults in the AssignMessage policy) and still use implement the flow hook so I can see a path forward. But I might just be overthinking it.

You're well on your way to being a CORS expert!

My primary use case for CORS is to allow the Dev Portal to make the `Try It` calls. You could devise a solution that is more precise per each proxy. That might involve using a KVM to look up values per proxy, but you'd have to weigh the merits of doing that.

I use an Assign Message in a Pre-proxy flow hook to get the headers:

<AssignMessage async="false" continueOnError="false" enabled="true" name="AM-cors-save-headers">
    <DisplayName>AM-cors-save-headers</DisplayName>
    <AssignVariable>
        <Name>access_control_request_headers</Name>
        <Template>{substring(request.header.Access-Control-Request-Headers.values,1,-1)}</Template>
    </AssignVariable>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>

Then in my Post-proxy flow to set the headers:

<AssignMessage async="false" continueOnError="false" enabled="true" name="AM-cors-set-headers">
    <DisplayName>AM-cors-set-headers</DisplayName>
    <Set>
        <Headers>
            <Header name="Access-Control-Allow-Origin">*</Header>
            <Header name="Access-Control-Allow-Headers">{access_control_request_headers}</Header>
            <!-- Set to a larger value in seconds, this for testing  -->
            <Header name="Access-Control-Max-Age">60</Header>
            <Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
            <Header name="X-SF-CORS">true</Header>
        </Headers>
    </Set>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

As for why I put it in the Post-proxy flow, it works for all proxies, especially no-target proxies.