I have an electron app that creates a websocket connection to a node js server. It sends a JSON request to that server telling it to create a xmpp client.
let message = {
action: "setupXmpp",
data: {
username,
password,
},
};
socket.send(JSON.stringify(message));
Within that server I have a switch that reads the message action and creates the xmpp client. The code in xmppActions is standard boilerplate taken from xmpp's repo
const xmppActions = require("./Webapp/xmppActions");
case "setupXmpp":
console.log(`Received setupXmpp request`);
var { username, password } = message.data;
const xmpp = xmppActions.setUpXMPPconn(username, password);
xmpp.on("online", async (address) => {
console.log("▶", "online as", address.toString());
ws.send("Register xmpp Success!");
});
break;
Everything works fine I can create an xmpp client and send messages, all good.
My issue is when i have two clients open and they both register (with different username and password ) whoever is the last request always overrides the previous register. I've done a wireshark trace and the two websocket connections are created as I would expect but when it comes to sending messages they both use the most recent register. I assume it's because the XMPP client is a constant and whoever is last it uses those for all future requests.
How do I make it so that each websocket connection gets its own XMPP client almost like a request scoped client specific for each websocket.
I had a constant outside the websocket connection, changed it to have a var inside so each connection had it own client.
Related
If i don't have server details then who will establish or install the web socket connection? Can admin (who handle our host) establish connection and provide me the URL of websocket to use in android or angular code?
Is this necessary to write code in php?
I expect server admin install and configure web socket and provide to use in chat module.
Web admin(Ops) can help create the domain name but you have to write serverside code to handle websocket connection. You can use any programming language that supports websocket. for example,
nodejs - https://github.com/websockets/ws
java - https://docs.oracle.com/javaee/7/api/javax/websocket/package-summary.html
With java websocket you simply can define ServerEndpoint and deploy the war(web archive) to any host or cloud provider(Amazon cloud, google cloud etc),
#ServerEndpoint("/chat/{clientId}")
public class MyWebsocketServer {
#OnMessage
public String handleMessage(#PathParam("clientId") String clientId,
String message,
Session session) {
System.out.println("chat message: " + clientId + "/" +message);
return "hi how are you doing?";
}
}
Now, with javascript as a client your code to establish WebSocket connection would be:
var clientId = Math.random().toString(36).substring(2) + (new Date()).getTime().toString(36);
var webSocket = new WebSocket("ws://localhost:8080/my-app/chat/" + clientId);
localhost means I'm running both Server and Client in same host, in your case serving host might be www.mike-simons.com/chat while client running on phone
Example: https://github.com/duwamish-os/chat-server_websocket-java
I have a client application which runs in the browser which I can't change the implementation of to implement an MQTT client such as mqtt on npm.
The code in the library is as follows and allows me to pass in a socketUrl
const ws = new WebSocket(socketUrl)
I have tried generating a presigned URL for IoT, which seems to work in terms of authenticating (i.e. no Unauthorized response) but I get a 426 Upgrade Required response.
I believe I'm correct in saying that if it were working it'd reply with a 101 Switching protocols but without knowing much about MQTT i'm unsure if this is not happening because I'm doing something wrong or because I'm not using MQTT.
I'm generating a signed URL using the below code (I'll switch to Cognito Identities if I get this working rather than using the fixed key/secret)
const v4 = require('aws-signature-v4')
const crypto = require('crypto')
const socketUrl = v4.createPresignedURL(
'GET',
'myioturl.iot.us-east-1.amazonaws.com',
'/mqtt', // tried just /mytopic, too
'iotdevicegateway',
crypto.createHash('sha256').update('', 'utf8').digest('hex'), {
'key': 'removed',
'secret': 'removed',
'protocol': 'wss',
'region': 'us-east-1'
}
)
The protocols page in the iot documentation seems to suggest that if I point at /mqtt I'm indicating I'll be using MQTT.
mqtt Specifies you will be sending MQTT messages over the WebSocket protocol.
What does this mean if I just specify /foobar? Should I be able to connect to the socket but not using MQTT?
There are quite a few unknowns for me so I'm struggling to work out if it should work at all, and if so, which bit am I doing wrong.
This might be opinion based, but I still wonder is there a best practice since I'm almost clueless about websocket practices
I have a SPA that gets a JWT token from my own OP. It then uses that JWT to connect to other services I own using both REST and WebSockets.
As far as it goes for REST, it's pretty straightforward:
The REST API validates the JWT (sent in Authorization: Bearer ...) and provides access to the protected resource or responds with a 401, letting the SPA know it needs to request a new token.
Now with websockets :
During the load of the SPA, once I got a token, I'm opening a WS to my webservice. First message I send is a login_message with my JWT, which then I keep on server's websocket instance to know who's sending the messages.
Each subsequent message I receive, I validate the JWT to see if it's expired.
Once it has expired as far as I understand, I'm facing two options :
Drop the websocket with a token_expired error of some kind and force the browser to establish a new websocket connection once the token get refreshed.
Keep the websocket open, return an error message and send a new login message (once token is refreshed)
Don't use a login message but just send the JWT in each request.
Question : Which method would you recommend and why? In terms of security, and performance. Are there any other common practice I did not listed?
Quite an old question I've asked, so I'd be happy to share our chosen practice:
Once the client gets his JWT for the first time (when the application starts), a WebSocket is opened.
To authenticate the channel, we send a message that we define as part of our protocol, called authMessage which contains that JWT.
The server stores this data on the socket's instance and verifies it's validity/expiry before sending data down the wire or receiving from the client.
The token gets refreshed silently in web application minutes before it is expired and another authMessage is issued to the server (repeat from step 2).
If for whatever reason it gets expired before getting renewed, the server closes that socket.
This is roughly what we have implemented in our application (without optimization) and worked really well for us.
Oauth2 flow has two options to renew the token. As you said on of these options is prompt a message to the use to enforce a new login process.
The other option is to use the refresh_token in which you will avoid this login process to your user, every time the session expires and renew the token in a silent way.
In both case, you need to store the jwt in the client (commonly after login) and update it (after interactive login or silent regeneration). Localstorage, store or just a simple global variable are alternatives to handle the store and update the jwt in he client.
As we can see, jwt regeneration is solved following oauth2 spec and is performed at client side, SPA in your case.
So the next question is: How can I pass this jwt (new or renewed) to the external resources (classic rest api or your websocket)?
Classic Rest Api
In this case as probably you know, send the jwt is easy using http headers. In each http invocation we can send the old/valid jwt or the renewed jwt as header, commonly Authorization: Bearer ...
Websocket
In this case it's not so easy because according to a quickly review, there are not a clear way to update headers or any other "metadata" once the connection was established:
how to pass Authorization Bearer access token in websocket javascript client
HTTP headers in Websockets client API
What's more, there is no concept of headers, so you need to send this information (jwt in your case) to your websocket using:
protocols
var ws = new WebSocket("ws://example.com/path", ["protocol1", "protocol2"]);
cookies
document.cookie = 'MyJwt=' + jwt + ';'
var ws = new WebSocket(
'wss://localhost:9000/wss/'
);
simple get parameters
var ws = new WebSocket("ws://example.com/service?key1=value1&key2=value2");
Websocket only receive text
And according to the following links, websocket can extract header, get parameters and protocol just at the stabilization stage:
https://medium.com/hackernoon/implementing-a-websocket-server-with-node-js-d9b78ec5ffa8
https://www.pubnub.com/blog/nodejs-websocket-programming-examples/
https://medium.com/#martin.sikora/node-js-websocket-simple-chat-tutorial-2def3a841b61
After that, websocket server only receive text:
const http = require('http');
const WebSocketServer = require('websocket').server;
const server = http.createServer();
server.listen(9898);
const wsServer = new WebSocketServer({
httpServer: server
});
wsServer.on('request', function(request) {
const connection = request.accept(null, request.origin);
connection.on('message', function(message) {
//I can receive just text
console.log('Received Message:', message.utf8Data);
connection.sendUTF('Hi this is WebSocket server!');
});
connection.on('close', function(reasonCode, description) {
console.log('Client has disconnected.');
});
});
Send jwt in each request
Having analyzed the previous topics, the only way to send the new o renew token to your websocker backend is sending it in each request:
const ws = new WebSocket('ws://localhost:3210', ['json', 'xml']);
ws.addEventListener('open', () => {
const data = {
jwt: '2994630d-0620-47fe-8720-7287036926cd',
message: 'Hello from the client!'
}
const json = JSON.stringify(data);
ws.send(json);
});
Not covered topics
how perform a jwt regeneration using refresh_token
how handle silent regeneration
Let me know if you need this not covered topics.
Currently, we are using a fairly standard setup with Node.js + Passport + AJAX as the webserver, Backbone on the client, and an app server exposing APIs for the back-end. The client makes AJAX request to Node, which does some housecleaning such as checking the session authentication, then passes the request along to the app server.
We are experimenting with replacing the AJAX portion between the client and Node with WebSockets. That part I have working fine. Previously we were using passport to check if the request was authenticated. With a standard Node route, the request and response is always included so this is a trivial task, but I'm not sure how to get it from the socket.
var server = http.createServer(app).listen(port);
var io = socketio.listen(server);
var namespaced = io.of('/socket/test');
namespaced.on('connection', function (socket) {
console.log('a user connected to /socket/test');
socket.on('fetch', function (args) {
// the client has sent a socket message which will functionally replace an AJAX request
// authenticate session here - how to get the request??
});
});
I'm picking my way through the dartiverse_search example from the welcome page in dart editor. I see that it uses a path route to decide whether to transform a request into a websocket:
// The client will connect using a WebSocket. Upgrade requests to '/ws' and
// forward them to 'handleWebSocket'.
router.serve('/ws')
.transform(new WebSocketTransformer())
.listen(handleWebSocket);
Is it possible to turn a request into a websocket without using a routing path, for example using a query string to the root url?
You can specify any condition for upgrading to a WebSocket connection. You can even upgrade any connection request to a WebSocket connection without specifying a condition like this:
WebSocketTransformer.upgrade(request).then((WebSocket websocket) {
websocket.listen((String text) {
// process sent data
});
websocket.add(JSON.encode("Hello"));
});
If the request is not a valid web socket upgrade request a HTTP response with status code 500 will be returned. Otherwise the returned future will complete with the [WebSocket] when the upgrade process is complete.