I am wondering if this is the correct way to go about securing a socket.io room. Currently these are the steps that I am taking to ensure a user ends up in a private room, now I need to make sure the user gets authenticated before he is able to join.
A client connects to my socket.io server by calling io.connect and passing along the room he wants to join.
Client will be subscribed to the room and waits for emit of a message.
socket_write sends a json_encoded object to my server which holds a data property and the room to which this data will be sent.
My server receives the data through socket.on and emits the data to the given room where the client is listening.
The client will receive the proper information meant for him only.
My client code:
var socket = io.connect('http://localhost:8000');
socket.on('connect', function() {
socket.emit('room', window.location.href);
});
The server:
const server = net.createServer((socket) => {
// The event that fires on PHP's socket_write.
socket.on('data', (msg) => {
const data = JSON.parse(msg);
io.sockets.in(data.room).emit('log', data.html.toString().trim());
});
}).listen(5600);
The on connection event of my server:
io.on('connection', function (socket) {
// A client will trigger this event and pass their window.location.href(room) as room identifier.
socket.on('room', function(room) {
socket.join(room);
});
});
How I write to my socket.io server:
$data = [
'html' => 'test',
'room' => 'http://app.dev/stephan-v/app-test'
];
$json = json_encode($data);
socket_write($this->socket, $json, strlen($json));
This is my implementation of a private websocket message but of course this is not secure. If somebody knows the name of the room that person could easily listen in on the emitted messages.
Since the socket.join can only take place on my server I guess the only place to authentication is at that point?
So instead of simply passing along the room I guess I should also pass in data to authenticate the user. And at that point make an AJAX requests from my node server to my PHP backend?
Obviously I do not want to send a plain password and username over the wire so I have been reading up on JSON web tokens. I guess you need to:
send a JWT to the node server at the point of connection(along with which room to join, which I am currently doing).
Make an AJAX requests from my node.js server to my PHP backend on an authentication route.
Only let the user join the room if that user gets authenticated.
Sorry for the long post but there are a lot of components involved in this so I am hoping someone could confirm this workflow and perhaps give me some tips/pointers.
I would prefer not to install something like Redis at the moment because although it is a nice bridge between the frontend/backend it feels like overkill right now and I would like to understand this all at a more basic level.
Related
I am currently making an application that is using React.js on the front end and Node.js on the back end. During development, I discovered that I needed to use sockets in order to reflect real time changes to all of the users in a particular room. To do this, I am using this code on my Node.js backend:
io.emit("name", {
action: "action",
data: data
});
and then to receive the requests on my front end I am doing this:
import openSocket from "socket.io-client";
const socket = openSocket(url);
socket.on("name", (data) => {
... logic
});
While this code works, I am currently emitting information to all players that are connected to my socket. However, I only want to emit information to the players in a certain room. The part I dont understand is how to tell socket.io which users are in a certain room. I read the documentation and the concept of rooms seems to be only a server side thing, and to add a player to a room this code can be used:
io.on('connection', socket => {
socket.join('some room');
});
But the room name I want this particular connection to join is stored on my front end. Is there a way I can pass this data from the front end to the back end so that it can be used when 'connection' is made? If not, how do I get the roomID I need? Thanks!
In a Node JS app, I want my server to be able to send a notification to clients, in some specific cases. Searching the net on the subject, I find Service Workers which seems to be what I need; but I am not totally sure.
Here is why. When I look at various tutorials and videos, it seems like the notifications always somewhat originates from some client. But this is not what I want. It should be the server deciding when and what to send as a notification, not some client. So comes my questions:
Am I misunderstanding the way to use Service Workers?
Is this the right path to have my server send a notification to clients?
If the answer to the last question is NO. Then what is the proper way?
Like Dimitar wrote earlier, you could use socket.io to send messages from the server to the client and vice versa. This can be done as simple as that:
//Server sent message:
io.on('connection', (socket) => {
socket.emit('notificationToClient', 'Message'); //message sent from server to client
});
});
//Client receives the message:
const socket = io.connect('http://localhost:3000');
socket.on('notificationToClient', (data) => { //received message
console.log(data);
});
The websocket API is a little less confusing than socket.io but IMO, I'd go with socket.io since it already deals with a lot of the heavy lifting like re-connecting, custom namespaces, rooms etc. There are several fantastic udemy courses on socket.io.
See the socket.io documentation here: https://socket.io/docs/
Update 5/9/2020:
Michel - Since you asked about how to implement Socket.io in your code I'll give you an example of how I implemented this technology in my most recent project. I was working on a web app that connects to ELK Alarm systems and is able to arm/disarm these systems as well as receive sensor input (chime signals) at any remote location. The backend is Node.js and the UI is React.
//client side code
import React, { Component, Suspense } from 'react';
import panels from "./panels.js"
const socketConnection = io("http://localhost:5000"); //Socket.io
function App() {
function handleClick(panel) {
socketConnection.emit("panelData", panel); //Socket.io - sends the connection data for the ELK alarm panel to the express server
socketConnection.on('data', (data) => { //Socket.io - receives data from the server.
//does something with the data
})
}
}
//server side code
const express = require('express');
const elkClient = require('elk-client');
const app = express();
const server = app.listen('5000', () => {
console.log('------Server Running on 5000---------');
});
let io = new sio(server);
io.on("connection", (socket) => { //boilerplate code - establishes the "handshake" with the client.
socket.on("panelData", async (msg) => { //receives the ELK connection parameters from the client. See client side code
const client = new ElkClient({
connection: {
site: msg.name,
host: msg.host,
}
})
await client.connect();
client.on("message", (elkMessage) => { // This is NOT Socket.io - server is listening for emitted data from the alarm system - beam breaks, alarms, arm/disarm etc. [See event emitter][2]
if(elkMessage.messageType == 'A' && elkMessage.subMessageType == 'S') {
const armingStatusReport = {elkMessage};
socket.emit("data", armingStatusReport); //Socket.io - emits the received data to the client.
}
})
I tried to simplify the code above to hit the point. The server is using the connection parameters that it receives from the client to connect to a remote alarm system. The server then waits and listens for incoming data with client.on()(event emitter - not socket.io). As the data is received, I'm using Socket.io to send the received data back to the client with socket.emit(); Due to the nature of how alarm systems work, data is sent event driven so the fetch api wouldn't really fit the bill but socket.io (or websockets) does.
Let me know if I can help with anything else. With this recent project, I have spent the last few months EXTENSIVELY dealing with socket.io, including namespaces, rooms etc. I also had to create a real-time monitoring app for performance management for my server utilizing socket.io since I implemented clustering. Feel free to contact me anytime!
The answer is WebSockets.
Check out Socket.IO. It will give you the possibility of receiving live notifications on the client and you'll have complete server control on them.
What about using twilio library to manage your notification job .twilio library
To give an example, let's say a user sends some json to a server containing his socket id, and the server takes 5 seconds to respond. When it's ready to respond, it extracts the socket.id from the json object, and emits some data only to that socket.
If the user where to refresh (therefore changing the socket he is connecting via ) between sending the message and the server responding, how would I go about ensuring they still receives the data?
Server:
io.on("connection", function(socket) {
socket.on("data", function(data) {
var id = data.socket_id;
io.sockets.connected[id].emit("response", "sup");
// I know I could just do -> socket.emit("response", "sup") <- but im simply trying it out for learning purposes
})
})
Client:
socket.emit("data", {
username: "chris",
socket_id: socket.id,
});
socket.on("response", function(res) {
console.log(res);
})
If the user refreshes their page, that will make a new socket.io connection that will have a new and different socket.id. So, if you want to be able to identify a particular browser session across refreshes, then you need an identifier that survives a page refresh. That is not the socket.id.
Most likely, you would use some sort of long lasting userID in a cookie. If you don't already have any sort of authentication happening that gives you a userID in a cookie, then you can simply coin a unique ID in a cookie if there isn't already one anytime a user makes a request to your server. That will give you a unique userID in a cookie for every incoming socket.io connection (as long as cookies aren't disabled). When your socket.io connection connects, you can add it to a Map object you keep where the userID is the key and the current socket.id is the data and you can add the userID as a property on the socket. When the socket disconnects, you would then remove it from the Map object.
Then, when you want to save a reference to the user, you save the userID, not the socket.id and that reference stays the same across a page refresh. When you want to send to the userID, you can look it up in the Map object to get the current socket.id (which will be automatically kept current by your connect and disconnect event handlers as described above) and then use that to send to it.
I need to update some client side stuff if a user has a socket connection.
Currently I am broadcasting to everyone when a user connects he is "logged in"
Client side (could do it server side and get the same result)
// announce to everyone the user is online
socket.on('connect', function() {
socket.emit('im online');
});
Server side
io.sockets.on('connection', function (socket) {
socket.on("im online", function (data) {
// announce the online status of a new user
socket.broadcast.emit('connected user', {"name": socket.handshake.headers.user.username});
});
});
The problem is, now I load the page I let everyone know I am "online" because that loading of the page triggers the event, but if I am idol and another user reloads his/her page nothing is triggered on my end, so there is no indication I am online.
So I am thinking server side I can just capture an array of all the users who made it to the connection and send that array down to any new clients connecting and use that to compare to the users friend list to see if any username matches (usernames are 100% unique in each case BTW)
Then when a user disconnects I can just remove it from the array and let the client know.
This would be my solution, but it seems highly ineffective and I am hoping there is a better solution.
You could easily broadcast the disconnect events as well:
socket.on("disconnect", function (data) {
// announce the offline status of a new user
socket.broadcast.emit('disconnected user', {"name": socket.handshake.headers.user.username});
});
Th disconnect event is automatically generated on server side when the client disconnects.
On the other hand probably you have to store on server side all the connected users, because otherwise the newly connected users wouldn't be notified about the already connected others.
I'm looking for a Node.js module (or a suggestion/idea) so that my server can create/handle a 2-way communication between specific clients
In other words, I want to be able to transmit data coming from client X to client Y and vice versa.
I was looking into socket.io but it seems to work as a traditional web socket server that receives messages from all clients and then sends the same message to all clients. I might be wrong as I have no experience in this field.
In my case, I do want my server to be able to receive messages from different clients but I want the server to then forward each of those messages to a specific client instead.
e.g.
forward msg coming from client X to client Y only (and vice versa)
forward msg coming from client A to client B only (and vice versa)
etc.
I have zero experience with web sockets (I'm not even sure whether web sockets is what I'm looking for) but I do have experience with basic Node.js servers.
Thanks for any help!
In a Node.js WebSocket server, since you're coding the server, you have complete control over who gets what messages. You can do something like this...
When a client makes a WebSocket connection to the server and you store their connection object in an array, you can treat their connection's index in the array as their ID. If you want, you can broadcast this ID (and their IP address) to all other clients at that point to let them know who has connected. Then when a client wants to send a message to another client or clients, have the client doing the messaging include the destination client(s) along with the message. Here's the relevant parts of the server:
var client_list = [];
ws_server.on('request', function(request) {
var connection = request.accept(null, request.origin);
//let all other clients know that a new client has arrived
//send the new client's ID and IP address
for(var i = 0; i < client_list.length; i++) {
client_list[i].send(JSON.stringify({
type: "new client",
data: {
ID: client_list.length,
IP: connection.remoteAddress
}
}));
}
//store the new connection
client_list.push(connection);
//let the new client know what their ID is
connection.send(JSON.stringify({
type: "assignment",
data: {
ID: client_list.length - 1
}
}));
//when you receive a message from a client of type "relay",
//send it to the intended targets
connection.on("message", function(message) {
var parsed_message = JSON.parse(message.utf8Data);
if(parsed_message.type === "relay") {
for(var i = 0; i < parsed_message.targets.length; i++) {
client_list[parsed_message.targets[i]].send(parsed_message.data);
}
}
});
});
An outgoing message from a client would look like this:
{
type: "relay",
targets: [1, 33, 7],
data: {
content: "Hey guy!",
origin: my_id
}
}
I didn't test that, so let me know if it gives you trouble.
I am new to NodeJs too. Brief answer to your question
Socket.io is the way forward. You can isolate your client communication by connecting them on different ports.
client X to client Y only - port 1234
client A to client B only - port 1256
Regards
Socket.io will help you set up websockets between multiple clients and a server, so that you may synchronize data in real time between the connected instances. This is a client-server communication.
What I think you want is a WebRTC library, such as PeerJS. This one in particular sets up a handshake server using Node.js to bridge the connection between the two clients and let them communicate on their own from that point, for as long as the websocket lasts (i.e: a refresh will break the connection). This library can help you achieve client-client real time communication.
This Stack Overflow thread may help you further with a longer list of WebRTC libraries.