Azure APIM throws error for WebSocket API due to Connection: Keep-Alive,Upgrade - azure

I have REST and WebSocket APIs on the Azure API Management services portal. WebSocket is redirecting to different Web Pubsub service based on input parameters from clients/frontend for different development environments.
When hitting WebSocket api from Google Chrome, I am able to successfully establish connection end-to-end.
When hitting same WebSocket api from Firefox, I am getting InvalidWebsocketUpgrade error from APIM service.
This happens because Chrome is sending Connection: Upgrade in the socket connection request header, while Firefox is sending Connection: Keep-Alive, Upgrade in the header.
Chrome Request:
GET wss://apim-test.azure-api.net/qa/socket?access_token=eyJhbGc HTTP/1.1
Host: apim-ecv.azure-api.net
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36
Upgrade: websocket
Origin: https://abc.xyz.com
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Sec-WebSocket-Key: GTWCGvTFJN82sAl8gVv+VA==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Protocol: json.webpubsub.azure.v1
Firefox request:
GET wss://apim-test.azure-api.net/qa/socket?access_token=eyJhbGciOi HTTP/1.1
Host: apim-ecv.azure-api.net
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Sec-WebSocket-Version: 13
Origin: https://az-qa2.ecarevault.com
Sec-WebSocket-Protocol: json.webpubsub.azure.v1
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: r764n2hSpKKr0Y63z1Ok3A==
Connection: keep-alive, Upgrade
Sec-Fetch-Dest: websocket
Sec-Fetch-Mode: websocket
Sec-Fetch-Site: cross-site
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Do I need to configure anything to support this on APIM or anywhere else??

Glad that you got the Solution in Microsoft Q&A. Posting the suggestions over here to help the other community members as this is a feature request and would be helpful for the members with related discussions.
According to this document in the connection header, you need to use only Upgrade because it is a WebSocket protocol and as of now APIM is enabled only with Upgrade as a value of connection header and MS is working on enabling keep-alive, Upgrade values this could be a feature request.

Related

Browser is not adding cookie on some requests

We are running an application (local app) inside another application (moodle) as a plugin.
What happens is when our app tries to access the backend graphql it doesn't add the cookies but the cookie is available on document.cookie before the request is sent. Also before the app is opened in the iFrame there are few requests made from moodle to backend server and the browser adds the cookie to those request.
Following are the request headers
moodle -> backend
Host: graphql.app.home
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-Dest: iframe
Referer: https://moodle.home:8443/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: cb_ltiid=s%3AF0FJpsc8bVe9ZqyLzgNgK7flKfGf4W7u.2GL43c7XLV11BzHXCS%2B7AvQKBAS9xg%2BNc7gaj264%2Bks
app (from moodle iFrame) -> backend
Host: graphql.app.home
Connection: keep-alive
Content-Length: 118
Pragma: no-cache
Cache-Control: no-cache
accept: */*
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
content-type: application/json
Origin: https://app.home
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://app.home/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Thanks for help
It was issue with fetch not sending the header because of
fetch won’t send cookies, unless you set the credentials init option.
(Since Aug 25, 2017. The spec changed the default credentials policy
to same-origin. Firefox changed since 61.0b13.)
solution was to pass credentials: 'include' to fetch options

CORS blocks one API request (not others) in Safari

I am running a React (16) webapp (deployed on Netlify) that is failing with one if its API calls being blocked by CORS but only in Safari. There's no problem in Chrome or Firefox. The console shows the following:
Origin https://chicha.netlify.app is not allowed by Access-Control-Allow-Origin.
XMLHttpRequest cannot load https://chicha-api.herokuapp.com/votes due to access control checks.
Failed to load resource: Origin https://chicha.netlify.app is not allowed by Access-Control-Allow-Origin.
Headers of the blocked request (Safari):
Summary
URL: https://chicha-api.herokuapp.com/votes
Status: —
Source: —
Initiator:
xhr.js:178
Request
Accept: application/json, text/plain, */*
Origin: https://chicha.netlify.app
Referer: https://chicha.netlify.app/events
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15
X-Requested-With: XMLHttpRequest
Response
No response headers
Here are the headers of the same request that is not blocked (Chrome):
Request URL: https://chicha-api.herokuapp.com/votes
Request Method: OPTIONS
Status Code: 204 No Content
Remote Address: 52.214.138.78:443
Referrer Policy: no-referrer-when-downgrade
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: x-requested-with
Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
Access-Control-Allow-Origin: https://chicha.netlify.app
Connection: keep-alive
Content-Length: 0
Date: Wed, 03 Jun 2020 10:43:59 GMT
Server: Cowboy
Vary: Origin, Access-Control-Request-Headers
Via: 1.1 vegur
X-Powered-By: Express
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en,es;q=0.9,ca;q=0.8,fr;q=0.7
Access-Control-Request-Headers: x-requested-with
Access-Control-Request-Method: GET
Connection: keep-alive
Host: chicha-api.herokuapp.com
Origin: https://chicha.netlify.app
Referer: https://chicha.netlify.app/events
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36
The app makes the call with axios configured as followed:
class ApiClient {
constructor() {
this.apiClient = axios.create({
baseURL: process.env.REACT_APP_BACKEND_URI,
withCredentials: true,
headers: { 'X-Requested-With': 'XMLHttpRequest' },
});
}
getVotes = () => this.apiClient.get('/votes');
getEvents = () => this.apiClient.get('/events');
...
The API (Node.js with Express deployed on Heroku) has the following CORS configuration:
app.use(
cors({
credentials: true,
origin: [process.env.FRONTEND_DOMAIN],
})
);
... where FRONTEND_DOMAIN is https://chicha.netlify.app in environment config vars on Heroku.
What's strange is other API requests have no issue. API headers of a successful request (Safari):
Summary
URL: https://chicha-api.herokuapp.com/events
Status: 304 Not Modified
Source: Memory Cache
Address: 52.215.119.172:443
Initiator:
xhr.js:178
Request
GET /events HTTP/1.1
Accept: application/json, text/plain, */*
Origin: https://chicha.netlify.app
Accept-Language: en-gb
If-None-Match: W/"c010-APRvUYTowK2az7ovB1dPcY+SGuk"
Host: chicha-api.herokuapp.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15
Referer: https://chicha.netlify.app/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
X-Requested-With: XMLHttpRequest
Response
HTTP/1.1 304 Not Modified
Connection: keep-alive
Access-Control-Allow-Credentials: true
ETag: W/"c010-APRvUYTowK2az7ovB1dPcY+SGuk"
Date: Wed, 03 Jun 2020 10:26:18 GMT
Via: 1.1 vegur
Access-Control-Allow-Origin: https://chicha.netlify.app
Content-Length: 0
Vary: Origin
Server: Cowboy
X-Powered-By: Express
Does the fact that the blocked request has a referrer which is distinct from origin matter? Or perhaps some issue with the cookie not being sent with the request (despite the withCredentials configuration in axios)?
EDIT (4 Jun 2020): I've been able to replicate this on localhost, so I'm editing to indicate it's just an issue in Safari. Given related questions and some testing I believe this has to do with the allowedHeaders / CORS Access-Control-Allow-Headers configuration and maybe the way Safari configures OPTIONS preflight request. I haven't been able to see all the request details even configuring Reactotron and apisauce.
EDIT (7 Jun 2020): so I've noted cookies aren't being set in Safari, so the currentUser I set into express on the server side is not persisting (in req.session.currentUser where I set it after login). Also while I was able to produce the error at one point in localhost I am not able to reliably replicate it locally.
It turns out Safari was blocking the cookie sent from my API as a result of the "Prevent cross-site tracking" option in Safari privacy settings. Disabling this and everything works perfectly.
So it seems the only workaround is to place the API on the same domain as the web app, or to use a proxy, as referenced in this question and answer:
Is there a workaround for Safari iOS "Prevent Cross Site Tracking" option, when issuing cookies from API on different domain?
EDIT: The following answer only applies to localhost development, not for when this issue also happens on a remote server. It's a temporary solution in development mode.
Well, if someone else finds this question, there's an easy fix in Safari. First, make sure you have your Develop menu in the menu bar by going to "Safari" -> "Settings" and in the tab "Advanced" select the option "Show Develop menu in menu bar". Once this is done, you should see the Develop menu. Now, select "Develop" -> "Disable Cross-Origin Restrictions".
Disable Cross-Origin Restrictions in Develop menu

NodeJS: Why is my POST request not working?

I'm trying to use the net module in nodejs to send a POST request while connecting to a socket.
I've tried sending a GET request which indeed does work, but I can't seem to get the POST request to work.
This is the code:
socket.write(`POST /page.php HTTP/1.1\r\n
Host: domain.com\r\n
Connection: close\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3\r\n
User-Agent: Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13\r\n
Upgrade-Insecure-Requests: 1\r\n
Accept-Encoding: gzip, deflate\r\n
Accept-Language: en-US,en;q=0.9\r\n
Cache-Control: max-age=0\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 0\r\n\r\n`);
I've "debugged" it, and I've noticed some 301 Moved Permanently and Temporarily redirected responses from the host.

Is there any chances of having websocket client connecting to an aws ec2 server behind a load balancer?

I'm working on a webchat that runs nodejs on port 8080 to publish and 4555 to subscribe using websocket at client side(javascript). When I do this connection direclty to the ec2 instance it works like a charm but in this case I won't have scalability and also have to open port 443 to world. If I make the connection through Load Balancer, chrome/firefox console shows this output and it fails to connect:
WebSocket connection to 'wss://www.domain.com/socket.io/?EIO=3&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 200
These are the headers:
Request URL: wss://www.domain.com/socket.io/?EIO=3&transport=websocket
Request Method: GET
Status Code: 200 OK
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Date: Sat, 25 May 2019 13:52:55 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Pragma: no-cache
Server: nginx/1.12.2
Transfer-Encoding: chunked
X-Powered-By: PHP/5.6.40
Accept-Encoding: gzip, deflate, br
Accept-Language: pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7
Cache-Control: no-cache
Connection: Upgrade
Cookie: PHPSESSID=nt35ln6ipvbkhflic3obfhd615; AWSALB=l1rePGzfT+/sXpFl0uWliWsb9/Cqdy7V/FjiHF8slLY2pRJ2IbN86SsQJ2UBQZBIfFk8iHKKuCBbe+lLrKcN6toxjiOVVJNxzL2Xaz7BC9aShLzOCTuxkRgmC9/I
Host: www.domain.com
Origin: https:/ /www.domain.com
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: fN1LLn8igDR5CadSE9UVIw==
Sec-WebSocket-Version: 13
Upgrade: websocket
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36
EIO: 3
transport: websocket
The javascript client is very simple, for tests purposes:
var socket = io('https: //www.domain.com/chat',{'secure':true,transports: ['websocket', 'polling', 'flashsocket']});
In nginx config files I have a location /chat proxying the request to :4555 and there is nodejs running and listening.
I have spent 2 days on this and then decided to hire someone who could do that, I had two guys working on it and they couldn't be able to fix.
Can anyone here show me the way?

Secure WebSockets with Socket.io, Node.js, and iisnode

I have an application that works perfectly fine with websockets through iisnode to a Node.js/Express/Socket.io application.
Windows Server 2012
iisnode 2.21
IIS 8.5
Node 6.2.1
socket.io 1.4.8
express.js 4.14
When visiting via HTTPS, however, WebSockets are falling back to polling after a seemingly successful wss negotiation:
GET wss://x.com/socket/a145e1f7-c8e7-4b26-96d3-a4d9869b5f3a/?id=a145e1f7-c8e7-4b26-96d3-a4d9869b5f3a&EIO=3&transport=websocket&sid=2nKT2dHWKrmO5xaHAAAM HTTP/1.1
Host: x.com
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: https://x.com
Sec-WebSocket-Version: 13
DNT: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-US,en;q=0.8
Cookie: io=2AAAM; _ga=GA1.2.452745265.1462472455; connect.sid=s%3M8aVwM; rl-sticky-key=!1z8WBml+V4=
Sec-WebSocket-Key: qocdK/MRQ==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Server: Microsoft-IIS/8.5
server: Microsoft-IIS/8.5
Connection: Upgrade
sec-websocket-accept: VOFznVr/l4fsY=
sec-websocket-extensions: permessage-deflate
x-powered-by: ASP.NET
X-Powered-By: ASP.NET
Date: Mon, 19 Sep 2016 18:20:04 GMT
But there are no frames in this WebSocket, and Socket.io returns to HTTP polling. If TLS is handled by IIS, how could WebSockets be effected after wss protocol negotiation?
It turned out there were load balancer problems that caused this issue, nothing that the technology software stack was doing wrong.

Resources