I am using nodejs to implement a server application with XMPP. I am following the guide to authorize an XMPP connection. My problem is exactly when I expect a
<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>
when I send the server key, a SASL PLAIN authentication. It is made this way
const key = Buffer('\x00' + senderId + '#gcm.googleapis.com\x00' + serverKey).toString('base64');
const message = `<auth mechanism="PLAIN"
xmlns="urn:ietf:params:xml:ns:xmpp-sasl">${key}</auth>`;
Where senderID is that number that is in "Cloud Messaging" tag and
serverKey is one of the server keys from the "Cloud Messaging" tag. There are two server keys types: one is the "normal" and the other one is inherited; I've used both types without success.
I don't really know what I am doing wrong, or what I am missing.
The first two steps of the connection, the 'hello' and the list of mechanisms response from FCM are done. However, after this, FCM closes the connection. I suspect is related with this problem.
I would appreciate a help. Thanks.
I've contacted with the Firebase support team and they have solved my problem (thanks a lot).
The thing, with nodejs, is you have to avoid to implement the event 'end' on the socket because this seems to force to close the socket, and use the same socket. Another thing is to avoid set up the socket encoding. You can convert the buffer with another encoding though.
With all this I can mark this question as solved.
Related
I was trying to implement web-sockets (npm ws) in my express application, but got stuck on how I should implement my websockets so that they work with express router.
Currently, my endpoints look like this...
router.post('/create-note', jwtAuthentication, NotesController.createNote);
router.get('/get-notes/:id',jwtAuthentication, NotesController.getUserNotes);
router.??('/socket-endpoint', NotesController.wssNote);
As you can see, I am unsure of what method to call on my router. I have tried using 'get' and 'post, but for some reason it only works after I try a second connection on postman. (I click connect, nothing happens, I then click disconnect and connect again and it works.)
I know that I can pass in the path when creating the WebSocketServer...
var wss = new WebSocketServer({server: server, path: "/hereIsWS"});
This does work, but if it is possible to use routers with web-sockets, I think it would make my whole project much cleaner and more organised.
I have come across people recommending 'express-ws', but was wondering if there was a better method to solve my problem, specifically a method that does not involve other packages.
Thanks in advance!
You do not use Express routers with webSockets. That's not the proper architecture for webSockets. Your webSocket server can share an http server with Express, but that's pretty much all the two have to do with one another.
webSockets connect on a particular path which you pass to the webSocketServer() constructor as it appears you already know. Once they are connected they stay connected and form a TCP pipe that you can send packets of data from client to server or from server to client. There is no Express routing used for that.
You can create your own message handling within a webSocket message by creating a message name as part of the webSocket payload if you want (this is something that the socket.io layer on top of webSockets does for you), but it has nothing to do with Express at that point. That's just in how you choose to handle the incoming webSocket packets.
if there was a better method to solve my problem
What is your specific problem? Perhaps if you stated the specific problem you want help with, we could provide further assistance.
To handle incoming webSocket messages, you can follow the example in the ws server doc:
wss.on('connection', function connection(ws) {
ws.on('message', function message(data) {
console.log('received: %s', data);
});
ws.send('something');
});
To further break up this to handle different types of incoming webSocket messages, you have to create your own message format that you can branch on or use socket.io instead on both client and server that does that for you.
I have a webhook that runs on message save and message update. I do some basic bad word filtering. I can update the message to filter out bad words. However, when I attempt to flag the message, I get an error. Has anyone seen anything like this? How have you worked around it?
The code:
client.flagMessage(message.id).then(r => console.log('flagged message', r))
I have verified that client works as I am able to update the message with client in the same Promise.all() call.
The error:
Flag failed with error: "either user or user_id must be provided when using server side auth."
Version:
"stream-chat": "^1.7.4"
The docs:
https://getstream.io/chat/docs_rest/#flag
https://github.com/GetStream/stream-chat-js/blob/master/src/client.js#L1227
It seems very similar to this closed issue:
https://github.com/GetStream/stream-chat-js/issues/113
This might not be a proper answer but definitely is a resolution for your problem.
If setUser is called on the client then server will be able to get the flagging user from the JWT (client side auth) but in server side auth, there is no user passed to server so you get the expected error message.
You check REST docs and as seen, server supports it where JS client lacks this server side support. This a missing feature bug from JS client. It's reported to be extended as soon as possible.
The fact that I haven't found an existing answer for this makes me think I'm asking the wrong question. Please feel free to (gently or otherwise) push me onto a better path if necessary.
We use a dedicated auth server, the purpose of which is to (1) given login credentials, return a JWT with a near-term exp or (2) given a JWT, according to a set of rules, issue a new JWT. A refresh, essentially.
That all works ace, until it's hacked. But for now, it's ace.
When it comes to socket.io connections to non-auth servers, however, we're shooting more than a bit from the hip. I wonder if somebody would be so kind as to evaluate this process. (I'm happy to post more code; you tell me if it's relevant).
1) initial socket.io connection results in a challenge:
this.socket.emit('authenticate'); // the challenge
this.authTimeout = setTimeout(() => {
this.socket.disconnect('unauthorized', errors);
}, TIME_TO_AUTHENTICATE); // the response kills this!
this.socket.on('authenticate', token => {
clearTimeout(this.authTimeout);
this._authenticate(token)
})
2) subsequent messages must contain a "payload" message in the form:
payload = {token: 'foo', message: 'bar'}, which token would be accepted if valid or returned if invalid.
In addition, the resource server sends its own periodic heartbeat, which must be acknowledged by heartbeat {token}.
My question, thus is: this seems too easy; am I cutting corners somewhere? Could you defeat this feeble fortification?
Just to be clear, we're looking to roll our own module here. I'm happy to look at anything existing; just haven't found anything I could begin to convince the bosses is fully baked for our needs.
Many thanks in advance.
I cannot fully analyse the method or ensure it doesn't have flaws, however I'd like to point out some things that came up to mind:
Apart from disconnecting the user in case of timeout on authentication challenge, you must ensure that the server does not send any non-public message to this user until after the authorization challenge is actually fulfilled successfully. Otherwise, there is a period until timeout where the user could receive a message without being authenticated.
I assume that you are also disconnecting the socket if token is invalid (or someway preventing non-public message to be sent).
This article is about authenticating socket.io communications using JWT. It is from 2014 so it might be a little bit out of date but I think that the core concept is still valid.
Associated with the article, there is a tool built specifically to authenticate socket.io connections using jwt. Even if you don't want to use it, you might want to explore its code looking for "inspiration". You can find it here: socketio-jwt.
You can see that this tool is able to use two different approaches:
An approach pretty similar to yours:
from socketio-jwt/blob/master/lib/index.js
if(options.required){
var auth_timeout = setTimeout(function () {
socket.disconnect('unauthorized');
}, options.timeout || 5000);
}
socket.on('authenticate', function (data) {
// ...
// Token validation
// Emit "authenticated" event if token is valid, the server can use
// this event as a point to send messages, once token is valid
});
A "One roundtrip" approach that basically uses query strings during handshake. And whose main drawback is that the token is exposed in the URL, so it might be logged, or getting exposed.
I don't understand the way DNode uses websocket communication.
Some say it uses socket.io others say sockjs.
Which one is it? Or is it possible to choose?
I'm trying to use DNode, but I also need access to the connections for (semi-)broadcasting in reaction to RPC calls. How do I do this?
Is there a more extensive manual on dnode somewhere?
Your question is kind of vague. I'm not exactly sure whether DNode uses socket.io or sockjs, not sure it even uses one of those based on their dependencies list, but that is not really important when you program it.
As for using connections with DNode, it is pretty straight forward. Here's an example:
var server = dnode({
pushMessageNotification: function(message, cb) {
contact = getClientFromId(message.receiver);
contact.socket.emit('messageNotification', {
message: message.message,
sender: message.sender,
time: message.time
});
cb('success');
}
});
So as you can see, pushMessageNotification is a method that I binded with DNode-PHP and the message is encoded in JSON through PHP. Afterward, all you need is a method to find the socket of the client based on its id.
I'm using express framework and juggernaut. I looked at many examples about juggernaut, all examples using this method(client side):
var jug = new Juggernaut;
jug.subscribe("channel_name", function(data){
console.log("Got data: " + data);
});
Is it secure? I don't think so, everybody can subscribe to a channel from javascript console. Is there a way to subscribe a channel from server side?
Use security through obscurity, use a random channel name (HMAC).
I don't know how Juggernaut works, but I know that Faye supports extensions that can include authentication, so that subscribe operations require a valid token. See: http://faye.jcoglan.com/node.html
It would be worth checking whether Juggernaut supports a similar extension/intercept model.