Skip to Content

Or as the subtitle could also read: Overcoming “Access-Control-Allow-Origin”- errors


Setup in this example is:

  • development machine with local (web) server running a WebApp from http://localhost:12345
  • Gateway server accessible under a different domain than the local development machine, such as http://example.gateway.com:8000
  • need to consume services onGateway server, originating from local machine => Cross-Domain Request!

There’s a lot written out there why Cross-Domain read requests (not write requests!) aren’t a good idea. They generally open up data transfer possibilities between domains. Which is something you don’t necessarily want – for production environments, that is. What you should also never cater to – use a proxy-based approach to consolidate data read access under one domain, either software-based or with middleware.

But what about development?

There are legitimate reasons to read data across domains in development scenarios. Not the least is if you want to work with features offered by SAP Gateway – accessing SAP Backend logic that is exposed via OData Services. Which means issuing cross-domain REST requests from your development machine to a Gateway instance. If you don’t have Gateway running on your development machine. Which is unlikely. So here we are…

The good and the caveat – SOP

First of all, in the Web’s overall architecture, there’s SOP or Same-Origin-Policy:

[It] restricts how a document or script
loaded from one origin can interact
with a resource from another origin.

Origin here means the combination of protocol (e.g. http), FQDN (e.g. my.domain.com) and port (e.g. 80). So https://www.js-soft.com:4711 is considered an origin – so would http://localhost:12345.

And “can interact” in the quote above should be read as “scripts running from one origin are generally forbidden to interact with scripts from another origin”. (Does that mean you can’t include content from different sites? No! Read on…)

The client side

Stir in some OData- and Gateway-spice and it adds up to the following:

The user agent (e.g. Firefox) is rendering the WebApp from origin1, let’s say running on your development machine at http://localhost:12345/.

Now, if the WebApp is trying to retrieve JSON or XML via OData from http://example.gateway.com:8000/sap/opu/odata/sap/ZService/Entity(1), the user agent will send anXMLHttpRequest cannot load-error and block the request:


XMLHttpRequest cannot load http://example.gateway.com:8000/sap/opu/odata/sap/ZService/Entity(1).
No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
Origin ‘http://localhost:12345/ is therefore not allowed access.

This is due to SOP being implemented in all major user agents: the scripts in the WebApp are forbidden to consume resources from origins other than (in our example) http://localhost:12345/. Retrieving OData from the same origin such as http://localhost:12345/the/odata/endpoint would be allowed, retrieving http://example.gateway.com:8000/sap/opu/odata/sap/ZService/Entity(1) is not.

Note that we’re talking run-time and client-side here. SOP doesn’t apply to including JavaScript-files per se, it applies to the run-time interpretation of the script itself. So if you include JS-files via the <script>-tag, (e.g. <script src=”https://sapui5.hana.ondemand.com/resources/sap-ui-core.js“>), the JS-file will be downloaded from the remote address first. Then it will run from your origin, being interpreted by a user agent – that’s the moment that SOP restrictions will apply, not earlier.

Disclaimer: SOP is generally a Good Thing(tm). In prevents scripts from origin A to read data from origin B and transfer it back to A.

Which is exactly what you’d like to do in development 🙂

Resolution: start-flag for Chrome

Google Chrome offers a way to turn off SOP.

If you start the binary with the switch –disable-web-security, SOP gets disabled, allowing client-side cross-domain requests.

E.g. on OS X:

    /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome –disable-web-security

On Windows:

    “C:\Program Files (x86)\Google\Chrome\Application\chrome.exe” –disable-web-security

We’re half-way there – cross-domain requests are allowed on the client-side now.

The server side

Even though the client now allows requests across domain boundaries, the server still needs to grant access to those requests.

A standard way of doing this is utilizing CORS or Cross-Origin Resource Sharing on the server-side.

In essence, this means sending a an “Access-Control-Allow-Origin” header back to the client, authorizing the client’s domain – or granting general access with ‘*’.

Note: ‘*’-access is exactly what all the Northwind OData Test Services do. And what explains their popularity in example code.

Resolution: CORS with Gateway

Per default, Gateway send similar headers such as these:

Content-Encoding:”gzip”

Content-Length:”43233″

Content-Type:”text/html; charset=utf-8″

Server:”SAP NetWeaver Application Server / ABAP 731″

dataserviceversion:”2.0″

sap-metadata-last-modified:”Tue, 08 Jul 2014 08:55:38 GMT”

In order to send an additional, custom header from a Gateway-service, use set_header from Interface /iwbep/if_mgw_conv_srv_runtime.

It takes a structure as argument, consisting of a key-value pair.


data:
        ls            type ihttpnvp.
ls-name = 'Access-Control-Allow-Origin'.
ls-value = '*'.
/iwbep/if_mgw_conv_srv_runtime~set_header( is_header = ls ).

This will result in the desired CORS “Access-Control-Allow-Origin” header, granting all clients (“*”) read access:

access-control-allow-origin:”*”

Conclusion

Bringing all the above together means that by

  • using a switch to Google Chrome, you can get around SOP on the client side
  • sending the ‘Access-Control-Allow-Origin’ header from the Gateway-service allows CORS on the server side

And there you are, hopefully hacking away happily on your local machine, calling Gateway OData back and forth 🙂

For development purposes only – don’t unhinge these web security fundamentals just to make a quick transition into production scenarios, please!

tl;dr: Chrome –disable-web-security disables SOP, Gateway /iwbep/if_mgw_conv_srv_runtime~set_header allows CORS => developer happy

To report this post you need to login first.

22 Comments

You must be Logged on to comment or reply to a post.

  1. Paul Hardy

    Hello,

    I like to think I am very conformatble inside SAP world but I have to admit to utter ignorance when it comes to terms like “starting a binary”.

    In a Windows enviroment is the “disable web security” is this a setting you change when you open the Chrome internet browser, or something you have to run from the command prompt, or something utterly different yet again?

    Cheersy Cheers

    Paul

    (0) 
    1. Volker Buzek Post author

      Hi Paul,

      the disable-web-security is a flag for starting Chrome – so you can e.g. add it to a Desktop Shortcut. You can’t change that setting in about:config or such once the browser is running – the flag is valid for start-up only.

      hth, v.

      (0) 
      1. Paul Hardy

        Hello,

        That is a useful clue. I have a desktop shortcut for Google Chrome. I wonder if you could tell me whereabouts in the following screenshot do I put the “–disable-web-security”? I tried adding it at the end of “target” but it did not like that very much.

        Chrome Shortcut.PNG

        I appreciate this is probably obvious to most people!

        Cheersy Cheers

        Paul

        (0) 
  2. Prabaharan Asokan

    Hello Buzek,

    Good blog for developers.

    I would like to suggest some more content to the valuable contribution from you.

    Many Enterprise customers still use Interner Explorer as de facto standard. So we may need the same scenario above for IE.

    For Client side:

    You can set this in Internet Options: Go to the Security tab. For the current zone click the “Custom level…” button. In the next window, scroll about a third of the way down to “Miscellaneous > Access data sources across domains” and set it to “Enable”. If the current zone is Internet, then you should add the site to the trusted and set this option for the trusted zone instead.

    Note that this will effectively disable CORS and will not set the Origin header in the request.

    Capture.PNG

    Reference:

    http://stackoverflow.com/questions/20947359/disable-same-origin-policy-internet-explorer

    Cheers
    Prabaharan

    (0) 
  3. Tobias Trapp

    Hi Buzek,

    I like your blog 🙂 I have a question: have I to both on client an server side? I tried out the scenario and with Chrome and –disable-web-security everything is works – without and with AccessControlAllowOrigin:

    01.JPG

    You see that I set AccessControlAllowOrigin, but without –disable-web-security the result is the following, although AccessControlAllowOrigin is set:

    02.JPG

    I use local Gateway with SAP_GWFND 7.40 SP 5. and the latest UI5 from

    https://sapui5.hana.ondemand.com/resources/sap-ui-core.js

    Best Regards,

    Tobias

    (0) 
    1. Volker Buzek Post author

      Hi Tobias,

      if you run both the UI5 app and the OData endpoint on the same server, neither –disable-web-security (client) nor Access-Control-Allow-Origin (server) is needed – you’re not violating the same domain policy. This is also the desired setup for production scenarios (often this means “proxying” the OData resource to the same domain as the UI5 app) and what you’re experiencing if you’re running everything against the same GW install.

      If UI5 app and OData endpoint are on different URIs (e.g. localhost:8000 and services.odata.org), both client-side and server-side tweaks are required to make the data provisioning work.

      HTH, Volker.

      PS: Guessing the cause of the error 500 withouth knowing more about your setup (URI, JS code, …) is somewhat hard 😉

      (0) 
  4. Paolo Romano

    Hello Volker,

    got a question: I tried your server-side solution but I see no way to bypass the CORS check, e.g. the request seems not to arrive to my handler.

    There is some special arrangement? At which level should I put the access-control on my SICF tree ?

    Regards

    Paolo

    (0) 
    1. Volker Buzek Post author

      Hi Paolo,

      I haven’t made any modifications to the service in SICF after inital deployment of the OData service. This refers to GW & ERP being on the same instance. If you have GW in a stand-alone Hub install, you need to use the /iwfnd/* namespace for development instead of /iwbep/*.

      hth, v.

      (0) 
  5. Mark Teichmann

    I cannot get the Server side working.

    On client side we either get a 405 or a 401 regarding what HEADER data we send on client side ( withCredentials: true or false).

    The Allow-Origin header fields are set in method /IWBEP/IF_MGW_CORE_SRV_RUNTIME~READ_ENTITYSET but the client first does an OPTIONS call which seems to be unsupported in SAP Gateway (740 SP10).

    (0) 
    1. Volker Buzek Post author

      Confirmed: up to the current GW version, OPTIONS calls are not supported:

      /IWCOR/CL_DS_PROC_DISPATCHER->/IWCOR/IF_DS_PROCESSOR~PROCESS( ) only handles GET, POST, PUT, DELETE, MERGE and PATCH (depending on the resource called).

      OPTIONS requests are typically triggered only if cusom http headers are set on the client side for the call. Your “withCredentials” flag hints that you’re doing a custom ajax.get() that does just that, resulting in a “preflighted” OPTIONS request to the URL.

      So getting rid of the extra http headers in the ajax.get( ) might be a starting point for solving the issue.

      (0) 
      1. Mark Teichmann

        Now we are omitting the withCredentials header.

        But we need to authorize at the Gateway server and also need to fetch an CSRF token, therefore

        setRequestHeader(X-CSRF-Token, Fetch);

        is needed, but triggers also the preflight request?

        (0) 
        1. Volker Buzek Post author

          for authentication, you could do a GET on the service root, sending basic auth data (base64!) to Gateway (utilizing the underlying ICF basic auth).

          CSRF for POST, PUT, DELTE should work out of the box since it’s designed for cross-domain security.

          All in all this looks more like a client-side issue than a GW problem – should take this offline, PM me if you want.

          (0) 
          1. Basis Team

            Hello Buzek/Mark,

            We have the exact same issue Mark has highlighted. Would it be possible for you to let us know how this was solved i.e. how to we ensure that that call from client does not end up being a OPTIONS method?

            Regards,

            Jay

            (0) 
            1. Mark Teichmann

              Hi Jay,

              I did not find any time to investigate if Volkers answer would solve my problem.

              Will keep you updated if I continue with this task but it may take some weeks…

              Cheers, Mark

              (0) 
  6. Pavan Golesar

    Hey,

    I am new to Cors and its integration with GATEWAY, 😕 It’d be appreciated if someone puts more light on what is Cors, and how its related to gateway..

    Thanks alot in advance. 🙂

    –Pavan G

    (0) 
  7. Raghavendra Prabhu Mithal

    H Buzek,

    I tried all the steps you mentioned, but still get the CORS 401 error. the data fetch fails.

    I am running on local tomcat 8080 and trying to get the model on SAP server.

    var oModel =  new sap.ui.model.odata.ODataModel(“http://sapdeverp.XXXX.XXX:1080/sap/opu/odata/sap/Z_PERSON_SRV/“,true);

    in the debug I still see the cors error.. I have done everything you mentioned. Tried many blogs somehow this things is not getting solved. Do you have any clue.

    thanks,

    raghavendra

    (0) 
      1. Raghavendra Prabhu Mithal

        Hi Buzek,

        Thanks for your response. If I understand, you mean am I sending this one shown below.

        ******my service method*****************************

           method PDETAILSSET_GET_ENTITYSET.
        select pernr nachn vorna UP TO 20 ROWS from pa0002 into CORRESPONDING FIELDS OF TABLE  ET_ENTITYSET.
        DATA: ls type ihttpnvp.
        lsname = ‘Access-Control-Allow-Origin’.
        lsVALUE = ‘*’.
        /IWBEP/IF_MGW_CONV_SRV_RUNTIME~SET_HEADER( is_header = ls ).

        endmethod.

        Any inputs highly appreciated, unable to crack this one for many days now.

        thanks,

        raghavendra

        (0) 
        1. Volker Buzek Post author

          yep, that’s the server-side part.

          Now you need to make sure, that you have the client-side part prepared as well.

          I assume you’re consuming the service via some flavor of JS (UI5, jQuery, …).

          When calling the above service from your client-side runtime, you can either

          – run a local proxy to consume the service (thus “hiding” the remote GW server) or

          – provide your runtime with a tailored platform, such as running the Chrome Browser with –disable-web-security

          then you should be good to go.

          (0) 
        2. Raghavendra Prabhu Mithal

          Hi Buzek,

          Thanks once again.. I cleared the browser cache, and did a disable security using the command –disable-web-security. Yes, the CORS vanished now. But Alas! still data does not show 🙁 …well that is another problem. let me deal with it 🙂 .

          thanks,

          raghavendra

          (0) 
  8. Radhia Henouz

    Hello thank u for your post ,I’m new in SAPUI5 and the problem of CORS is driving me crasy 🙁 , I didn’t understand this part

    in order to send an additional, custom header from a Gateway-service, use set_header from Interface /iwbep/if_mgw_conv_srv_runtime.

    It takes a structure as argument, consisting of a key-value pair.

    1. data:
    2.         ls            type ihttpnvp.
    3. ls-name = ‘Access-Control-Allow-Origin’.
    4. ls-value = ‘*’.
    5. /iwbep/if_mgw_conv_srv_runtime~set_header( is_header = ls ).

    This will result in the desired CORS “Access-Control-Allow-Origin” header, granting all clients (“*”) read access:

    access-control-allow-origin:”*”


    ..where can I put it ?? I’m so confused

    Best regards

    (2) 

Leave a Reply