APN Feedback service does not send tokens - node.js

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.

Related

Send data to all clients upon internal event

I have a Node.js server that manages list of users. When new user is created, all the clients should display immediately the added user in the list.
I know how to send data to clients without request - using Websocket, but in this implementation, Websocket is not allowed.
Is it possible to update all the client's user-list without using Websocket, when new user is added in the server?
// Client side
const subscribe = function(callback) {
var longPoll = function() {
$.ajax({
method: 'GET',
url: '/messages',
success: function(data) {
callback(data)
},
complete: function() {
longPoll()
},
timeout: 30000
})
}
longPoll()
}
// Server Side
router.get('/messages', function(req, res) {
var addMessageListener = function(res) {
messageBus.once('message', function(data) {
res.json(data)
})
}
addMessageListener(res)
})
Long polling is where the client requests new data from the server, but the server does not respond until there is data. In the meantime, the client has an open connection to the server and is able to accept new data once the server has it ready to send.
Ref: http://hungtran.co/long-polling-and-websockets-on-nodejs/
There is a third way: Push Notifications
Your application should register in a Push Notification Server (public or proprietary) and then your server will be able to send messages asynchronously
You can use server-sent events with an implementation like sse-express:
// client
let eventSource = new EventSource('http://localhost:80/updates');
eventSource.addEventListener('connected', (e) => {
console.log(e.data.welcomeMsg);
// => Hello world!
});
// server
let sseExpress = require('./sse-express');
// ...
app.get('/updates', sseExpress, function(req, res) {
res.sse('connected', {
welcomeMsg: 'Hello world!'
});
});

Sending a login event from server to client

I am trying to set up a chat interface on a site using nodejs.
I need to update the logged_in_user list after a user is authenticated.
I am using this code as a reference
https://github.com/amirrajan/nodejs-chat
io.sockets.on('connection', (socket) => {
socket.on('sendchat', (data) => {
io.sockets.emit('updatechat', socket.username, data);
});
As you can see on the 'connection' event the updatechat event is triggered.
In the client side (public/app.js),the chatlist is updated on getting the updatechat event.
I need to do the same but after a client is authenticated.
Can I somehow tweak the socket object to trigger the event on a 'login' event
This is my code
app.get('/authenticate',function(request,response)
{
var db = couch.db.use('users');
var email = request.query['email'];
var pass = request.query['password'];
db.get(email,{email:email},function(err,body){
if(err)
console.log(err);
else
{
var user_pass = body['password'];
var name = body['name'];
console.log(user_pass);
if(pass == user_pass)
{
eventEmitter.emit('login',name);
response.render('pages/dashboard',{user:body['name']});
}
}
});
// And on 'login' event
eventEmitter.on('login', function(name)
{
// ?????? Broadcast the new user to all logged in users to update the chatlist
});
I have tried to redirect the logged in users to another port and use the socket 'connection' event there.It sort of works but I want to know if there is another approach I could use.
You should use socket.io middleware for authentication.
io.use(function(socket,next){
//auth here
});
Take a look at the docs http://socket.io/docs/server-api/
You can get the req and res objects from the socket object being passed to the middleware.
Note, it is not a great idea to pass a password on the query since the url is not pass ssl.

Anyone using Node.js with Amazon SNS and Apple Push Notifications?

I'm looking for examples of using node.js with Amazon SNS and Apple APN push notifications. We use Amazon for our hosting, and I have used SNS before, it's pretty simple. But the examples they have for push notifications are for java, and there is no examples for Node. It's confusing, as usual with them, and I'm hoping to cut my research and time spent short. It can't be that hard. I'm also wondering how they deal with errors, and the differences between the sandbox and production. Apple reacts differently between the two environments, not failing in the sandbox as they do in production.
It ends up not being that hard, just figuring out the documentation was unpleasant. You need to create the main endpoint for the SNS topic in the console, by far the easiest way, including the loading of the certificate. You then used createPlatformEnpoint to create an endpoint for each device id. That returns another SNS topic, specific fo that device, that you then use to send the message.
So, the following works to send a single message to a single client. If you want send something en masse, not sure you can do that. Also not sure how you deal with Apple's feedback, which you are supposed to check for failed sends.
config = require("./config.js").config;
var token = "1234567898123456789";
var AWS = require('aws-sdk');
AWS.config.update({accessKeyId: config.AWSAccessKeyId, secretAccessKey: config.AWSSecretKey});
AWS.config.update({region: config.AWSRegion});
var sns = new AWS.SNS();
var params = {'PlatformApplicationArn':config["AWSTargetARN"],'Token':token};
var message = 'Test';
var subject = 'Stuff';
sns.createPlatformEndpoint(params,function(err,EndPointResult)
{
var client_arn = EndPointResult["EndpointArn"];
sns.publish({
TargetArn: client_arn,
Message: message,
Subject: subject},
function(err,data){
if (err)
{
console.log("Error sending a message "+err);
}
else
{
console.log("Sent message: "+data.MessageId);
}
});
});
It's fairly straightforward as CargoMeister pointed out.
I've written a blog post about getting it setup check it out here http://evanshortiss.com/development/mobile/2014/02/22/sns-push-notifications-using-nodejs.html
I've also a Node.js wrapper module that is easier to use than the AWS SDK as I've worked around the documentation. It supports iOS and Android Push Services (as that's all I've tested/worked with), manages message formats other than Strings and exposes events: https://npmjs.org/package/sns-mobile
I haven't used topics to manage endpoints, not sure is that an issue though. You just create PlatformEndpoints first via the SNS console.
var AWS = require('aws-sdk');
var express = require('express');
var app = express();
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'add IdentityPoolId'
});
AWS.config.region = 'add region';
var sns = new AWS.SNS();
sns.createPlatformEndpoint({
PlatformApplicationArn: 'add platform application arn',
Token: 'add device token'
}, function (err, data) {
if (err) {
console.log("errorMessage" + err.stack);
return;
}
var endpointArn = data.EndpointArn;
var payload = {
default: 'Hello World',
APNS: {
aps: {
alert: 'Hello World',
sound: 'default',
badge: 1
}
}
};
// first have to stringify the inner APNS object...
payload.APNS = JSON.stringify(payload.APNS);
// then have to stringify the entire message payload
payload = JSON.stringify(payload);
console.log('sending push');
sns.publish({
Message: payload,
MessageStructure: 'json',
TargetArn: endpointArn
}, function (err, data) {
if (err) {
console.log(err.stack);
return;
}
console.log('push sent');
console.log(data);
});
});
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
})

socket.io is disconnecting all other connected users in my node twitter app

When I open two browser windows in incognito mode (different sessions).
I get a stream for one, which works fine...new tweets come in as expected from the twitter stream api. The problem is that when the other window loads the page, the first incognito window gets disconnected.
Here is the code I'm using. I'm not sure if twitter might not allow simultaneous streams or if its something with my logic.
var mod = module.exports = {}
, twitter = require('twitter')
, c = console;
var io
, sock
, intv = {};
function onNewsInit(data){
setTimeout(function(){
stream(streamQuery);
}, 1000 * 5);
}
function stream(query){
c.log('stream query', query);
twit.stream('statuses/filter', {track:query}, function(stream) {
stream.on('data', function (data) {
c.log(data);
var item = getItem(query, data);
c.log('stream item', item);
if ( item ) {
sock.emit('twitter:item', { item: item });
}
});
stream.on('end', function (response) {
// Handle a disconnection
c.log('stream end');
});
stream.on('destroy', function (response) {
// Handle a 'silent' disconnection from Twitter, no end/error event fired
c.log('stream destroy');
});
// Disconnect stream after five seconds
sock.on('news:end', stream.destroy);
sock.on('disconnect', stream.destroy);
});
}
mod.register = function register(soc, sio) {
c.log('news register');
io = sio;
sock = soc;
sock.on('news:init', onNewsInit);
sock.on('news:end', onNewsEnd);
};
app.js
io.sockets.on('connection', function(sock){
news.register(sock, io);
});
Is there any reason one client would be able to disconnect the other?
It turns out the twitter stream API only allows one simultaneous connection.
As described here:
https://dev.twitter.com/docs/streaming-apis/streams/public
Connections
Each account may create only one standing connection to the public
endpoints, and connecting to a public stream more than once with the
same account credentials will cause the oldest connection to be
disconnected.
Clients which make excessive connection attempts (both successful and
unsuccessful) run the risk of having their IP automatically banned.

NodeJS xmpp server

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);

Resources