Replies: 5 comments
-
Hi Tyler, yes, Bob and I shortly discussed this problem earlier today. I am hesitating a bit to add CORS support into ACME's web server because I have the feeling that this is the wrong approach, but I am still trying to get my head around it. To your points. I don't understand why you suggest that Origin and X-M2M-Origin are related. The first one deals with the http / CORS aspects of the http protocol, and X-M2M-Origin is a oneM2M identifier that identifies the originator of a oneM2M request.
The X-M2M-Origin is a URI but not necessary a URL. Its content must follow the format defined for AE-ID or CSE-ID, while Origin as I understand it must be the URL of the original website where the browser loaded the web page and the JS content, right? So my understanding is that both values are actually two different things.
Why? The browser application (ie. the AE) needs to identify itself. This is done through the X-M2M-Origin header. And a request must have the oneM2M originator, otherwise it is invalid and will be rejected.
That could be an interesting addition. There is actually a a field in ACP resources that, if present, enables the use of one particular ACP resource for an IP address. Unfortunately, I haven't implemented this yet. Another quick solution could be to add a list of sites to the configuration that are allowed to do CORS, and ignore ACPs for the moment. Why I am still hesitating is that in normal deployments this is a functionality that is not realised by the actual backend server (or here: the CSE), but by a reverse proxy that is part of an API gateway and routes the request accordingly. So, both the requests to the web site's http server as well as the oneM2M Mca requests would go through this reverse proxy first. Actually, I did this for the stand-alone version of ACME's web UI. When run as a stand-alone the web UI's server forward oneM2M requests to the actual CSE (which would run on a different address/port). I faced the same problems here as you do. See https://github.com/ankraft/ACME-oneM2M-CSE/blob/master/acme/webui/webUI.py#L92 for the simple implementation. You can also use nginx, of course. This could be a quick fix that unfortunately adds another component to your infrastructure but wouldn't require any further change. Another option would be to not send the oneM2M requests over http but via MQTT. This would require an MQTT broker of course, but would eliminate the CORS problem The oneM2M requests over look similar to the http requests, only the packaging is a bit different (actually, those would be "pure" oneM2M requests). You can try this yourself by installing a broker (like https://mosquitto.org), enabling and configuring MQTT for ACME to connect to the broker, and then configure the test suite to use MQTT. By reading the logs you should get a good idea about the request format. Unfortunately, this would add more efforts for your installation as well as to add MQTT to your JS application. |
Beta Was this translation helpful? Give feedback.
-
I agree with what you have said about the The same is not true of the
The idea behind using the The reason that this change could be advantageous is that this would allow pure JavaScript web apps can now connect to the oneM2M CSE, without the need to have a backend hosted on the internet. Without this, you would have to use the reverse proxy as you mentioned. But if you're already using a reverse proxy, why not just use your own backend database instead of using oneM2M? To be clear, I am not suggesting that you add these changes to ACME. I am just thinking of ideas for improving oneM2M. Something like this would definitely have to get approved by the standard before adding it in. |
Beta Was this translation helpful? Give feedback.
-
There are a lot of interesting thoughts that we need to thing about and discuss. Bob and I will bring them definitely to the discussions within oneM2M. Thanks a lot for the discussion! There is one thing that is important to consider. The http binding is only one mapping to a concrete transport technology. Other are MQTT, CoAP or WebSockets. Each of these bindings is a mapping of the abstract oneM2M requests, and so is the oneM2M origin request attribute (that holds the ID of the sender, the "originator") mapped to the X-M2M-Origin http header field. The problem is now that the format of this origin attribute must follow a certain format. For example, it must usually start with an upper-case "C", like "CAdmin". It might have in addition the CSE-ID to identify the CSE node where this originator is registered, like "/id-in/CAdmin". But that's about it. It cannot be an http URL, like "https://example.com". Another issue is that this URL usually is a constant value (at least that is my understanding for the http(!) origin header field). This means that any instance of the web page would have the same originator, and so this value cannot be used to identify a particular instance of a web application. Each instance of an AE has a unique oneM2M originator. Now, is it possible to enable the browser to assign a unique oneM2M-valid ID to the http origin header? What I don't understand yet is why you want to replace the X-M2M-Origin header in the request? There are other X-M2M-* header that you must set for a valid request anyway.
That is a good question. In an enterprise deployment one would never expose backend services (or a CSE) directly to the public Internet, to a browser or to any other application directly. There is always an API gateway and/or a UI API layer in between (for authentication, load balancing, redirections etc). Another important point is that a oneM2M CSE is not just a database. Well, it offers data handling functionality in the form of storing data in the resource tree, but there is so much more IoT-related functionality that a normal database doesn't implement. I had a quick look at "flask-cors". You wrote that you integrated it in your ACME clone. It is really that simple to integrate as examples on the project's GH page suggest? (https://github.com/corydolphin/flask-cors) Are alle the headers added automatically for responses? In that case this could be a nice addition to ACME (configurable, of course, and switched off by default). |
Beta Was this translation helpful? Give feedback.
-
I am now adding CORS support directly in ACME. A first implementation with flask-cors was straightforward to do. Next will be a test by running a test in a browser as well as adding some configuration options here. |
Beta Was this translation helpful? Give feedback.
-
My tests with a browser (Safari & Firefox) went well. I tidied up the code, added configurations and documentations, and committed the changes to the development branch. There is no a [server.http.cors] section where CORS can be enabled. Please let me know whether this works for you. One note: I found that browsers refused to do the CORS procedures if the CSE isn't running with TLS (https) enabled. You still might need to accept a self-signed certificate, though, however the browsers don't ask when they trying to do the CORS test. I managed this by accessing ACME's web UI from the browser and then accepting an exception for that certificate. Then the certificate is also accepted for CORS. |
Beta Was this translation helpful? Give feedback.
-
Hi Andreas,
I have a feeling that Bob Flynn has already mentioned this to you, but I am current developing a React JS single page application (SPA) that requests data from the ACME CSE.
The problem with this approach that I am taking is that the SPA has to be hosted on a different IP (and/or port number) than the ACME CSE. This means that when the JavaScript code in the SPA makes an XHR/AJAX/HTTP request to the ACME CSE, the user's web browser will block the response if the ACME CSE does not specifically allow the Cross Origin Request.
For now, I've created a very bad workout one my own fork and branch: https://github.com/ExpandingDev/ACME-oneM2M-CSE/tree/cors-shim
The changes I make in there do two things:
flask-cors
as a dependencyI am aware that the way I am getting around these CORS problems is a hack and should not be used in the mainstream ACME CSE/oneM2M.
To me, the ideal solution would be something like this:
Origin
header as the same as theX-M2M-Origin
headerX-M2M-Origin
header if the plainOrigin
header is presentAccess-Control-Allow-Origin
only if theOrigin
in the response matches with an originator specified in an ACPI don't fully know if this would be a secure approach, I would be then concerned about attackers registering a domain name to match the originator present in an ACP in order to get their payload through.
Beta Was this translation helpful? Give feedback.
All reactions