Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@superhuman/push-receiver

Package Overview
Dependencies
Maintainers
9
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@superhuman/push-receiver - npm Package Compare versions

Comparing version
1.2.0
to
2.1.2
+25
example/index.js
const { register, listen } = require('../src');
const senderId = require('yargs').argv.senderId;
if (!senderId) {
console.error('Missing senderId');
return;
}
(async () => {
// First time
// Register to GCM and FCM
const credentials = await register(senderId); // You should call register only once and then store the credentials somewhere
const fcmToken = credentials.fcm.token; // Token to use to send notifications
console.log('Use this following token to send a notification', fcmToken);
// persistentIds is the list of notification ids received to avoid receiving all already received notifications on start.
const persistentIds = []; // get all previous persistentIds from somewhere (file, db, etc...)
await listen({ ...credentials, persistentIds }, onNotification);
})();
// Called on new notification
function onNotification({ notification }) {
// Do someting with the notification
console.log('Notification received');
console.log(notification);
}
const request = require('request-promise');
const { SENDER_ID, SERVER_KEY } = require('./keys');
const { register, listen } = require('../src/index');
const NOTIFICATIONS = {
SIMPLE : { title : 'Hello world ', body : 'Test' },
LARGE : { title : 'Hello world ', body : require('./4kb') },
};
let credentials;
let client;
describe('Parser', function() {
beforeEach(async function() {
credentials = await register(SENDER_ID);
});
afterEach(async function() {
client.destroy();
credentials = null;
});
it('should receive a simple notification', async function() {
await send(NOTIFICATIONS.SIMPLE);
const notifications = await receive(1);
expect(notifications.length).toEqual(1);
expect(notifications[0].notification.notification).toEqual(
NOTIFICATIONS.SIMPLE
);
});
it('should receive a large notification', async function() {
await send(NOTIFICATIONS.LARGE);
const notifications = await receive(1);
expect(notifications.length).toEqual(1);
expect(notifications[0].notification.notification).toEqual(
NOTIFICATIONS.LARGE
);
});
it('should receive multiple notifications', async function() {
await send(NOTIFICATIONS.SIMPLE);
await send(NOTIFICATIONS.LARGE);
await send(NOTIFICATIONS.SIMPLE);
const notifications = await receive(3);
expect(notifications.length).toEqual(3);
expect(notifications[0].notification.notification).toEqual(
NOTIFICATIONS.SIMPLE
);
expect(notifications[1].notification.notification).toEqual(
NOTIFICATIONS.LARGE
);
expect(notifications[2].notification.notification).toEqual(
NOTIFICATIONS.SIMPLE
);
});
});
async function send(notification) {
const response = await request({
method : 'POST',
url : 'https://fcm.googleapis.com/fcm/send',
json : true,
body : {
to : credentials.fcm.token,
notification : notification,
},
headers : { Authorization : `key=${SERVER_KEY}` },
});
try {
expect(response.success).toEqual(1);
} catch (e) {
throw new Error(
`sending of notification failed: ${JSON.stringify(response)}`
);
}
return response;
}
async function receive(n) {
const received = [];
return new Promise(async resolve => {
const onNotification = notification => {
received.push(notification);
if (received.length === n) {
resolve(received);
}
};
credentials.persistentIds = [];
client = await listen(credentials, onNotification);
});
}
+22
-9
{
"name": "@superhuman/push-receiver",
"version": "1.2.0",
"description": "A module to subscribe to GCM/FCM and receive notifications within a node process.",
"version": "2.1.2",
"description":
"A module to subscribe to GCM/FCM and receive notifications within a node process.",
"main": "src/index.js",

@@ -10,8 +11,18 @@ "scripts": {

"send": "node scripts/send",
"pretty": "prettier-eslint --single-quote --trailing-comma es5 --write \"**/*.js\" \"**/*.json\"",
"pretty:check": "prettier-eslint --single-quote --trailing-comma es5 --list-different --log-level silent \"**/*.js\" \"**/*.json\"",
"pretty":
"prettier-eslint --single-quote --trailing-comma es5 --write \"**/*.js\" \"**/*.json\"",
"pretty:check":
"prettier-eslint --single-quote --trailing-comma es5 --list-different --log-level silent \"**/*.js\" \"**/*.json\"",
"lint": "eslint 'src/**/*.js'",
"lint:fix": "eslint 'src/**/*.js' --fix",
"test": "mocha test/*_test.js"
"test": "jest",
"precommit": "lint-staged"
},
"lint-staged": {
"*.js": [
"eslint --fix",
"prettier-eslint --single-quote --trailing-comma es5 --write"
],
"*.json": ["prettier-eslint --single-quote --trailing-comma es5 --write"]
},
"repository": {

@@ -42,3 +53,5 @@ "type": "git",

"http-proxy": "^1.16.2",
"mocha": "^5.0.0",
"husky": "^0.14.3",
"jest": "^22.2.2",
"lint-staged": "^6.1.0",
"prettier": "^1.6.1",

@@ -52,7 +65,7 @@ "prettier-eslint": "^7.0.0",

"long": "^3.2.0",
"protobufjs": "^6.8.6",
"request": "^2.88.0",
"request-promise": "^4.2.4",
"protobufjs": "^6.8.0",
"request": "^2.81.0",
"request-promise": "^4.2.1",
"uuid": "^3.1.0"
}
}

@@ -7,5 +7,10 @@ const EventEmitter = require('events');

const tls = require('tls');
const {checkIn} = require('./gcm');
const {kMCSVersion, kLoginRequestTag, kDataMessageStanzaTag, kLoginResponseTag} = require('./constants');
const {load} = require('protobufjs');
const { checkIn } = require('./gcm');
const {
kMCSVersion,
kLoginRequestTag,
kDataMessageStanzaTag,
kLoginResponseTag,
} = require('./constants');
const { load } = require('protobufjs');

@@ -19,3 +24,3 @@ const HOST = 'mtalk.google.com';

module.exports = class Client extends EventEmitter {
static async init () {
static async init() {
if (proto) {

@@ -27,6 +32,7 @@ return;

constructor(credentials, persistentIds) {
constructor(credentials, { persistentIds, socketTimeout } = {}) {
super();
this._credentials = credentials;
this._persistentIds = persistentIds || [];
this._socketTimeout = socketTimeout;
this._retryCount = 0;

@@ -38,2 +44,3 @@ this._onSocketConnect = this._onSocketConnect.bind(this);

this._onParserError = this._onParserError.bind(this);
this._onSocketTimeout = this._onSocketTimeout.bind(this);
}

@@ -46,6 +53,10 @@

// can happen if the socket immediately closes after being created
if (!this._socket) { return }
if (!this._socket) {
return;
}
await Parser.init();
// can happen if the socket immediately closes after being created
if (!this._socket) { return }
if (!this._socket) {
return;
}
this._parser = new Parser(this._socket);

@@ -60,8 +71,7 @@ this._parser.on('message', this._onMessage);

getPersistentIds() {
return this._persistentIds
}
async _checkIn() {
return checkIn(this._credentials.gcm.androidId, this._credentials.gcm.securityToken);
return checkIn(
this._credentials.gcm.androidId,
this._credentials.gcm.securityToken
);
}

@@ -71,7 +81,13 @@

this._socket = new tls.TLSSocket();
if (this._socketTimeout) {
this._socket.setTimeout(this._socketTimeout);
}
this._socket.setKeepAlive(true);
this._socket.on('connect', this._onSocketConnect);
this._socket.on('error', this._onSocketError);
this._socket.on('timeout', this._onSocketTimeout);
this._socket.on('close', this._onSocketClose);
this._socket.on('error', this._onSocketError);
this._socket.connect({host : HOST, port : PORT});
this._socket.connect({ host : HOST, port : PORT });
this._socket.write(this._loginBuffer());

@@ -99,3 +115,5 @@ }

const LoginRequestType = proto.lookupType('mcs_proto.LoginRequest');
const hexAndroidId = Long.fromString(this._credentials.gcm.androidId).toString(16);
const hexAndroidId = Long.fromString(
this._credentials.gcm.androidId
).toString(16);
const loginRequest = {

@@ -112,3 +130,3 @@ adaptiveHeartbeat : false,

useRmq2 : true,
setting : [{name : 'new_vc', value : '1'}],
setting : [{ name : 'new_vc', value : '1' }],
// Id of the last notification received

@@ -123,7 +141,9 @@ clientEvent : [],

}
const message = LoginRequestType.create(loginRequest);
const buffer = LoginRequestType.encodeDelimited(message).finish();
// FIXME Can change depending on persistentIds
return Buffer.concat([Buffer.from([kMCSVersion, kLoginRequestTag]), buffer]);
const buffer = LoginRequestType.encodeDelimited(loginRequest).finish();
return Buffer.concat([
Buffer.from([kMCSVersion, kLoginRequestTag]),
buffer,
]);
}

@@ -137,14 +157,17 @@

_onSocketClose() {
this.emit('disconnect');
this._retry();
}
_onSocketError(error) {
_onSocketError() {
// ignore, the close handler takes care of retry
console.error(error);
}
_onParserError(error) {
this.emit('parserError', error)
_onSocketTimeout() {
// Socket will be reopened by the _onSocketClose handler
this._socket.destroy();
}
_onParserError() {
this._retry();
console.error(error);
}

@@ -158,9 +181,9 @@

_onMessage({tag, object}) {
_onMessage({ tag, object }) {
if (tag === kLoginResponseTag) {
// clear persistent ids, as we just sent them to the server while logging
// in
this._persistentIds = []
this._persistentIds = [];
} else if (tag === kDataMessageStanzaTag) {
this._onDataMessage(object)
this._onDataMessage(object);
}

@@ -174,21 +197,23 @@ }

let message
let message;
try {
message = decrypt(object, this._credentials.keys);
} catch (error) {
if (error.message.includes('Unsupported state or unable to authenticate data')) {
// NOTE(ibash) Periodically we're unable to decrypt notifications. In
// all cases we've been able to receive future notifications using the
// same keys. So, we silently drop this notification.
this._persistentIds.push(object.persistentId);
// NOTE(ibash) for superhuman we want to keep an eye on these errors -
// so throw them in their own call stack so that they get caught by
// bugsnag.
setTimeout(() => {
throw error
}, 0)
return
} else {
throw error
switch (true) {
case error.message.includes(
'Unsupported state or unable to authenticate data'
):
case error.message.includes('crypto-key is missing'):
case error.message.includes('salt is missing'):
// NOTE(ibash) Periodically we're unable to decrypt notifications. In
// all cases we've been able to receive future notifications using the
// same keys. So, we silently drop this notification.
console.warn(
'Message dropped as it could not be decrypted: ' + error.message
);
this._persistentIds.push(object.persistentId);
return;
default: {
throw error;
}
}

@@ -201,6 +226,7 @@ }

this.emit('ON_NOTIFICATION_RECEIVED', {
client: this,
notification : message,
// Needs to be saved by the client
persistentId : object.persistentId,
});
}
};

@@ -21,4 +21,10 @@ const crypto = require('crypto');

endpoint : `${FCM_ENDPOINT}/${token}`,
encryption_key : keys.publicKey,
encryption_key : keys.publicKey
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_'),
encryption_auth : keys.authSecret
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_'),
},

@@ -41,5 +47,5 @@ });

return resolve({
privateKey : dh.getPrivateKey('base64'),
publicKey : dh.getPublicKey('base64'),
authSecret : buf.toString('base64'),
privateKey : escape(dh.getPrivateKey('base64')),
publicKey : escape(dh.getPublicKey('base64')),
authSecret : escape(buf.toString('base64')),
});

@@ -46,0 +52,0 @@ });

@@ -9,2 +9,7 @@ const path = require('path');

// Hack to fix PHONE_REGISTRATION_ERROR #17 when bundled with webpack
// https://github.com/dcodeIO/protobuf.js#browserify-integration
protobuf.util.Long = Long
protobuf.configure()
const serverKey = toBase64(Buffer.from(fcmKey));

@@ -11,0 +16,0 @@

@@ -9,3 +9,5 @@ const register = require('./register');

async function listen(credentials, notificationCallback) {
async function listen(credentials, notificationCallback, options = {}) {
const { socketTimeout } = options;
if (!credentials) {

@@ -33,3 +35,6 @@ throw new Error('Missing credentials');

const client = new Client(credentials, credentials.persistentIds);
const client = new Client(credentials, {
persistentIds : credentials.persistentIds,
socketTimeout,
});
client.on('ON_NOTIFICATION_RECEIVED', notificationCallback);

@@ -36,0 +41,0 @@ client.connect();

const EventEmitter = require('events');
const path = require('path');
const {load, BufferReader} = require('protobufjs');
const { load, BufferReader } = require('protobufjs');
const {

@@ -42,3 +42,3 @@ MCS_VERSION_TAG_AND_SIZE,

module.exports = class Parser extends EventEmitter {
static async init () {
static async init() {
if (proto) {

@@ -74,3 +74,3 @@ return;

_onData (buffer) {
_onData(buffer) {
DEBUG(`Got data: ${buffer.length}`);

@@ -89,3 +89,3 @@ this._data = Buffer.concat([this._data, buffer]);

switch(this._state) {
switch (this._state) {
case MCS_VERSION_TAG_AND_SIZE:

@@ -110,3 +110,6 @@ minBytesNeeded = kVersionPacketLen + kTagPacketLen + kSizePacketLenMin;

// TODO(ibash) set a timeout and check for socket disconnect
DEBUG(`Socket read finished prematurely. Waiting for ${minBytesNeeded - this._data.length} more bytes`);
DEBUG(
`Socket read finished prematurely. Waiting for ${minBytesNeeded -
this._data.length} more bytes`
);
this._isWaitingForData = true;

@@ -118,3 +121,3 @@ return;

switch(this._state) {
switch (this._state) {
case MCS_VERSION_TAG_AND_SIZE:

@@ -171,3 +174,3 @@ this._onGotVersion();

this._emitError(error);
return
return;
}

@@ -210,3 +213,3 @@ }

if (this._messageSize === 0) {
this.emit('message', {tag: this._messageTag, object: {}});
this.emit('message', { tag : this._messageTag, object : {} });
this._getNextMessage();

@@ -218,3 +221,7 @@ return;

// Continue reading data.
DEBUG(`Continuing data read. Buffer size is ${this._data.length}, expecting ${this._messageSize}`);
DEBUG(
`Continuing data read. Buffer size is ${this._data.length}, expecting ${
this._messageSize
}`
);
this._state = MCS_PROTO_BYTES;

@@ -234,3 +241,3 @@ this._waitForData();

this.emit('message', {tag: this._messageTag, object: object});
this.emit('message', { tag : this._messageTag, object : object });

@@ -257,3 +264,3 @@ if (this._messageTag === kLoginResponseTag) {

_buildProtobufFromTag(tag) {
switch(tag) {
switch (tag) {
case kHeartbeatPingTag:

@@ -260,0 +267,0 @@ return proto.lookupType('mcs_proto.HeartbeatPing');

@@ -8,8 +8,6 @@ const crypto = require('crypto');

function decrypt(object, keys) {
const cryptoKey = object.appData
.find(item => item.key === 'crypto-key')
.value.slice(3);
const salt = object.appData
.find(item => item.key === 'encryption')
.value.slice(5);
const cryptoKey = object.appData.find(item => item.key === 'crypto-key');
if (!cryptoKey) throw new Error('crypto-key is missing');
const salt = object.appData.find(item => item.key === 'encryption');
if (!salt) throw new Error('salt is missing');
const dh = crypto.createECDH('prime256v1');

@@ -20,5 +18,5 @@ dh.setPrivateKey(keys.privateKey, 'base64');

authSecret : keys.authSecret,
dh : cryptoKey,
dh : cryptoKey.value.slice(3),
privateKey : dh,
salt,
salt : salt.value.slice(5),
};

@@ -25,0 +23,0 @@ const decrypted = ece.decrypt(object.rawData, params);

@@ -1,1 +0,2 @@

module.exports = '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'
module.exports =
'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';
// Set a sender id and server key and save this under test/keys.js
module.exports = {
SENDER_ID: 'Your sender id here',
SERVER_KEY: 'Your server key here'
}
SENDER_ID : 'Your sender id here',
SERVER_KEY : 'Your server key here',
};
const assert = require('assert')
const request = require('request-promise')
const {SENDER_ID, SERVER_KEY} = require('./keys')
const {register, listen} = require('../src/index')
const NOTIFICATIONS = {
SIMPLE: {title: 'Hello world ', body: 'Test'},
LARGE: {title: 'Hello world ', body: require('./4kb')}
}
describe('Parser', function() {
beforeEach(async function() {
let credentials = await register(SENDER_ID)
this.send = async (notification) => {
let response = await request({
method: 'POST',
url: 'https://fcm.googleapis.com/fcm/send',
json: true,
body: {
to: credentials.fcm.token,
notification: notification,
},
headers: {Authorization : `key=${SERVER_KEY}`},
})
assert.equal(response.success, 1, `sending of notification failed: ${JSON.stringify(response)}`)
return response
}
this.receive = async (n) => {
let received = []
return new Promise(async (resolve) => {
let onNotification = (notification) => {
received.push(notification)
if (received.length == n) {
resolve(received)
}
}
credentials.persistentIds = []
this.client = await listen(credentials, onNotification)
})
}
})
afterEach(async function() {
this.client.destroy()
})
it('should receive a simple notification', async function() {
await this.send(NOTIFICATIONS.SIMPLE)
let notifications = await this.receive(1)
assert.equal(notifications.length, 1)
assert.deepEqual(notifications[0].notification.notification, NOTIFICATIONS.SIMPLE)
})
it('should receive a large notification', async function() {
await this.send(NOTIFICATIONS.LARGE)
let notifications = await this.receive(1)
assert.equal(notifications.length, 1)
assert.deepEqual(notifications[0].notification.notification, NOTIFICATIONS.LARGE)
})
it('should multiple notifications', async function() {
await this.send(NOTIFICATIONS.SIMPLE)
await this.send(NOTIFICATIONS.LARGE)
await this.send(NOTIFICATIONS.SIMPLE)
let notifications = await this.receive(3)
assert.equal(notifications.length, 3)
assert.deepEqual(notifications[0].notification.notification, NOTIFICATIONS.SIMPLE)
assert.deepEqual(notifications[1].notification.notification, NOTIFICATIONS.LARGE)
assert.deepEqual(notifications[2].notification.notification, NOTIFICATIONS.SIMPLE)
})
})

Sorry, the diff of this file is too big to display