In this blog post, I will share the configuration required to work around Cross-Origin Resource Sharing (CORS) when communicating from SAP Build Apps with an SAP SuccessFactors API.
My colleague Alejandro Rodriguez Barea approached me asking how we can get the communication going between SAP Build Apps and SAP SuccessFactors, given that he is building an app using the data in SAP SuccessFactors but he was facing the infamous CORS issue when calling the API from SAP Build Apps. I will share here the problems faced and how we can resolve them.
We are interested in Personal Information, which is exposed via the SAP SuccessFactors Employee Central APIs. In the API Business Hub, there is an API package that contains all the SAP SuccessFactors Employee Central APIs, within it, we find the Personal Information API, which contains the PerPersonal entity that I will be using in this blog post.
Alejandro shared with me the credentials needed to connect to SAP SuccessFactors API. I configured them in the SAP API Business Hub to validate them and quickly interact with the live system and get a sense of the data exposed.
You can find the list of SAP SuccessFactors API servers here: https://help.sap.com/docs/SAP_SUCCESSFACTORS_PLATFORM/d599f15995d348a1b45ba5603e2aba9b/af2b8d5437494b12be88fe374eba75b6.html?locale=en-US
In case you wonder why we don’t get a CORS error when calling the API from the SAP API Business Hub but we do get one from SAP Build Apps…. When calling the API from the SAP API Business Hub, the call is not being made directly from your web browser but it is sent to a server which is the one that calls the API on your behalf. Meaning, that the call to the SAP SuccessFactors API is actually being made from a server and not from the web browser. Contrary to how the call is being made from SAP Build Apps, which is made from your web browser.
To analyse what was going on when calling the API, I relied heavily on the Web Developer Tools available in my web browser (Mozilla Firefox in case you wonder as some of the error shown in the Web UI abstracted a bit the real error under the hood. Also, I used a REST client… in my case Postman.
What happens if we call the API directly from SAP Build Apps?
To test this, configure a Direct REST Integration in SAP Build Apps, set the Base URL, e.g. https://apisalesdemo2.successfactors.eu/odata/v2, in GET Collection configure an entity, e.g. PerPersonal?$top=20, include our credentials as an HTTP header, e.g. Authorization: Basic c2ZhZG1yMDIx, and then click the Run Test button.
- Error in UI: Error: TypeError: Failed to fetch. Does the server allow CORS?status: undefined.
- Error in browser console: Access to fetch at ‘https://apisalesdemo2.successfactors.eu/odata/v2/PerPersonal?$top=20’ from origin ‘https://emea-south-sapbuild-cmte91cp.design-time.eu10.apps.build.cloud.sap’ has been blocked by CORS policy: 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.
Unfortunately, we are unable to set the request’s mode to no-cors in SAP Build Apps. Also, it expects there to be an Access-Control-Allow-Origin header and if we test the call from a REST client, e.g. Postman, there is no such header returned.
In Web Developer Tools, I can see that there are two calls being made, one with the GET method and the second one with the OPTIONS method. The OPTIONS method was returning a 401 error, meaning I’m unauthorised to make the call. I validated this by making the same call from Postman and the OPTIONS method does require authentication.
Ok, so I found two ways of solving this problem…. I will discuss both approaches in this blog post:
- Using SAP BTP destinations
- Using SAP API Management
1. Using SAP BTP Destination
I only stumbled upon this after adding an SAP BTP destination to SAP Build Apps and honestly found it by mistake.
Configure the SAP SuccessFactors destination in SAP BTP
Below, is the destination I configured in the SAP BTP subaccount. The URL I setup is https://[my-api-server].successfactors.eu/odata/v2/. Notice the AppgyverEnabled property that was added to the destination and also that the basic authentication details are part of the destination.
Creating the SAP SuccessFactors destination in SAP BTP
In SAP Build Apps, we can then add an SAP system and select the destination. In my case, SFSF_Demo. I proceeded to “install it” and I get the message below displayed. Although no data entities are found in that particular destination.
Even though there are no data entities in that particular endpoint, it is possible to configure one by setting it up manually when creating a Data Entity using an SAP BTP Destination REST API Integration. To do this, go to SAP Build Apps classic data entities > Create Data entity button > SAP BTP Destination REST API integration. Then, you can select the destination we installed previously (SFSF_Demo) and set up the data entity manually. The video below shows how you can install the SAP system, add the data entity, set the schema from the response, and save the new data entity.
Simple right? I like this approach because of its simplicity.
2. Configuring SAP API Management
I know that others in the past have used SAP API Management to get around CORS so I will do just that. Similar to what was mentioned before regarding the SAP API Business Hub call… when the call goes via SAP API Management, the call to the API is being made from the server – API Management, not the browser.
As we all know, we can easily import APIs available in the SAP API Business Hub from within SAP API Management. I search for the SAP SuccessFactors Employee Central package and copy the Personal Information API to my tenant.
I then configure the API server, and set its name and title. Make any changes in the Host Alias, API Base Path, if applicable, and click OK.
Once created, I proceeded to deploy the API which gives me a new URL. I can now call the SAP SuccessFactors API through this URL.
What if we configure this new URL in SAP Build Apps and test it? Will it work?
Unfortunately, we get the same error as before.
Ok, so I saw a blog post from my colleague Murali Shanmugham, where he set a route rule in the Proxy Endpoint tab of the API. Also, he edits the PreFlow and PostFlow flows in the PolicyEditor.
I will do something a bit different here…. I know that there is now a Policy Template for CORS available in the SAP API Business Hub and I will use that for the API policy flows.
Ok, when applying the policy template we can see that it has assigned a couple of policies:
- in the ProxyEndpoint > PreFlow – Incoming Request the handleoptions policy, which will take place then the request.verb is OPTIONS. Sets the different headers needed.
- in the ProxyEndpoint > PostFlow – Outgoing Request the setCORS policy, which will insert some header parameters in the response headers.
What if we test again from SAP Build Apps after applying the CORS policy template?
In the UI we see the same error “Error: TypeError: Failed to fetch. Does the server allow CORS?status: undefined” but we see a different story in browser’s console. We can see that the OPTIONS call was successful and that the GET request now has a different error.
Access to fetch at ‘https://dev-advocates-free-tier.test.apimanagement.eu10.hana.ondemand.com/sfsf/odata/v2/PerPersonal?$top=20’ from origin ‘https://emea-south-sapbuild-cmte91cp.design-time.eu10.apps.build.cloud.sap’ has been blocked by CORS policy: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.
Looks similar to the error before but if we look closely we can see that it states that the authorization header is not allowed in the Access-Control-Allow-Headers. This is the Authorization header that we are passing in our request. Notice that the error mentions the preflight response. Meaning that we need to include the authorization header in Access-Control-Allow-Headers when we handle options. I include the header name in the handleoptions policy and redeploy the API.
What if we test again from SAP Build Apps after including the Authorization header?
Send a new request from SAP Build Apps and now we get a new error… Error: SyntaxError: Unexpected token ‘<‘, “<?xml vers”… is not valid JSONstatus: -1.
Although we get an error, this is great! It looks like we’ve solved the issue related to CORS but our response looks like an XML document. Let’s specify in our request that we expect the response body to be a JSON payload. We can do this by specifying an Accept: application/json HTTP header. Also, I set the response key path as I know the structure of the JSON payload.
If we test again we will get a successful result!!!! We can now set our schema based on the response and save our data entity.
Thanks for reading this far…. we’ve covered a lot in this blog post. We’ve seen how we can use SAP BTP destination to communicate directly with the SAP SuccessFactors system, no need for another system/proxy to be in the middle and we don’t have to deal with CORS. That said, we also saw how we can leverage SAP API Management to work around the CORS issue and how we can solve different issue, e.g. authorization header. I hope this blog post helps you troubleshoot issues when dealing with CORS + SAP Build Apps + policies in SAP API Management.
Now, over to Alejandro Rodriguez Barea who will show you the cool app he built with the SAP SuccessFactors API.