preflight OPTIONS Error with NodeJs endpoint

We have an endpoint in NodeJs application. Before calling endpoint we are authenticating user with OKTA.

The request from source is failing with CORS error on OPTION request.

We are using introspect call in okta to validate access token. Below are the policies we are using

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ServiceCallout async="false" continueOnError="true" enabled="true" name="SC-introspectToken">
    <DisplayName>SC-introspectToken</DisplayName>
    <Properties/>
    <Request clearPayload="true" variable="okta.request">
        <Set>
            <Headers>
                <Header name="content-type">application/x-www-form-urlencoded</Header>
                <Header name="cache-control">no-cache</Header>
                <Header name="accept">application/json</Header>
            </Headers>
            <FormParams>
                <FormParam name="token">{token}</FormParam>
                <FormParam name="token_type_hint">access_token</FormParam>
            </FormParams>
            <QueryParams>
                <QueryParam name="client_id">{client_id}</QueryParam>
            </QueryParams>
        </Set>
        <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    </Request>
    <Response>okta.introspection.response</Response>
    <HTTPTargetConnection>
        <Properties>
            <Property name="use.proxy">true</Property>
        </Properties>
        <!-- <URL>https://dev-79724599.okta.com/oauth2/default/v1/introspect</URL>-->
        <URL>https://mmc.oktapreview.com/oauth2/default/v1/introspect</URL>
    </HTTPTargetConnection>
</ServiceCallout>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ExtractVariables async="false" continueOnError="false" enabled="true" name="EV-checkOktaResponse">
    <DisplayName>EV-checkOktaResponse</DisplayName>
    <Properties/>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <VariablePrefix>extracted</VariablePrefix>
    <JSONPayload>
        <Variable name="isActiveToken">
            <JSONPath>active</JSONPath>
        </Variable>
        <Variable name="username">
            <JSONPath>$.username</JSONPath>
        </Variable>
    </JSONPayload>
    <Source clearPayload="false">okta.introspection.response</Source>
</ExtractVariables>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ExtractVariables continueOnError="false" enabled="true" name="EV-from-Variable">
    <DisplayName>EV-from-Variable</DisplayName>
    <VariablePrefix>extracted</VariablePrefix>
    <Variable name="extracted.username">
        <Pattern>{emplid}@**</Pattern>
    </Variable>
</ExtractVariables>
    <HTTPTargetConnection>
        <Properties/>
        <URL>https://abc-dev.apigateway.abc.com/worker/{extracted.emplid}</URL>
    </HTTPTargetConnection>

I have added CORS at target pre-flow response

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage async="false" continueOnError="false" enabled="true" name="add-cors">
    <DisplayName>AM-AddCors</DisplayName>
    <FaultRules/>
    <Properties/>
    <Add>
        <Headers>
            <Header name="Access-Control-Allow-Origin">{request.header.origin}</Header>
            <Header name="Access-Control-Allow-Headers">Authorization, origin, x-requested-with, accept, Content-Type</Header>
            <Header name="Access-Control-Max-Age">3628800</Header>
            <Header name="Access-Control-Allow-Methods">GET,PUT,POST,DELETE,HEAD,OPTIONS</Header>
        </Headers>
    </Add>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

This call is failing in on OPTION request with CORS header 'Access-Control-Allow-Origin' missing.

Do we need to take care of CORS in nodejs application or at apigee. If we need to do it in apigee what needs to be done. We have already added below conditions in the below calls.

	     <Step>
                <Name>SC-introspectToken</Name>
                <Condition>request.verb != "OPTIONS"</Condition>
            </Step>
            <Step>
                <Name>EV-checkOktaResponse</Name>
                <Condition>request.verb != "OPTIONS"</Condition>
            </Step>
            <Step>
                <Name>EV-from-Variable</Name>
                <Condition>request.verb != "OPTIONS"</Condition>
            </Step>
0 3 172
3 REPLIES 3

I presume you have a web client, connecting through Apigee, which then proxies to the node.js-based application running somewhere else.

In that case, YES, you need to handle the OPTIONS request from within Apigee.

The web browser is sending the OPTIONS request as a pre-flight request, to inquire with the server (Apigee), "should I allow the web app running in me, to send this request?" And the server (in this case Apigee) needs to affirmatively respond to that request to tell the browser "yes, allow that request."

A more detailed explanation of this is available here in a recent screencast I recorded.

Apigee is introducing a new CORS policy to make it easier to add CORS "awareness" to your API proxy. This policy is already available in Apigee X, and if it is not in hybrid, it will be there soon.

If you are using the older Apigee Edge, then you need to "manually" handle the CORS requirements, by

  • explicitly handling the CORS preflight request (the OPTIONS call) in your API proxy
  • append CORS headers to other calls (so called "simple requests")

This means you need to attach something like your "AM-addCORS" policy to various places in your API Proxy, including in the DefaultFaultRule.

The documentation from Apigee describes how to do this. It is MUCH easier with the builtin CORS policy, as my screencast explains.

Hi Dino,

We have done as you suggested in our proxy and now the CORS error is not occurring.

However, we are now getting Status 304 code in the response.

Initially we get 200 status, but calling the endpoint repeatedly we get status 304.

Is this something we can take care in apigee . We tried setting below property in HTTPTargetConnection but it did not work.

	<Properties>
            <Property name="success.codes">1xx,2xx,3xx,404,500</Property>
        </Properties>

Our endpoint is in nodejs application, so do we need to handle it in nodejs using below code or is there a way to handle it in apigee ?

var express = require('express');
app = express();
app.use(function(req, res, next) {
  req.headers['if-none-match'] = 'no-match-for-this';
  next();    
});

ok I understand that you have solved the CORS error.

And now you are getting a Status 304... where? Where do you see the 304?

a 304 response means "unchanged". The server is telling the client, "the response data you have from a previous request is unchanged; you can use your cached response." From that web page:

In other words, there is no need for the server to transfer a representation of the target resource because the request indicates that the client, which made the request conditional, already has a valid representation; the server is therefore redirecting the client to make use of that stored representation as if it were the payload of a 200 OK response.

Do you consider it to be an error if your endpoint returns a 304? Usually it's not an error.

If in your case you do not want to allow caching, you will need to explicitly disable caching in one or more of the layers. For example, in the client (the approach varies depending on the client framework), or with a Cache-Control header in the original response.