trying to call a proxy that requires CORS & Authentication header & OAuthv2 policy

Not applicable

Hi

I am very new to Apigee. I am creating an Apigee "No Target" proxy that requires passing in the authentication basic token into the header. Then in my Apigee proxy, I have a OAuthv2 policy that uses this basic token to generate an AccessToken. The problem I am having is that when I test the apigee proxy in Postman, it works fine. But when I test apigee proxy in an ajax call, the OAuthV2 fails to generate the AccessToken (seems like the header is not passing through). Is there some existing examples where it address the combinations of CORS and authentication header.

Thanks

Alex

Solved Solved
0 5 3,398
1 ACCEPTED SOLUTION

Just checking - the "Authentication basic token" I guess is HTTP Basic Authentication, something like

Authorization: Basic <BASE64-BLOB-HERE>

Is that right? We don't call that blob thing a token. It may seem to be strange terminology, but it's not a token.

ANYWAY....

To get to the point of your question, When invoking any endpoint from an AJAX call initiated from a browser, the browser will conform to the Same Origin policy. You may want to look it up, but the basic idea is that a Javascript on a page that is served from domain foo.bar can call back to API endpoints hosted on foo.bar, but cannot call to endpoints hosted elsewhere.

There is an exception, and that is CORS - if the endpoint in question (let's say an endpoint hosted at yourorg-test.apigee.net ) emits CORS headers in the response, and handles the CORS preflight request correctly, then the browser will allow JavaScripts to invoke endpoints there.

In short, your API needs to support CORS if you want to invoke it from a webpage. Maybe you already knew this, because you mentioned CORS.

Under CORS , there are several important RESPONSE headers that the API Proxy must emit, in order to tell the browser "you can call me, even if the web page was not served from this domain".

These headers are listed here.

On a POST, the browser will send a "CORS preflight" request which is an OPTIONS call. your API proxy needs to respond to that with the appropriate headers including the Access-Control-Allow-Headers header. That header must contain the name of REQUEST headers allowed by your endpoint. If your AJAX request includes an outbound header named "Authorization" then the Access-Control-Allow-Headers header that is sent back in response to the preflight must include the value "Authorization". Likewise those CORS headers must be applied to all responses sent by AJAX.

It's not difficult to do this with AssignMessage - you can do it in the PostFlow. But you need to be clear on which headers to set and which values to apply to them.

if you just want to disable all CORS restrictions, then you can use this AssignMessage policy in the Response PostFlow of your proxy endpoint:

<AssignMessage name="AM-CORSResponse">
    <Add>
        <Headers>
            <Header name="Access-Control-Allow-Origin">*</Header>
            <Header name="Access-Control-Allow-Headers">*</Header>
            <Header name="Access-Control-Max-Age">3628800</Header>
            <Header name="Access-Control-Allow-Methods">OPTIONS, GET, PUT, POST, DELETE</Header>
        </Headers>
    </Add>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

That says:

"any client from any domain can send me any headers and any verb".

View solution in original post

5 REPLIES 5

Just checking - the "Authentication basic token" I guess is HTTP Basic Authentication, something like

Authorization: Basic <BASE64-BLOB-HERE>

Is that right? We don't call that blob thing a token. It may seem to be strange terminology, but it's not a token.

ANYWAY....

To get to the point of your question, When invoking any endpoint from an AJAX call initiated from a browser, the browser will conform to the Same Origin policy. You may want to look it up, but the basic idea is that a Javascript on a page that is served from domain foo.bar can call back to API endpoints hosted on foo.bar, but cannot call to endpoints hosted elsewhere.

There is an exception, and that is CORS - if the endpoint in question (let's say an endpoint hosted at yourorg-test.apigee.net ) emits CORS headers in the response, and handles the CORS preflight request correctly, then the browser will allow JavaScripts to invoke endpoints there.

In short, your API needs to support CORS if you want to invoke it from a webpage. Maybe you already knew this, because you mentioned CORS.

Under CORS , there are several important RESPONSE headers that the API Proxy must emit, in order to tell the browser "you can call me, even if the web page was not served from this domain".

These headers are listed here.

On a POST, the browser will send a "CORS preflight" request which is an OPTIONS call. your API proxy needs to respond to that with the appropriate headers including the Access-Control-Allow-Headers header. That header must contain the name of REQUEST headers allowed by your endpoint. If your AJAX request includes an outbound header named "Authorization" then the Access-Control-Allow-Headers header that is sent back in response to the preflight must include the value "Authorization". Likewise those CORS headers must be applied to all responses sent by AJAX.

It's not difficult to do this with AssignMessage - you can do it in the PostFlow. But you need to be clear on which headers to set and which values to apply to them.

if you just want to disable all CORS restrictions, then you can use this AssignMessage policy in the Response PostFlow of your proxy endpoint:

<AssignMessage name="AM-CORSResponse">
    <Add>
        <Headers>
            <Header name="Access-Control-Allow-Origin">*</Header>
            <Header name="Access-Control-Allow-Headers">*</Header>
            <Header name="Access-Control-Max-Age">3628800</Header>
            <Header name="Access-Control-Allow-Methods">OPTIONS, GET, PUT, POST, DELETE</Header>
        </Headers>
    </Add>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

That says:

"any client from any domain can send me any headers and any verb".

Hi

Thanks for your answer

Using IE and Chrome produced different outcomes. IE hit the proxy with a GET verb and works fine, but chrome will hit the proxy with an OPTION verb which is what is throwing the error.

My colleague pointed out that for the OPTION verb, should handle the CORS headers and the rest of the flow will continue.

Apigee automatically adds this Assign Message policy to the Response PreFlow of the Target Endpoint. Why do you say to add it to the PostFlow?

Apigee automatically adds this Assign Message policy to the Response PreFlow of the Target Endpoint.

I think your statement is off base.

Apigee adds the policy where you tell Apigee to add the policy. Apigee doesn't make those decisions. You control that.

Why do you say to add it to the PostFlow

The reason I suggested putting the AssignMessage in PostFlow is ... that is the attachment point that runs after all the other policies in the flow. So you can be sure that any headers you add with an AssignMessage policy attached at that point, will remain in the response message.

jovaniac
Participant II

hey guys, I implemented something like that and it served me correctly.

In the proxy endpoint 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:

<FlowCallout name="FC-CORS"> 
  <SharedFlowBundle>OPTIONS-CORS-Headers-Response</SharedFlowBundle> 	
</FlowCallout>

definition of sharedflow

<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

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