CORS Error in Apigee Developer Portal

Hello Team,

We have created an API Proxy using No-Target and added CORS in Assign Message policy. When testing API Proxy with POST method in developer portal, got a CORS error i.e., Unknown Status

{ "isTrusted": true } .


In Assign Message Policy we have added the following code:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage async="false" continueOnError="false" enabled="true" name="ADD CORS">
<DisplayName>ADD CORS</DisplayName>
<FaultRules/>
<Properties/>
<Remove>
<Headers>
<Header name="Access-Control-Allow-Origin"/>
<Header name="Access-Control-Allow-Headers"/>
<Header name="Access-Control-Max-Age"/>
<Header name="Access-Control-Allow-Methods"/>
<Header name="Access-Control-Allow-Credentials"/>
</Headers>
</Remove>
<Add>
<Headers>
<Header name="Access-Control-Allow-Origin">*</Header>
<Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept,Authorization</Header>
<Header name="Access-Control-Max-Age">3628800</Header>
<Header name="Access-Control-Allow-Methods">GET</Header>
<Header name="Access-Control-Allow-Credentials">true</Header>
</Headers>
</Add>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<AssignTo createNew="false" transport="http" type="response"/>

</AssignMessage>

Thanks in Advance

0 10 2,654
10 REPLIES 10

sidd-harth
Participant V

Hi Lakshmi, can you provide the full error shown in browser developer tools(F12 >> Console)

Make sure your are responding to the OPTIONS request, that happens before any other new request

Try to add "content-type" header to Access-Control-Allow-Headers.

Like this

<Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept, content-type</Header>

It helped me.

In the "Access-Control-Allow-Headers", add "Authorization"

In the response also include POST and other verbs.

<Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>

Also, may want to include the origin and request headers automatically, as you might in a shared flow.

For example to get all the request headers and assign to a variable:

/* globals context, request */
/**
 * This script reads all values of the multi-valued request header Access-Control-Request-Headers
 * and sets them as a CSV in the flow variable accessControlRequestHeaders.
 *      
 * Use this script because there seems to be no direct way to get the
 * values of a multi-valued header directly in out-of-the-box policies
 */ 


// Variable to hold value of flow variable accessControlRequestHeaders.
var accessControlRequestHeadersAsCsv = "";


// Copy all values of the header Access-Control-Request-Headers.
var valueCount = request.headers["Access-Control-Request-Headers"].length();
for(var i = 0; i < valueCount; i++) {
    accessControlRequestHeadersAsCsv += request.headers["Access-Control-Request-Headers"][i];
    if( i+1 !== valueCount ) {
        accessControlRequestHeadersAsCsv += ",";
    }
}


// Set as flow variable.
context.setVariable("accessControlRequestHeaders", accessControlRequestHeadersAsCsv);

Then in your Assign Message to add CORS

<AssignMessage async="false" continueOnError="false" enabled="true" name="AM-add-CORS">
    <DisplayName>AM-add-CORS</DisplayName>
    <Add>
        <Headers>
            <Header name="Access-Control-Allow-Origin">{request.header.origin}</Header>
            <Header name="Access-Control-Allow-Headers">{accessControlRequestHeaders}</Header>
            <!-- Set to a larger value in seconds, this allows for testing  -->
            <Header name="Access-Control-Max-Age">60</Header>
            <Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
            <Header name="X-CORS">true</Header>
        </Headers>
    </Add>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

Here's a cleaner way to get all the values in a multi-valued header, as would be the case for Access-Control-Allow-Headers. It uses the newly released Message Template Functions.

<AssignMessage async="false" continueOnError="false" enabled="true" name="AM-add-CORS">
    <DisplayName>AM-add-CORS</DisplayName>
    <Add>
        <Headers>
            <Header name="Access-Control-Allow-Origin">{request.header.origin}</Header>
            <Header name="Access-Control-Allow-Headers">{substring(request.header.Access-Control-Request-Headers.values,1,-1)}</Header>
            <!-- Set to a larger value in seconds, this allows for testing  -->
            <Header name="Access-Control-Max-Age">60</Header>
            <Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
            <Header name="X-CORS">true</Header>
        </Headers>
    </Add>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

jovaniac
Participant II
hey guys, I implemented something like that and it served me correctly.
In the proxy enpoint we must place in the preflow the next call of a Flowcallout to invoke a sharedflow which will have the policy of CORS

<PreFlow name="PreFlow">
<Request>
<Step>
<Name>FC-CORS</Name>
</Step>
<Step>
<Name>FC-OAuth2</Name>
</Step>
</Request>
<Response/>
</PreFlow>

Definition of flowcallout, where we invoke the sharedflow

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FlowCallout async="false" continueOnError="false" enabled="true" name="FC-CORS">
<DisplayName>FC-CORS</DisplayName>
<FaultRules/>
<Properties/>
<SharedFlowBundle>OPTIONS-CORS-Headers-Response</SharedFlowBundle>
</FlowCallout>

definition of sharedflow

<?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>

definition of the policy of raisefull, where we will indicate the headers of Access-Control-Allow-Origin with * that will allow the invocation from our browser

<?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>

angular:

const httpOptions2= { headers:newHttpHeaders({ 'Authorization':'Bearer token' }) };

obtenerCatalogos():Observable<any> { return this.httpClient.get<any>(uriApigee+'endpointapigee',httpOptions2); }

Regars

Be warned, using RaiseFault with 200 will still be recorded as an error in analytics.

I've created a feature request to have "return immediately" or "interrupt" functionality added to Assign Message to support this use case.

Also, note that you will need to add CORS to any true fault responses.

Have you used CORS in shared flow and can you attach proxy code please. I am facing error

Access to fetch at 'https://mmmmmm.ccccom' from origin 'https://xxx.xxx.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Here's an example Shared Flow that may help.

https://github.com/kurtkanaskie/CORS-Shared-Flow

FYI, a new CORS policy that handles everything is being developed.