I have an application created with create-react-app and typescript. This application is an oauth2 client, and it needs a valid redirect_uri with https. It also uses websockets. I could setup an nginx server that accepts https and wss connections, and forwards them to my development machine.
This is how it should work:
web browser --> https and wss request --> nginx --> ssh tunnel (remoteforward) --> development machine --> local webpack and local application server
Nginx maps all https requests to localhost:3000 and all wss requests (anything under /api) to localhost:9091. These are forwarded to the local development machine via ssh tunnels. The local dev machine runs webpack on port 3000 and an appserver on port 9091.
This allows me to develop a valid web based oauth2 client, with valid https certificates and redirect_uri values. This setup was working for a long time. Today I have updated all packages (including node js, create-react app and webpack), and it stopped working. Here is the trace log:
SecurityError: Failed to construct 'WebSocket': An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.
./node_modules/react-dev-utils/webpackHotDevClient.js
node_modules/react-dev-utils/webpackHotDevClient.js:60
__webpack_require__
/home/my_user/projects/my_project/frontend/webpack/bootstrap:785
Of course, the problem is that webpack uses websockets for hot reload notifications, and the default code assumes that the webpack server is available at ws://localost. In reality, the page was loaded with https. So the hot reloading code tries to create an insecure websocket to ws://localhost, from a page that was loaded with https.
Since the oauth2 client MUST use https, the obvious solution would be to turn off hot reloading completely. But that would slow down the development. To work efficiently, I need hot reloading. This would only work if I could also forward the websocket of the hot reloading client from nginx to my local webpack server. In other words: make it work through wss:// instead of ws://.
However, I do not know how to do this. Is it even possible to re-route the hot reload client? Is there an option for that? I can see that it is possible to turn off hot reloading. But I do not see any way to re-route the URL for the websocket.
UPDATE - this is not needed anymore, solved in v3.3.1 https://github.com/facebook/create-react-app/pull/8079 . But there are still packages that depend on an older version. In those cases, the solution provided below may be used.
Since create-react-app 3.4.0 released only couple weeks after your post, you can now precise these environment variables:
WDS_SOCKET_HOST
WDS_SOCKET_PORT
WDS_SOCKET_PATH
Check this commit for more details: create-react-app#822422c
I have found a solution. It is really a hack, but it can be used for testing oauth2 clients with https, your own websockets and webpack dev server's own socks-node websocket at the same time.
Install all required packages
Edit your node_modules/react-dev-utils/webpackHotDevClient.js file
Replace this:
// Connect to WebpackDevServer via a socket.
var connection = new WebSocket(
url.format({
protocol: 'ws',
hostname: window.location.hostname,
port: window.location.port,
// Hardcoded in WebpackDevServer
pathname: '/sockjs-node',
})
);
With this:
// Connect to WebpackDevServer via a socket.
var connection = new WebSocket(
url.format({
protocol: 'wss',
hostname: window.location.hostname,
port: window.location.port,
// Hardcoded in WebpackDevServer
pathname: '/sockjs-node',
})
);
This will force nodejs-socket to use wss instead of ws. If the nodejs-socket is also tunneled through your web server to your local webpack dev server, then it works!
It would be great if this code could use ws / wss depending on the protocol of window.location.
Related
I've created a multiplayer game with websockets in nodejs (using the ws lib), which works just fine. For debugging, I connected to the websocket server with my client webpage by just opening the html file via file:// protocol.
I wanted to have the page hosted on my web-server which uses https. This web-server also uses nodejs, but because the webpage is served via https, it cannot create a connection via ws and needs wss. Security downgrading and so on.
My problem is that I've got two separate programs: the https webserver and the websocket "game" server.
When i try connecting to the ws server i get:
Uncaught DOMException: The operation is insecure.
I only found instructions on how to set up wss by creating a https server, but i already have one.
Do i need to combine the two programs?
Could i maybe just serve the single page for the game with http?
Is there some other technology, which doesn't have these security restrictions? (i don't care about encryption for the websockets)
I was able to make it work:
Instead of wanting to use the https server from the web-server, i "upgraded" my http-server for the "gameserver" to https. I didn't want to "create" another https server, because i thought it would cause errors, but i already made a http server indirectly anyways, with new WebSocketServer.Server({ port: PORT });.
To create the https server and use it for the WebSocketServer i used this code:
let cert = fs.readFileSync(pathtocertkey, "utf8");
let key = fs.readFileSync(pathtopublickey, "utf8");
let options = {key: key, cert: cert};
let server = require("https").createServer(options);
const wss = new WebSocketServer.Server({ server: server});
After that i could listen to any port with server.listen(PORT,callback).
I also wasn't sure how or if i could get the cert i got with greenlock. But I found it undergreenlock.d/live/[yourdomain]/
With greenlock.d being the configDir specified in greenlock.init(options).
For the client i need to connect like this :
ws = new WebSocket("wss://mydomain:"+PORT);'
I'm currently using websocket library on both nodejs backend and JS client.
I'm trying to change to socket.io, I managed to write the code for both sides however I'm unable to connect to the server from my client.
On the console of my browser I see the error and realised that the address is being changed by the socketio library.
websocket: wss://domain.com/asset-ws/ ==> works fine
socketio: auto changes the url to wss://domain.com/socket.io/?EIO=4&transport=websocket
The error: WebSocket connection to 'wss://domain.com/socket.io/?IO=4&transport=websocket' failed.
Is it something to do with my nginx config?
Any help would be much appreciated.
Thank you
By default socket.io url adds the /socket.io path.
In my Nginx config I'm already using a custom path add, so I just specified a path on the io server config, like so:
const io = new Server(server, {
path: '/'
});
This way the default path would be overwritten.
I've created a application with Vue and Node js but I get a trouble "CORS" when I try to connect to a public websocket from "http://localhost".
Project structure
Error log
I believe the cause of this problem is a trying to connect via http to a https server.
Any idea to solve this problem in localhost ?
Try installing this node package named cors-server that will start a web server on a given port that will proxy any request received and add CORS headers to it.
To start the server simply call it from the command line and supply a port number
cors-server <port>
After that, any requests sent to http://localhost:[port] will have Access-Control-Allow-Origin: * on the response.
e.g:
POST http://localhost:3005/http://www.google.com
Hope this helps!.
The real problem in this case, is to try to connect to other domain from the client side and the server do not allow "Access-Control-Allow-Origin".
The best solution for this question is follow the arquiteture of project bellow:
https://github.com/Tucsky/SignificantTrades
In resume, implement conection websocket in server side and expose a local websocket in the client side.
In my case, Node and Vue js.
Trying to be able to run a SSL server using the generator-angular-fullstack https://github.com/DaftMonk/generator-angular-fullstack.
However when I look at all the examples for enabling SSL, when I comb through the code it doesn't seem to initialize the server the same way as the NodeJS documentation explains to:
var options = {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};
// Create a service (the app object is just a callback).
var app = express();
// Create an HTTP service.
http.createServer(app).listen(80);
// Create an HTTPS service identical to the HTTP service.
https.createServer(options, app).listen(443);
Has anyone had any success in doing it? Outside of that this generator seems to be incredible and easy to use.
Yes, the code above is how you run your app on 443, using the key and cert you have specified above. This should allow you to communicate with your app over HTTPS, assuming you have those keys (and of course you'll get warnings in the browser if they're self signed).
But yes, that works, and is how it's done. I've found that most people like to keep the Node app running on HTTP and instead use a web server (such as nginx) to deal with SSL. The communication from the web server to the Node app is then over HTTP. This helps keep the Node app easy to run in a development/test environment, and then in production you have the security of SSL.
I'm planning to do three sites using node.js. I have got some common templates among the sites. Should I run all three sites on single node.js instance?
I'm aware of 'vhost' middleware that allows you to run multiple domains on single http server. Is there any better option to do this?
I've also got some static html templates and not sure how to deal with these in node.js?
Finally I would like to know hosting options for this kind of setup?
I myself just had to do this exact same thing. What you want to do is use some sort of reverse proxy.
The one I use is here: https://github.com/nodejitsu/node-http-proxy
Simply install the proxy package: npm install http-proxy
What I do is have the proxy running on the server on port 80. I set the DNS up on each domain to point to this server.
Each application is running on the same server (im using screens).
For example:
MySiteApplication1 - 3001
MySiteApplication2 - 3002
MySiteApplication3 - 3003
Then your proxy server file would look like this
var httpProxy = require('http-proxy');
var server = httpProxy.createServer({
router: {
'mysite1.com': 'localhost:3001',
'mysite2.com': 'localhost:3002',
'mysite3.com': 'localhost:3003'
}
});
server.listen 80