diff --git a/keyserver/flow-typed/npm/@parse/node-apn_vx.x.x.js b/keyserver/flow-typed/npm/@parse/node-apn_vx.x.x.js --- a/keyserver/flow-typed/npm/@parse/node-apn_vx.x.x.js +++ b/keyserver/flow-typed/npm/@parse/node-apn_vx.x.x.js @@ -27,11 +27,19 @@ pushType: NotificationPushType; threadId: string; payload: any; - badge: number; + badge: ?number; sound: string; contentAvailable: boolean; mutableContent: boolean; urlArgs: string[]; + // Detailed explanation of this field can be found here: + // https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification?language=objc#2943363 + // More fields can be added here, if they ever need to + // be accessed from apn.Notification instance + aps: { + +badge: string | number, + ... + }; } declare type ProviderToken = {| diff --git a/keyserver/src/push/crypto.js b/keyserver/src/push/crypto.js new file mode 100644 --- /dev/null +++ b/keyserver/src/push/crypto.js @@ -0,0 +1,71 @@ +// @flow + +import apn from '@parse/node-apn'; + +import { encryptAndUpdateOlmSession } from '../updaters/olm-session-updater.js'; + +async function encryptIOSNotification( + cookieID: string, + notification: apn.Notification, +): Promise { + const { id, ...payloadSansId } = notification.payload; + let notificationFieldsToEncrypt = { + badge: notification.aps.badge.toString(), + ...payloadSansId, + }; + + if (notification.body) { + notificationFieldsToEncrypt = { + merged: notification.body, + ...notificationFieldsToEncrypt, + }; + } + + let encryptedFields; + try { + encryptedFields = await encryptAndUpdateOlmSession( + cookieID, + 'notifications', + notificationFieldsToEncrypt, + ); + } catch (e) { + console.log('Notification encryption failed: ' + e); + notification.payload.encrypted = 0; + return notification; + } + + const { merged, threadID, badge, ...restPayload } = encryptedFields; + // node-apn library does not allow to store + // strings in 'badge' property. It will be + // restored in NSE on the device. + notification.badge = undefined; + notification.payload.badge = badge.body; + + if (threadID) { + notification.threadId = threadID.body; + notification.payload.threadID = threadID.body; + } + + if (merged) { + notification.body = merged.body; + } + + for (const payloadField in restPayload) { + notification.payload[payloadField] = restPayload[payloadField].body; + } + + notification.payload.encrypted = 1; + return notification; +} + +function prepareEncryptedIOSNotifications( + cookieIDs: $ReadOnlyArray, + notification: apn.Notification, +): Promise> { + const notificationPromises = cookieIDs.map(cookieID => + encryptIOSNotification(cookieID, notification), + ); + return Promise.all(notificationPromises); +} + +export { prepareEncryptedIOSNotifications };