My Sockets work fine when a client has the token (provided by Laravel jwt) but, a need to work with the clients while they aren't authenticated yet, I Want something like:
io.sockets
.on('connection', socketioJwt.authorize({
secret: process.env.JWT_SECRET,
timeout: 15000 // 15 seconds to send the authentication message
}))
.on('authenticated', function(socket) {
console.log('Authenticated!!');
// This works:
socket.on('setDataRegistered', function(datos) {
console.log('Registering!');
});
})
// This doesn't works:
.on('config', function(socket) {
console.log('A client wants to be configured before authenticated!');
})
How can I call from the FrontEnd to 'config' (socket.emit('config')) before authenticate??
Thanks for your help. Sorry my English.
What I do is this:
io.on('connection', function (socket) {
// Client connected but not authenticated. Client has to emit to 'authenticate' for authentication
socket.on('authenticate', function (data, fn) {
// Authenticate socket.
// define more events
});
// still accessible to unauthorised sockets
socket.on("other-event", function(data) {
});
});
I don't use any middleware for authentication. But that's just me. I never found the use for it.
Related
I'm learning basics of Socket.io and I want to implement connection ONLY upon when user logs in. Currently, the offical documentation doesn't really seem to mention this kind of implementation. I'm using JWT for authentication and Node.js for server side.
Currently, Socket.io connects upon website visit:
const express = require('express');
const app = express();
const server = app.listen( process.env.PORT || 5555 );
const io = require('socket.io')(server, {
cors: {
origin: "*",
methods: ["GET", "POST"]
}
});
io.on('connection', socket => {
console.log('A client connected but not logged in yet.');
socket.on('disconnect', () => {
console.log('A client disconnected');
});
});
I want Socket.io to connect only when user logs in:
/** Example Logic **/
if (user_is_logged_in) {
const io = require('socket.io')(server, {
cors: {
origin: "*",
methods: ["GET", "POST"]
}
});
}
Best way to implement this? or is it even possible? Thanks.
The way to do this is check whether user is logged in or not after connect.
On the client
const jwt = getJWTFromLocalStorage(); // assume it returns undefined if user is not logged in
if (jwt) {
const socket = io("http://example.com", {
query: {
token: jwt
})
};
}
And on the server side
io.on('connection', socket => {
const token = socket.handshake.query.token; // jwt token passed from client
// authenticate
try {
if (!token) throw new Error("Token not found");
jwt.verify(token, yourJWTSecret)
} catch (err) {
// jwt verification failed
socket.emit("authFailed") // emits event to client to let client know authentication failed, optional.
socket.disconnect(); // disconnect client
}
socket.on('disconnect', () => {
console.log('A client disconnected');
});
});
Keep in mind that passing JWT from the URL query string is not a safe method, I recommend implementing something else for authentication like this
I am trying to make a game server with node.js, socket.io.
The basic idea likes below.
Initialize socket.io instance when the server starts
Store instance in global scope, so controllers can access it
When API calls, we trigger some socket.io event in the controller or some other points
Here is the implementation I made ...
First, in server.js - entry point
let GlobalVars = require('./state/GlobalVars');
const apiRouters = require('./router');
...
app.use('/api', apiRouters);
app.get('/', (req, res) => {
res.sendFile(`${__dirname}/test/simpleClient.html`)
});
const httpServer = http.createServer(app);
let socketIOInstance = socketIO(httpServer);
socketIOInstance.on('connection', (socket) => {
console.log('SOCKET.IO A USER CONNECTED');
socket.on('create', (data) => {
console.log('SOCKET.IO create called', socket);
socket.join(data.room);
socketIOInstance.emit('message', 'New people joined');
});
socket.on('join', (data) => {
console.log('SOCKET.IO join called', data);
})
socket.emit('message', 'Hi');
});
GlobalVars.socketIO = socketIOInstance;
// Add to global, so the controllers can manage own actions like create, join ...
httpServer.listen(port, () => {
console.log(`Server Listening on the port ${port}`);
})
...
When I access from a client, I am able to see SOCKET.IO A USER CONNECTED and Hi in the browser console.
Second, In api controller.
let GlobalVars = require('../state/GlobalVars');
...
router.post('/create', (req, res) => {
console.log('GenerateGameSokect');
let game = new Game();
let gameId = game.gameId;
// console.log('Global vars ', GlobalVars.socketIO);
GlobalVars.socketIO.emit('create', {
room: gameId
});
res.json({
result : 'SUCCESS',
game : game
})
});
I imported GlobalVars which contains socketIO instance. So what I expected was, socket create event triggered from the statement GlobalVars.socketIO.emit('create', Object) but could not find message in the server logs.
I got no clue what I was missing.
The final form I pursue is something like...
When user call create API, I creates socket connection and room
API will called in HTTP protocol, but in the API, the server publishes some events. - pubsub like.
Thanks for reading my questions b. Here is full source code till now(bitbucket public)
================== EDIT ====================
I got understood (maybe...)
The user-flow I wanted was ...
The client call API
(In the server) Checking validation in API and if valid emit to socket.io
If event accepted send new status to all clients
However, creating socket.io connection in the server looks strange for me, the solution is up to the client.
New user-flow I will change
The client call a validation API
If return is valid, the client emit socket.io event. This time server only do validation, not emit socket.io
In socket event, send new status to all other users
================== EDIT #2 ====================
This is a kind of conclusion. It looks I just misunderstanding the concept of socket communication. Like answer and replies say, Socket and HTTP are totally different channels, there is no way to connect both. (At least, without open new connection from http server to socket)
If this is wrong, you could add reply, Thanks
Now I understand you. Or at least I think!
Let's put it this way: there are two (asymetric) sides on a socket, server and client. What I called, respectively, "global manager" and "socket" in my comment to your post.
const server = require('socket.io')(yourHttpServer);
// client is installed as well when `npm i socket.io`
const client = require('socket.io-client')('http://localhost:' + yourServerPort);
// `socket` is the server side of the socket
server.on('connection', (socket) => {
// this will be triggered by client sides emitting 'create'
socket.on('create', (data) => {
console.log('a client socket just fired a "create" event!');
});
});
// this will be triggered by server side emitting 'create'
client.on('create', (data) => {
server.emit('create', {content: 'this will result in an infinite loop of "create" events!'});
});
In your /create route, when you GlobalVars.socketIO.emit('create', ...), the server-side socket handler isn't triggered, however if you have clients connected through a browser (or, like I showed above, if you connect a client socket directly from the server) then these will trigger their 'create' listener, if any.
Hope this helps you get on the right tracks!
I'm using socket.io 1.7.3 version. Server code:
io.on('connection', function (socket) {
console.log(socket.id); // CdnNBVe9ktJmMcb1AAAA
socket.to(socket.id).emit('something');
socket.emit('something'); // if I do it without to, it works but to all clients
console.log(socket.rooms); // { CdnNBVe9ktJmMcb1AAAA 'CdnNBVe9ktJmMcb1AAAA' }
});
Client:
<script src="/socket.io/socket.io.js"></script>
var socket = io.connect(..);
socket.on('connect', function() {
console.log(socket.id); // CdnNBVe9ktJmMcb1AAAA
socket.on('something', function() {
alert('it works');
});
});
Why it doesn't work? I'm not getting any alert although all console.logs seems to be correct.
To send a message to the particular client, you must provide socket.id of that client to the server and at the server side socket.io takes care of delivering that message by using,
socket.broadcast.to('ID').emit( 'send msg', {somedata : somedata_server} );
In your code
socket.broadcast.to(socket.id).emit('something');
I am trying to log the event name and parameter for each event on my Node server. For this purpose I used
io.use(function(socket, next){
// how to get event name out of socket.
});
Now, I got stuck while trying to get event name and arguments. To me, it looks like common demand from API dev, so I am pretty sure there must be some way to the library to get that, I have tried to read the docs and source but I am not able to get the stuff.
The socket events needs to be handled properly,in any case if an event is not handled there will be no response.
var io = require('socket.io')(server);
var sessionMiddleWare=(session({secret: 'secret key', resave: true, saveUninitialized: true,cookie: { path: '/', httpOnly: true, maxAge: 300000 },rolling: true}));
app.use(sessionMiddleWare)
io.use(function(socket, next) {
sessionMiddleWare(socket.request, socket.request.res, next);
});
io.on('connection', function(socket) { // On Socket connection.
// inside this you can use different events
//event name and parameters can be found in socket variable.
console.log(socket.id) // prints the id sent from the client.
console.log(socket.data) // prints the data sent from the client.
// example event
socket.on('subscribe', function(room) { // Event sample.
console.log('joining room', room);
socket.room=room;
socket.join(room);
});
})
Hope this helps.
Is it possible to run authentification as soon as socket is connected?
Right now I do this:
io.sockets.on('connection', function (socket) {
socket.on('login', function (token) {
// this is where I currently authtificate users
});
});
If I'm not wrong in this case socket opens on "connection" and hangs there and "login" event may be never called. If someone wants to attack my server, they can simply open thousands of socket connections. How can I authentificate on "connection", I mean right away? So if it fails I will be able to close socket immediately.
Thank you!
It is possible to run authentication when a client tries to connect. Socket.io has a mechanism of authorizing clients when they try to connect to your server, However authorization is disabled by default. There is an object of handshake when a socket tries to connect to server, this object has the request query and request headers etc. You can enforce authorization by this:
io.configure(function (){
io.set('authorization', function (handshakeData, callback) {
if(handshakeData.query.token == "somevalue"){
callback(null, true); // this will allow the client to connect
}else{
callback(null, false); // this will prevent the client from connecting
}
});
});
And on the client side you can send a query string like this:
var socket = io.connect("http://yourIP:Port?token=somevalue");