Default Fault Rule not adding CORS headers in Raise Fault

Hi Team,

I am implementing CORS policy . I have added in CORS policy in pre flow and target pre flow and Options Preflight flow .

The issue I am getting whenever Default Fault Rule is called it is not adding CORS headers in response 

CORS policy 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CORS continueOnError="false" enabled="true" name="CORS">
<DisplayName>CORS</DisplayName>
<AllowOrigins>{request.header.origin}</AllowOrigins>
<AllowMethods>GET, PUT, POST, DELETE</AllowMethods>
<AllowHeaders>authorization, origin, x-requested-with, accept, content-type, referer</AllowHeaders>
<ExposeHeaders>*</ExposeHeaders>
<MaxAge>3628800</MaxAge>
<AllowCredentials>false</AllowCredentials>
<GeneratePreflightResponse>true</GeneratePreflightResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</CORS>

 

RF-CommonError

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="RF-CommonError">
<FaultResponse>
<Set>
<Payload contentType="application/json">
{
"errorCode": "{flow.error.code}",
"errorType": "{flow.error.type}",
"errorMessage": "{flow.error.message}"
}
</Payload>
<StatusCode>{flow.error.status}</StatusCode>
<ReasonPhrase>{flow.error.reason}</ReasonPhrase>
</Set>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<!--<Copy source="request">-->
<!-- <Headers/>-->
<!--</Copy>-->
</FaultResponse>
</RaiseFault>

 

<DefaultFaultRule name="fault-rule">
<AlwaysEnforce>true</AlwaysEnforce>
<Step>
<Name>CORS</Name>
</Step>
<Step>
<Name>AM-500InternalServerError</Name>
</Step>
<Step>
 500InternalServerError
<Name>RF-CommonError</Name>
<Condition>(fault.name != "RaiseFault")</Condition>
</Step>
</DefaultFaultRule>

 

AM-500InternalServerError this policy is to set default error message 

can any one guide me on this issue

@dchiesa1

 

 

 

Solved Solved
2 6 145
1 ACCEPTED SOLUTION

Yes, two things

  1. Do not use RaiseFault in a FaultRule.  It is an anti-pattern (details here).  If you need to set the response in a FaultRule, including in DefaultFaultRule, you should use AssignMessage. 
  2. I don't think you should need to explicitly set a header in an AssignMessage. If you use the CORS policy, it will apply access-control header as appropriate in the response. See the bundle i attached in my prior reply.  It shows the correct way. 

 

It could be that using a RaiseFault in a FaultRule is causing the problem, which requires you to set a header in AssignMessage.  

View solution in original post

6 REPLIES 6


@Sau101 wrote:

I have added in CORS policy in pre flow and target pre flow and Options Preflight flow .


 

You need to attach the CORS policy once.  In the Proxy Request preflow.  Just one attachment.  

https://youtu.be/OHbuqW_1fP0

Done Same thing but its giving error 

has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

when the default fault rule is called .

Here I am setting up error messages based on conditions and its giving response in default fault rule 

like oauthV2.OAuthV2-VerifyAccessToken.failed == true

I have added step for raise fault for custom error message format like 

{
"errorCode": "{flow.error.code}",
"errorType": "{flow.error.type}",
"errorMessage": "{flow.error.message}"
} getting these variable from fault rule assign message 

<FaultRules>
<FaultRule name="Unauthorized">
<Condition>(oauthV2.OAuthV2-VerifyAccessToken.failed == true)</Condition>
<Step>
<Name>AM-401Unauthorized</Name>
</Step>
</FaultRule>
<FaultRule name="Product not added to app">
<Condition>oauthV2.VA-APIKeyVerifier.fault.name == "oauth.v2.InvalidApiKeyForGivenResource"</Condition>
<Step>
<Name>AM-500InternalServerError</Name>
</Step>
</FaultRule>
<FaultRule name="Quota Limit Exceeded">
<Condition>(fault.name = "QuotaViolation")</Condition>
<Step>
<Name>AM-429QuotaError</Name>
</Step>
</FaultRule>
<FaultRule name="SC-Error">
<Condition>(fault.name Matches "ExecutionFailed") </Condition>
<Step>
<Name>AM-InvalidSSO</Name>
<Condition>(CyberArkOAuthResponse.status.code != "200") </Condition>
</Step>
</FaultRule>
</FaultRules>
<DefaultFaultRule name="fault-rule">
<Step>
<Name>AM-500InternalServerError</Name>
</Step>
<Step>
<Name>RF-CommonError</Name>
<Condition>(fault.name != "RaiseFault")</Condition>
</Step>
<AlwaysEnforce>true</AlwaysEnforce>
</DefaultFaultRule>

My suggestion: simplify your scenario to see what is really going on. 

I just tried this in my Apigee X organization, and it works as expected.

I have a simple proxy with a CORS policy in the Preflow.  And a conditional flow that calls VerifyAPIKey.  That one does not always execute. The  VerifyAPIKey is there, so that I can conditionally cause a fault.  

The proxy has a DefaultFaultRule which injects a header into the response. The DFR will execute in the fault condition that occurs if the proxy executes the conditional flow that calls VerifyAPIKey, but there is no api key. I see the expected behavior from this proxy:

  • When there is an Origin header in the request, meaning it is a CORS request, then there are Access-Control-XX-XXX (CORS) headers in the response.   
  • When there is no Origin header in the request, there are no Access-Control-* headers in the response. 

This occurs whether there is a fault or not.  In other words, Apigee is behaving as expected. (See the terminal session below) So there is something else going on wrong with yours.

Looking at your CORS policy, it may be that your Max-Age is set to 3628800, which equates to 42 days. Is it possible that your browser has cached an older version of the CORS response, and is relying on that older cache, and is never connecting with Apigee at all?  If you would like to eliminate this as a possibility, use the browser developer tools and turn off the in-browser caching. (Google it if you don't know which button to click) 

Otherwise I don't know what to suggest.  I've attached my proxy so you can try it in your environment.

My session: 

$ curl -i $apigee/cors-loopback/t2 -H "Origin:http://localhost"
HTTP/2 200 
apiproxy: cors-loopback r4
content-type: application/json
access-control-allow-origin: http://localhost
access-control-expose-headers: *
access-control-allow-credentials: true
x-request-id: 02b7fdd2-ea30-47fb-8421-a85411fd5a97
content-length: 24
date: Wed, 24 Apr 2024 19:47:40 GMT
via: 1.1 google, 1.1 google
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

{
    "status" : "ok"
}

$ curl -i $apigee/cors-loopback/t1 -H "Origin:http://localhost"
HTTP/2 401 
content-type: application/json
apiproxy: cors-loopback r4
access-control-allow-origin: http://localhost
access-control-expose-headers: *
access-control-allow-credentials: true
x-request-id: 7fadc9f9-c89f-444f-8c02-409f04b859d4
content-length: 150
date: Wed, 24 Apr 2024 19:47:45 GMT
via: 1.1 google
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

{"fault":{"faultstring":"Failed to resolve API Key variable request.queryparam.apikey","detail":{"errorcode":"steps.oauth.v2.FailedToResolveAPIKey"}}}

$ curl -i $apigee/cors-loopback/t1                             
HTTP/2 401 
content-type: application/json
apiproxy: cors-loopback r4
x-request-id: f6a6351b-8665-4496-90fd-47e6f0318ac9
content-length: 150
date: Wed, 24 Apr 2024 19:47:55 GMT
via: 1.1 google
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

{"fault":{"faultstring":"Failed to resolve API Key variable request.queryparam.apikey","detail":{"errorcode":"steps.oauth.v2.FailedToResolveAPIKey"}}}

 

Hi 

Thanks for the guidance.

The issue was in default fault rule raise fault policy.

It was not passing the Access-Control-Allow-Origin in response header even if the origin was present in the request .

For the solution I have updated the RF-CommonError

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="RF-CommonError">
<FaultResponse>
<Set>
<Payload contentType="application/json">
{
"errorCode": "{flow.error.code}",
"errorType": "{flow.error.type}",
"errorMessage": "{flow.error.message}"
}
</Payload>
<StatusCode>{flow.error.status}</StatusCode>
<ReasonPhrase>{flow.error.reason}</ReasonPhrase>
<Headers>
<Header name="Access-Control-Allow-Origin">{firstnonnull(request.header.origin,*)}</Header>
</Headers>
</Set>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</FaultResponse>
</RaiseFault> 

by This solution its working now.

Guide me if required any changes

Yes, two things

  1. Do not use RaiseFault in a FaultRule.  It is an anti-pattern (details here).  If you need to set the response in a FaultRule, including in DefaultFaultRule, you should use AssignMessage. 
  2. I don't think you should need to explicitly set a header in an AssignMessage. If you use the CORS policy, it will apply access-control header as appropriate in the response. See the bundle i attached in my prior reply.  It shows the correct way. 

 

It could be that using a RaiseFault in a FaultRule is causing the problem, which requires you to set a header in AssignMessage.  

Thanks for the help ,

It worked with adding Assign Message instead of Raise Fault .

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage continueOnError="false" enabled="true" name="AM-CommonError">
<DisplayName>AM-CommonError</DisplayName>
<Properties/>
<Set>
<Payload contentType="application/json">
{
"errorCode": "{flow.error.code}",
"errorType": "{flow.error.type}",
"errorMessage": "{flow.error.message}"
}
</Payload>
<StatusCode>{flow.error.status}</StatusCode>
<ReasonPhrase>{flow.error.reason}</ReasonPhrase>
</Set>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>