I make the first steps in the node js and xmpp
I need to run at xmpp server on node js for messaging
Here's the process:
I use node-xmpp server https://github.com/astro/node-xmpp
run the example of a server (/examples/c2s.js)
join to server with two clients (clients tested on other servers jabber - it works and the messages are sending through)
Clients have authorization on my server.
But when I send a message from one client to another, the message comes to the server (I see it in the logs)
and that was the message does not come to recipient
I don `t know where to look for the problem
server configuration ? routing ? messaging may be necessary to add yourself ?
help me plz
my server code (by examples)
var xmpp = require('../lib/node-xmpp');
var c2s = new xmpp.C2SServer({
port: 5222,
domain: 'localhost'
});
// On Connect event. When a client connects.
c2s.on("connect", function(client) {
c2s.on("register", function(opts, cb) {
console.log("REGISTER");
cb(true);
});
client.on("authenticate", function(opts, cb) {
console.log("AUTH" + opts.jid + " -> " +opts.password);
cb(null);
});
client.on("online", function() {
console.log("ONLINE");
client.send(new xmpp.Message({ type: 'chat' }).c('body').t("Hello there, little client."));
});
client.on("stanza", function(stanza) {
console.log("STANZA" + stanza);
});
client.on("disconnect", function(client) {
console.log("DISCONNECT");
});
});
I run a server and connect to it by this code
var xmpp = require('../lib/node-xmpp');
var argv = process.argv;
if (argv.length < 6) {
console.error('Usage: node send_message.js <my-jid> <my-password> <my-text> <jid1> [jid2] ... [jidN]');
process.exit(1);
}
var cl = new xmpp.Client({ jid: argv[2], password: argv[3] });
cl.addListener('online',
function() {argv.slice(5).forEach(
function(to) {cl.send(new xmpp.Element('message', { to: to,type: 'chat'}).c('body').t(argv[4]));
});
// nodejs has nothing left to do and will exit
// cl.end();
});
cl.addListener('stanza',
function(stanza) {
if (stanza.is('message') &&
// Important: never reply to errors!
stanza.attrs.type !== 'error') {
console.log("New message");
// Swap addresses...
stanza.attrs.to = stanza.attrs.from;
delete stanza.attrs.from;
// and send back.
cl.send(stanza);
}
});
cl.addListener('error',
function(e) {
console.error(e);
process.exit(1);
});
The short answer: change cb(null) to cb(null, opts).
The long answer:
client.on("authenticate", function(opts, cb) {...}) registers what the server will do when the client tries to authenticate itself. Inside node-xmpp, it will look for the authentication mechanism first and the mechanism will then call the callback and retrieve the authentication results via cb.
By default, the Plain authentication is used. You can check out how it works here: https://github.com/node-xmpp/node-xmpp-server/blob/master/lib/authentication/plain.js. With Plain the opts stores the jid and password.
Then to inform node-xmpp that authentication failed/sucessed, we need to look into mechanism, https://github.com/node-xmpp/node-xmpp-server/blob/master/lib/authentication/mechanism.js, inherited by Plain.
this.authenticate(this.extractSasl(auth), function (err, user) {
if (!err && user) {
self.success(user)
} else {
self.failure(err)
}
})
Here, cb requires two parameters. When err is null and user is non-null, it indicates authentication successes. Otherwise, failed.
I am no expert on neither node.js nor xmpp. But reading your code. I assume the "stanza" is the event where a client sent a message. You asked it to log the message, but you gave no instructions on how to route it to the recipient. You should break down the received message on the server into message body and recipient, and ask your server to send it to the recipient.
Alex you have used C2SServer which connects a stream between a server and a client. When you send a message from one client to another they get to server. Now its responsibility of the sever to relay them back to actual receiver.
One possible solution is to keep client object is a global object corresponding to their jids when client is authenticated, when you get a message for that client you can get that from global variable and route the message to actual client kept in global variable.
You can get the text message & receiver JID from server. Just break the stanza in following ways and put this before error listeners:-
cl.on('stanza', function(stanza) {
if (stanza.is('message') && (stanza.attrs.type !== 'error')) {
var body = stanza.getChild('body');
if (!body) {
return;
}
console.log(stanza.attrs.from+" Says: "+body.children[0]);
}
});
In "authenticate", may an argument not be enough for a callback?
NG:
cb(null);
OK:
cb(null, opts);
Related
So I have an application running node js with socket.io as a backend and normal javascript as frontend. My application has a login system which currently simply has the client send its login data as soon as it's connected.
Now I figured it would be much nicer to have the login data sent along with the handshakeData, so I can directly have the user logged in while connecting (instead of after establishing a connection) respectively refuse authorization when the login data is invalid.
I'm thinking it would be best to put my additional data in the header part of the handshakeData, so any ideas how I could do that? (Without having to modify socket.io if possible, but if it's the only way I can live with it)
As a lot of comments have pointed out below the Socket.IO API changed in their 1.0 release. Authentication should now be done via a middleware function, see 'Authentication differences' # http://socket.io/docs/migrating-from-0-9/#authentication-differences. I'll include my orginal answer for anyone stuck on <1.0 as the old docs seem to be gone.
1.0 and later:
Client Side:
//The query member of the options object is passed to the server on connection and parsed as a CGI style Querystring.
var socket = io("http://127.0.0.1:3000/", { query: "foo=bar" });
Server Side:
io.use(function(socket, next){
console.log("Query: ", socket.handshake.query);
// return the result of next() to accept the connection.
if (socket.handshake.query.foo == "bar") {
return next();
}
// call next() with an Error if you need to reject the connection.
next(new Error('Authentication error'));
});
Pre 1.0
You can pass a query: param in the second argument to connect() on the client side which will be available on the server in the authorization method.
I've just been testing it. On the client I have:
var c = io.connect('http://127.0.0.1:3000/', { query: "foo=bar" });
On the server:
io.set('authorization', function (handshakeData, cb) {
console.log('Auth: ', handshakeData.query);
cb(null, true);
});
The output on the server then looked like:
:!node node_app/main.js
info - socket.io started
Auth: { foo: 'bar', t: '1355859917678' }
Update
3.x and later
You can pass an authentication payload using the auth param as the second argument to connect() in the client side.
Client Side:
io.connect("http://127.0.0.1:3000/", {
auth: {
token: "AuthToken",
},
}),
In server side you can access it using socket.handshake.auth.token
Server Side:
io.use(function(socket, next){
console.log(socket.handshake.auth.token)
next()
});
This has now been changed in v1.0.0. See the migration docs
basically,
io.set('authorization', function (handshakeData, callback) {
// make sure the handshake data looks good
callback(null, true); // error first, 'authorized' boolean second
});
becomes :
io.use(function(socket, next) {
var handshakeData = socket.request;
// make sure the handshake data looks good as before
// if error do this:
// next(new Error('not authorized');
// else just call next
next();
});
For socket.io v1.2.1 use this:
io.use(function (socket, next) {
var handshake = socket.handshake;
console.log(handshake.query);
next();
});
This my code for sending query data to nodejs and server.io server client.
var socket = io.connect(window.location.origin, { query: 'loggeduser=user1' });
io.sockets.on('connection', function (socket) {
var endp = socket.manager.handshaken[socket.id].address;
console.log("query... " + socket.manager.handshaken[socket.id].query.user);
}
Perhaps the api has changed but I did the following to get extra info to the server.
// client
io.connect('localhost:8080', { query: 'foo=bar', extra: 'extra'});
// server
io.use(function(sock, next) {
var handshakeData = sock.request;
console.log('_query:', handshakeData._query);
console.log('extra:', handshakeData.extra);
next();
});
prints
_query: { foo: 'bar',
EIO: '3',
transport: 'polling',
t: '1424932455409-0' }
extra: undefined
If anyone knows how to get data from a client to the server through the handshake that is not in the query params let me know please.
Update I ran into issues later with this syntax
io.connect('localhost:8080?foo=bar');
is what I'm currently using.
Old thread but assuming you store your jwt token/session id in session cookies (standard stuff) this gets passed to the server by default anyway when doing handshake (socket.io-client) I've noticed.
Is there anything wrong with just getting the auth information for the handshake (via middleware or on.connection) via cookie?
eg.
io.on('connection', function(socket) {
// assuming base64url token
const cookieStr = socket.handshake.headers.cookie
const matchRes =
cookieStr == null
? false
: cookieStr.match(/my-auth-token=([a-zA-Z0-9_.-]+)/)
if (matchRes) {
// verify your jwt...
if ( tokenIsGood(matchRes[1]) {
// handle authenticated new socket
} else {
socket.emit('AUTH_ERR_LOGOUT')
socket.disconnect()
}
} else {
socket.emit('AUTH_ERR_LOGOUT')
socket.disconnect()
}
}
I'm using this now for a project and it's working fine.
I found a little problem to see the .loggeduser
io.sockets.on('connection', function (socket) {
var endp = socket.manager.handshaken[socket.id].address;
console.log("query... " + socket.manager.handshaken[socket.id].query.loggeduser);
// ↑ here
}
I am still learning node.js basics. My flow is like this,
browser<-->node<-->backend server doing calculation.
node and backend uses socket to communicate.
From the browser there are start/stop buttons to ask backend to start/stop the
calculation.
When node asks backend to start/stop, it must query to see if backend is
alive first.
My code is like this -
app.get('/stopCmd', function(req, res)
{
socketToBackendServer.write("status", function() {
console.log("Sending:", 'Node asking for STATUS');
});
socketToBackendServer.on("data", function() {
if(status is ok) // pseudo code
{
socketToBackendServer.write("stop", function() {
console.log("Sending:", 'Node sending STOP');
});
} else {
console.log("backend server is NOT ready");
}
});
});
app.get('/startCmd', function(req, res)
{
// do similar things as stopCmd
});
/////////////////////////////////////////////////
var socketToBackendServer = net.connect(2899);
function openSocket() {
socketToBackendServer.setKeepAlive(true);
socketToBackendServer.on('connect', onConnect.bind({}, socketToBackendServer));
socketToBackendServer.on('error', onError.bind({}, socketToBackendServer));
}
function onConnect(socket) {
var myData;
console.log('Socket is open!');
socket.on('data', function(data) {
console.log('Received:', data);
io.emit('time', { time: data.toJSON() });
});
}
function onError(socket) {
console.log('Socket error!');
// Kill socket
clearInterval(interval);
socket.destroy();
socket.unref();
// Re-open socket
setTimeout(openSocket, 1e3);
}
openSocket();
server.listen(7778);
if using the same browser, if i go crazy clicking start/stop... for the "
stopCmd", how to make sure when it queries "status", the response is caught
by its function, not "startCmd"'s ?
it's this line
socketToBackendServer.on("data", function()
Thank you again !
You can use multiple connections to the backend server, so one function can freely use one channel, the responses won't mix.
Or you can use a multiplexer function, that you call from both of your functions:
It could work if you can identify your requests, like you send and id with the status, for example socketToBackendServer.write("status 1", ... , and you send the id with the status response back from the backend server (if it yours). In this way you can send multiple requests at the same time, and when the response come, you can identify it, and call the callback function that you stored in an array with the ids.
You only send one request, and you wait for the response before you send another one. You must use a waiting queue, where you store the request, and the callback functions.
i want to send datas in same client (not all clients) with this code;
app.post("/search", function(req, res) {
//some other codes
//if database saved {
io.sockets.emit('preview-post', {title: "blabla" });// io variable was declared as GLOBAL
// } database saved end.
res.send({bla:bla});// response end before database saving process
});
this sample is working ! but it sends to all clients , How can i emit data to same opened browser(same client) ?
Second Question is: Are there any alternative ways to do this scenario?
My algorithm is post method fired > async call to an api > response end and page loaded on client > async call to an api is still continue > if async call is finished > send alert to client . But How? i wanted to do it wiht socket .io , if i use your 3.part , it'll work , can i do this scenario any other way?
This indeed sends to all sockets.
There are a couple ways to achieve what you are looking to do. The first way is to do something like this on the server:
io.on('connection', function(socket) {
socket.on('search', function(*/ client supplied arguments /*){
socket.emit('preview-post', {title: "blabla" });
});
});
If you are insistent on using a post request, and then sending it back to the client, there are two ways to achieve this.
The easiest way, if you only need to respond to this response, is just send a standard response from node, and let the client handle it:
res.send({
event: 'preview-post',
payload: {title: "blabla" }
});
This removes socket.io's event system, so if you are insistent on using socket.io to send this event back to the same client, you are going to need to use cookies. Express and the module cookie-parser make this easy for you.
Once you have this setup, inside your request you could do something like this:
app.post("/search", function(req, res) {
var socket = findSocketByCookie(req.cookies.myUniqueCookie);
socket.emit('preview-post', {title: "blabla" });
});
function findSocketByCookie(cookie) {
for(var i in io.sockets.connected) {
var socket = io.sockets.connected[i];
if(socket.handshake.headers.cookie.indexOf(cookie) !== -1){
return socket;
}
}
}
I implemented a node.js script that queries the APN Feedback service to retrieve the list of invalid tokens. Unfortunately, I did not manage to get any invalid token. I followed these steps:
Install the ios app with push notifications in sandbox mode.
Send some notifications to the app (done successfully).
Uninstall the app (I read that if the app that I uninstall is the only one with push notifications, it will cause the disconnection from the APN Service and make impossible to notify it that the app was uninstalled; but this is not my case, the iPad has many push notification apps installed!!).
Send many other notifications with the same token, about ten or twenty, just to prove that the application token is not valid anymore (obviously the notifications are not delivered because the app has just been uninstalled).
Query the Feedback service to finally get the invalid token. The
Feedback service does not send anything, it just closes the connection without any kind of data.
This is the script I use to query the feedback service:
function pollAPNFeedback() {
var certPem = fs.readFileSync('apns-prod-cert.pem', encoding='ascii');
var keyPem = fs.readFileSync('apns-prod-key-noenc.pem', encoding='ascii');
var options = { key: keyPem, cert: certPem };
console.log("Connecting APN feedback service");
var stream = tls.connect(2196, 'feedback.sandbox.push.apple.com', options, function() {
if (stream.authorized == false) {
return console.log('not connected')
} else {
console.log('connected');
};
var bufferlist = [];
stream.on('data', function(data) {
// APN feedback starts sending data immediately on successful connect
console.log('-->Data: ', data);
//bufferlist.push(data);
});
stream.on('readable', function(){
console.log('we have incoming data');
});
stream.on('error', function(err){
console.log('error: ', err);
});
stream.on('close', function(){
console.log('closed');
console.log('stream.data = ', stream.data);
});
});
}
As you can see, I put some listeners on the stream variable. The callback function on the 'data' listener is never invoked, only the 'close' event triggers its callback. I am sure that the connection is up because stream.authorized is true.
What am I doing wrong?
Is it possible that you are using a production certificate to contact the sandbox environment?
From your code :
var certPem = fs.readFileSync('apns-prod-cert.pem', encoding='ascii');
var keyPem = fs.readFileSync('apns-prod-key-noenc.pem', encoding='ascii');
And :
var stream = tls.connect(2196, 'feedback.sandbox.push.apple.com', options, function()
If that's the case, that's why it doesn't work.
So I have an application running node js with socket.io as a backend and normal javascript as frontend. My application has a login system which currently simply has the client send its login data as soon as it's connected.
Now I figured it would be much nicer to have the login data sent along with the handshakeData, so I can directly have the user logged in while connecting (instead of after establishing a connection) respectively refuse authorization when the login data is invalid.
I'm thinking it would be best to put my additional data in the header part of the handshakeData, so any ideas how I could do that? (Without having to modify socket.io if possible, but if it's the only way I can live with it)
As a lot of comments have pointed out below the Socket.IO API changed in their 1.0 release. Authentication should now be done via a middleware function, see 'Authentication differences' # http://socket.io/docs/migrating-from-0-9/#authentication-differences. I'll include my orginal answer for anyone stuck on <1.0 as the old docs seem to be gone.
1.0 and later:
Client Side:
//The query member of the options object is passed to the server on connection and parsed as a CGI style Querystring.
var socket = io("http://127.0.0.1:3000/", { query: "foo=bar" });
Server Side:
io.use(function(socket, next){
console.log("Query: ", socket.handshake.query);
// return the result of next() to accept the connection.
if (socket.handshake.query.foo == "bar") {
return next();
}
// call next() with an Error if you need to reject the connection.
next(new Error('Authentication error'));
});
Pre 1.0
You can pass a query: param in the second argument to connect() on the client side which will be available on the server in the authorization method.
I've just been testing it. On the client I have:
var c = io.connect('http://127.0.0.1:3000/', { query: "foo=bar" });
On the server:
io.set('authorization', function (handshakeData, cb) {
console.log('Auth: ', handshakeData.query);
cb(null, true);
});
The output on the server then looked like:
:!node node_app/main.js
info - socket.io started
Auth: { foo: 'bar', t: '1355859917678' }
Update
3.x and later
You can pass an authentication payload using the auth param as the second argument to connect() in the client side.
Client Side:
io.connect("http://127.0.0.1:3000/", {
auth: {
token: "AuthToken",
},
}),
In server side you can access it using socket.handshake.auth.token
Server Side:
io.use(function(socket, next){
console.log(socket.handshake.auth.token)
next()
});
This has now been changed in v1.0.0. See the migration docs
basically,
io.set('authorization', function (handshakeData, callback) {
// make sure the handshake data looks good
callback(null, true); // error first, 'authorized' boolean second
});
becomes :
io.use(function(socket, next) {
var handshakeData = socket.request;
// make sure the handshake data looks good as before
// if error do this:
// next(new Error('not authorized');
// else just call next
next();
});
For socket.io v1.2.1 use this:
io.use(function (socket, next) {
var handshake = socket.handshake;
console.log(handshake.query);
next();
});
This my code for sending query data to nodejs and server.io server client.
var socket = io.connect(window.location.origin, { query: 'loggeduser=user1' });
io.sockets.on('connection', function (socket) {
var endp = socket.manager.handshaken[socket.id].address;
console.log("query... " + socket.manager.handshaken[socket.id].query.user);
}
Perhaps the api has changed but I did the following to get extra info to the server.
// client
io.connect('localhost:8080', { query: 'foo=bar', extra: 'extra'});
// server
io.use(function(sock, next) {
var handshakeData = sock.request;
console.log('_query:', handshakeData._query);
console.log('extra:', handshakeData.extra);
next();
});
prints
_query: { foo: 'bar',
EIO: '3',
transport: 'polling',
t: '1424932455409-0' }
extra: undefined
If anyone knows how to get data from a client to the server through the handshake that is not in the query params let me know please.
Update I ran into issues later with this syntax
io.connect('localhost:8080?foo=bar');
is what I'm currently using.
Old thread but assuming you store your jwt token/session id in session cookies (standard stuff) this gets passed to the server by default anyway when doing handshake (socket.io-client) I've noticed.
Is there anything wrong with just getting the auth information for the handshake (via middleware or on.connection) via cookie?
eg.
io.on('connection', function(socket) {
// assuming base64url token
const cookieStr = socket.handshake.headers.cookie
const matchRes =
cookieStr == null
? false
: cookieStr.match(/my-auth-token=([a-zA-Z0-9_.-]+)/)
if (matchRes) {
// verify your jwt...
if ( tokenIsGood(matchRes[1]) {
// handle authenticated new socket
} else {
socket.emit('AUTH_ERR_LOGOUT')
socket.disconnect()
}
} else {
socket.emit('AUTH_ERR_LOGOUT')
socket.disconnect()
}
}
I'm using this now for a project and it's working fine.
I found a little problem to see the .loggeduser
io.sockets.on('connection', function (socket) {
var endp = socket.manager.handshaken[socket.id].address;
console.log("query... " + socket.manager.handshaken[socket.id].query.loggeduser);
// ↑ here
}