So PWA’s are a thing now, and I’ve been testing out the PLC next for our facility. I started to set up a PWA to hit the rest API for PLC Next and got a good old CORS error: No ‚Access-Control-Allow-Origin‘ header is present on the requested resource. I’m looking to see what technology PLC Next uses for its app server, but as it is now, I don’t think a PWA (or any other browser based front end) can hit the REST API. Anyone else see this before? Any chance the REST api can be updated to accept the CORS standard? Best,
We use nginx as the web server. There are resources on the web and many discussions around PWA+CORS+nginx. A good starting point is: How to create a VueJS PWA on a high performance, secure NGINX infrastructure https://www.freecodecamp.org/news/vuejs-pwa-on-nginx-22360ee7a7bf/ We’re happy to help further if you need any more information.
@Martin PLCnext Team# The info you provided is sort of a basic overview of these technologies. I spent the weekend spelunking your architecture and still cannot pass muster with CORs in a browser. Let me give you my findings so far. This is currently the result being produced by the browser: The architecture and pathing for the res API is based on nginx and fastCGI. The concerned configurations I have tracked down here:
/etc/nginx/nginx.conf /etc/plcnext/device/Services/Ehmi/nginx_ehmi_location.conf I have modified these configs to the following: nginx.conf user www; worker_processes 5; error_log syslog:server=unix:/dev/log,tag=nginx,nohostname,severity=error; pid /run/nginx/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #disable nginx version number in headers and error pages server_tokens off; #prevent content type sniffing add_header X-Content-Type-Options nosniff; #enable XSS filtering add_header X-Xss-Protection "1; mode=block" always; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; #filter out all 2xx and 3xx messages to prevent log flooding map $status $loggable { ~^[23] 0; default 1; } access_log syslog:server=unix:/dev/log,facility=auth,tag=nginx,nohostname,severity=info main if=$loggable; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; include /etc/plcnext/device/Services/Ehmi/nginx_ehmi_upstream*.conf; server { listen 80; #Added these for good measure #################################################################### add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Request-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"; add_header Access-Control-Allow-Methods "GET, PUT, POST, DELETE, OPTIONS"; ###################################################################### return 301 https://$host$request_uri; } server { client_max_body_size 400M; #TLS configuration listen 443 ssl; ssl_certificate /opt/plcnext/Security/Certificates/https/https_cert.pem; ssl_certificate_key /opt/plcnext/Security/Certificates/https/https_key.pem; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; #charset koi8-r; #Access log turned on ####################################### access_log /var/log/nginx/host.access.log main; ##################################################################### # Every thing below this line remains default ################################## ##################################################################### nginx_ehmi_location.conf access_log /var/log/nginx/server.access.log combined if=$loggable; error_log /var/log/nginx/server.error.log error; location ~* ^/_pxc_api/* { # pass the _pxc_api json commands to the FastCGI server listening on 127.0.0.1:9999 fastcgi_pass fastcgi_backend; # upstream set above fastcgi_buffering off; fastcgi_request_buffering off; fastcgi_buffers 8 64k; fastcgi_buffer_size 64k; # upstream sends chunks of 64k, as limited by the FCGI header. #fastcgi_busy_buffer_size 64k; # must be equal to or greater than the maximum of the value of fas> #fastcgi_connect_timeout 60; fastcgi_send_timeout 300; fastcgi_read_timeout 300; fastcgi_intercept_errors off; fastcgi_keep_conn on; fastcgi_next_upstream error off; fastcgi_pass_header status; fastcgi_pass_header Authorization; expires off; add_header Cache-Control "max-age=0, no-cache, no-store, must-revalidate"; # added configurations here ################################################################## add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Request-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"; add_header Access-Control-Allow-Methods "GET, PUT, POST, DELETE, OPTIONS"; ################################################################### include fastcgi_params; access_log /var/log/nginx/pxcapi.access.log combined if=$loggable; error_log /var/log/nginx/pxcapi.error.log error; #add_header X-debug-message "location _pxc_api" always; } ##################################################################### # Every thing below this line remains default ################################## ##################################################################### I have also rebuilt the SSL cert so that it will pass muster with browsers. This gist here is that we needed this line „subjectAltName = IP:192.168.1.10“. SSL.conf [ req ] default_bits = 4096 distinguished_name = req_distinguished_name req_extensions = req_ext prompt = no [ req_distinguished_name ] commonName = 192.168.1.10 [ req_ext ] subjectAltName = IP:192.168.1.10 Based on all these changes I still get the CORs error in the browser. However, you can see if I call the api directly from something like Postman, I do get the appropriate headers: Request OPTIONS https://192.168.1.10/_pxc_api/api/auth/auth-token Accept: */* Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.9 Access-Control-Request-Headers: content-type Access-Control-Request-Method: POST Cache-Control: no-cache Connection: keep-alive Host: 192.168.1.10 Origin: http://localhost:8080 Pragma: no-cache Referer: http://localhost:8080/ Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: cross-site User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36 content-type: application/json Response HTTP/1.1 200 OK Server: nginx Date: Mon, 04 Oct 2021 04:11:20 GMT Content-Type: application/json Content-Length: 44 Connection: keep-alive Status: 200 OK read_time: 2021-10-04T04:11:20.779352Z end_time: 2021-10-04T04:11:20.779946Z Cache-Control: max-age=0, no-cache, no-store, must-revalidate Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true Access-Control-Request-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS { "code": "f77bc27cb0164bdb", "expires_in": 600 } So you can see that this works, not sure why the browsers are still not working.
I am afraid that we (PLCnext Runtime Support) are not so familiar with general web development, so we have been passing your questions to people here with a bit more experience in this area. Here is some information on your latest question: [quote]See this discussion on SO. https://stackoverflow.com/questions/36250615/cors-with-postman Postman does not enforce CORS. This is why it works in postman. This is a general nginx configuration issue [not specific to PLCnext Technology].[/quote]If there are no answers on this topic from the PLCnext Community, perhaps you can try a general nginx forum.
Hi @Martin PLCnext Team# I was able to resolve this finally. The crux of the issue was that Chrome and Firefox where I tested these both had very specific requirements for accepting a root SSL certificate in combination with a CORS request. I tried a number of certificate configs, and this is what actually worked for future reference: ssl.conf [ req ] default_bits = 4096 distinguished_name = req_distinguished_name req_extensions = req_ext prompt = no [ req_distinguished_name ] commonName = 192.168.1.10 [ req_ext ] subjectAltName = IP:192.168.1.10 Also, it should be noted in my examples from he previous posts, I did also have one mistake. In nginx I used the request header by accident: Access-Control-Request-Headers: That should actually be: Access-Control-Allow-Headers:
That’s great, I’m glad you found a solution, and thanks for letting us know. I will pass this information on to the relevant people here, in case anyone else has a similar issue. Thanks again, and enjoy the weekend.