Add usable CORS policy in my API proxy when using OAuth 2.0

Not applicable

I add a OAuth Verify Token policy in the sample proxy, weatherxml.

After that, I use POSTMAN to send request to the proxy with bearer tokens in request header and it work well.

However, when I changed to jQuery in my browser, the CORS problems happens, both in my Chrome and Firefox.

My code is like this:

    $.ajax({
        type: "GET",
        url: "http://{my-org}.apigee.net/v1/weatherxml/forecastrss?w=12797282",
        //contentType: "application/x-www-form-urlencoded",
        beforeSend: function(xhr) {
            xhr.withCredentials = true;
            xhr.setRequestHeader('Authorization', 'bearer '+ access_token);
        },
        success: function(data) {
            console.log("SUCCESS IN CHECK POINT 2");
            console.log(data);
        },
        error: function(data) {
            console.log("FAILED IN CHECK POINT 2");
        }
    }).done( function(msg) {
        console.log("CHECK POINT 2 DONE!");
    });

The error in the console in chrome is:

XMLHttpRequest cannot load http://{my-org}.apigee.net/v1/weatherxml/forecastrss?w=12797282. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.

I feel it's wired because after I add a allow CORS policy in my oauth proxy, it indeed returns the access token when I request it using jQuery in my browser.

I've tried to add the policy in every flow in every proxy and target endpoint but my jQuery still doesn't work. I also tried to add header, or change to jsonp in the code, but they are not work.

The CORS policy I added is:

<?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/>
    <Add>
        <Headers>
            <Header name="Access-Control-Allow-Origin">*</Header>
            <Header name="Access-Control-Max-Age">3628800</Header>
            <Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept</Header>
            <Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
        </Headers>
    </Add>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

Could anyone help please?

2 14 6,340
14 REPLIES 14

Not applicable

Zhongli Wu, You said that you added the Add-CORS policy in all the flows but from the error looks like no header is being sent in the response for the resource '/v1/weatherxml/forecastrss'

However you don't need that policy as part of the actual calls but you need that only for OPTIONS (preflight).

Just add the Access-Control-Allow-Origin with allowed domains , '*' in you case for every response on the actual call.

You can check these links for more information on CORS and

http://apigee.com/docs/api-platform/content/how-solve-cross-origin-resource-sharing-issues

BTW you see this issue only with chrome but not firefox ?

The reason why I asked above as I remember seeing something like that few days back and adding Access-Control-Allow-Origin worked on both browsers .

Thanks Maruti!! I'll try it tomorrow. And actually I see this issue both with chrome and firefox, that's why I got confused because in the first time I think it's chrome's CORS restrictions.

Again, thanks!

And Maruti, another questions, how can I add that policy that allow OPTIONS? Just use the xml I used before but add OPTIONS verbs in "Access-Control-Allow-Methods"?

Actually I've tried that and attached policy in request part in the proxy endpoints' preflow as well as the forecast source flow.

But that still does not work. Could you please help agian?

I think you should add Authorization header to list of allowed headers, and instead of * for Hostname use the origin header itself, [i have had issues for * in chrome]

For eg,

<Header name="Access-Control-Allow-Origin">{request.header.origin}</Header>
<Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept, authorization</Header>

+, make sure CORS preflight is handled like Maruthi mentioned,

It usually simple, for the OPTIONS call, just apply the same policy Add-CORS

Thanks,

Thanks! But when I replace {request.header.origin} with null, it still do not work. 😞

Actually I'm not so sure where to add the policy. From Apigee's doc, it should be in the response part in target endpoints' preflow. From the video Maruthi provided, it should be in the request part in the Proxy endpoints' preflow.

Could you please help again? Thanks!

it should defnitely be in response flow, its usually pretty simple - can you post your proxy here?

Not applicable

@Zhongli Wu,

To keep things simple, listen for request.verb OPTIONS and respond with your Add-CORS policy on your resource

// change Add-CORS to have below

<Header name="Access-Control-Allow-Origin">{request.header.origin}</Header>
<Header name="Access-Control-Allow-Headers">{reqHeaders}</Header> 
// for now to see if end-end flow works .

once the above is done , you should see your browser making a call like below

GET /cors HTTP/1.1
Origin: <a href="http://api.bob.com">http://api.bob.com</a>
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

and you should see proper 200 Ok with all the Access-Control headers .

Once that is done , for your actual call make sure you add the below header for all success and error responses .

<Header name="Access-Control-Allow-Origin">{request.header.origin}</Header>

If it works you can extend your functionality and if it doesn't pls post the req/resp that you see on your browser .

pls refer this for more info http://www.html5rocks.com/en/tutorials/cors/

Not applicable

Thanks! @Maruti Chand and @mukundha@apigee.com

I've attached my policy in the attachment in this post. I added the policy in two places: the preflow in the proxy endpoint and the response flow in forecast resource.

Another interesting thing I found is:

When I delete the OAuth 2.0 policy in my forecast resource, and delete the header in my ajax request, the xml document is returned successfully. However, when I add the beforesend part in my ajax request (without the OAuth 2.0 policy part), the same problem happened.

var granter = function() {
    $.ajax({
        type: "GET",
        url: "http://{my-org}.apigee.net/v1/weatherxml/forecastrss?w=12797282",
        //contentType: "application/x-www-form-urlencoded",
        /**beforeSend: function(xhr) {
            xhr.withCredentials = true;
            xhr.setRequestHeader('Authorization', 'bearer '+ access_token);
        },*/
        success: function(data) {
            console.log("SUCCESS IN CHECK POINT 2");
            console.log(data);
        },
        error: function(data) {
            console.log("FAILED IN CHECK POINT 2");
        }
    }).done( function(msg) {
        console.log("CHECK POINT 2 DONE!");
    });
};

The last thing need to mention is:

when I saw network part in chrome, the response header of OPTIONS is:

Age:
0
Cache-Control:
max-age=200, public
Connection:
keep-alive
Content-Encoding:
gzip
Content-Type:
text/xml;charset=UTF-8
Date:
Tue, 21 Apr 2015 18:11:31 GMT
P3P:
policyref="http://info.yahoo.com/w3c/p3p.xml", CP="CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV"
Server:
ATS
Transfer-Encoding:
chunked
Vary:
Accept-Encoding
Via:
http/1.1 ats24.weather.bf1.yahoo.com (ApacheTrafficServer/5.0.1 [c sSf ]), http/1.1 r28.ycpi.dcb.yahoo.net (ApacheTrafficServer [c sSf ]) 

The request header is:

Accept:
*/*
Accept-Encoding:
gzip, deflate, sdch
Accept-Language:
en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4
Access-Control-Request-Headers:
accept, authorization
Access-Control-Request-Method:
GET
Connection:
keep-alive
Host:
{myorg}.apigee.net
Origin:
null
User-Agent:
Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36 

I added the CORS policy in proxy endpoints' preflow as well as the response flow of forecast recourse. But in my chrome, it still returns :

XMLHttpRequest cannot load <a href="http://{my-org}.apigee.net/v1/weatherxml/forecastrss?w=12797282.">http://laurenceusc-test.apigee.net/v1/weatherxml/forecastrss?w=12797282.</a> No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
script.js:39 FAILED IN CHECK POINT 2

We might have the same problem @Barahalikar Siddharth, right?

If anyone could help, please post here! I really appreciate you help!!

@Zhongli Wu Unable to import your bundle for some reason . Can you follow the steps that I mentioned here .

http://community.apigee.com/answers/3228/view.html

weatherxml-rev2-2015-04-23.zip

Maruti, Thanks! I've modified my proxy and attached it (I tried to import using apigeetool on nodejs, and it succeeded). If you still could not import it, please contact me via olivier_bbs@outlook.com immediately! Thanks for help!

@Zhongli Wu

Sure thanks , I will check that . Meanwhile can you follow the steps here http://community.apigee.com/answers/3228/view.html and see if that helps you ?

Hi Maruti, Thanks! I checked it and edit my policy. An interesting thing happened: the result for weatherxml comes back in options request (that means returned by brower's automatic preflight request). In the meantime it still blocks at /forecast resource.

Do you have any ideas about this?

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>

Regars