Skip to content

Commit 130ba8c

Browse files
rohit-gohrimarco-c
authored andcommitted
Allow falsy values for vapidDetails to allow not using VAPID for FCM endpoints (#456)
1 parent 0117eb4 commit 130ba8c

File tree

2 files changed

+146
-21
lines changed

2 files changed

+146
-21
lines changed

src/web-push-lib.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ WebPushLib.prototype.generateRequestDetails = function(subscription, payload, op
148148
currentGCMAPIKey = options.gcmAPIKey;
149149
}
150150

151-
if (options.vapidDetails) {
151+
// Falsy values are allowed here so one can skip Vapid `else if` below and use FCM
152+
if (options.vapidDetails !== undefined) {
152153
currentVapidDetails = options.vapidDetails;
153154
}
154155

test/testSendNotification.js

Lines changed: 144 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,12 @@ suite('sendNotification', function() {
127127

128128
function validateRequest(request) {
129129
const options = request.requestOptions;
130+
const contentEncoding = (options.extraOptions && options.extraOptions.contentEncoding)
131+
|| WebPushConstants.supportedContentEncodings.AES_GCM;
130132
const isGCM = options.subscription.endpoint
131133
.indexOf('https://android.googleapis.com/gcm') === 0;
134+
const isFCM = options.subscription.endpoint
135+
.indexOf('https://fcm.googleapis.com/fcm') === 0;
132136

133137
assert.equal(requestDetails.headers['content-length'], requestBody.length, 'Check Content-Length header');
134138

@@ -152,10 +156,10 @@ suite('sendNotification', function() {
152156
return key.indexOf('dh=') === 0;
153157
}).substring('dh='.length);
154158

155-
assert.equal(requestDetails.headers['content-encoding'], options.extraOptions.contentEncoding, 'Check Content-Encoding header');
159+
assert.equal(requestDetails.headers['content-encoding'], contentEncoding, 'Check Content-Encoding header');
156160

157161
const decrypted = ece.decrypt(requestBody, {
158-
version: options.extraOptions.contentEncoding,
162+
version: contentEncoding,
159163
privateKey: userCurve,
160164
dh: appServerPublicKey,
161165
salt: salt,
@@ -166,29 +170,39 @@ suite('sendNotification', function() {
166170
}
167171

168172
if (options.vapid) {
169-
const keys = requestDetails.headers['crypto-key'].split(';');
170-
const vapidKey = keys.find(function(key) {
171-
return key.indexOf('p256ecdsa=') === 0;
172-
});
173+
let jwt;
174+
let vapidKey;
175+
176+
if (contentEncoding === WebPushConstants.supportedContentEncodings.AES_GCM) {
177+
const keys = requestDetails.headers['crypto-key'].split(';');
178+
const vapidKeyHeader = keys.find(function(key) {
179+
return key.indexOf('p256ecdsa=') === 0;
180+
});
173181

174-
assert.equal(vapidKey.indexOf('p256ecdsa='), 0, 'Crypto-Key header correct');
175-
const appServerVapidPublicKey = urlBase64.decode(vapidKey.substring('p256ecdsa='.length));
182+
assert.equal(vapidKeyHeader.indexOf('p256ecdsa='), 0, 'Crypto-Key header correct');
183+
vapidKey = vapidKeyHeader.substring('p256ecdsa='.length);
184+
185+
const authorizationHeader = requestDetails.headers.authorization;
186+
assert.equal(authorizationHeader.indexOf('WebPush '), 0, 'Check VAPID Authorization header');
187+
jwt = authorizationHeader.substring('WebPush '.length);
188+
} else if (contentEncoding === WebPushConstants.supportedContentEncodings.AES_128_GCM) {
189+
const authorizationHeader = requestDetails.headers.authorization;
190+
assert.equal(authorizationHeader.indexOf('vapid t='), 0, 'Check VAPID Authorization header');
191+
[jwt, vapidKey] = authorizationHeader.substring('vapid t='.length).split(', k=');
192+
}
176193

177-
assert(appServerVapidPublicKey.equals(vapidKeys.publicKey));
194+
assert.equal(vapidKey, vapidKeys.publicKey);
178195

179-
const authorizationHeader = requestDetails.headers.authorization;
180-
assert.equal(authorizationHeader.indexOf('WebPush '), 0, 'Check VAPID Authorization header');
181-
const jwt = authorizationHeader.substring('WebPush '.length);
182196
// assert(jws.verify(jwt, 'ES256', appServerVapidPublicKey)), 'JWT valid');
183-
const decoded = jws.decode(jwt);
184-
assert.equal(decoded.header.typ, 'JWT');
185-
assert.equal(decoded.header.alg, 'ES256');
186-
assert.equal(decoded.payload.aud, 'https://127.0.0.1');
187-
assert(decoded.payload.exp > Date.now() / 1000);
188-
assert.equal(decoded.payload.sub, 'mailto:[email protected]');
197+
const decoded = jws.decode(jwt);
198+
assert.equal(decoded.header.typ, 'JWT');
199+
assert.equal(decoded.header.alg, 'ES256');
200+
assert.equal(options.subscription.endpoint.startsWith(decoded.payload.aud), true);
201+
assert(decoded.payload.exp > Date.now() / 1000);
202+
assert.equal(decoded.payload.sub, 'mailto:[email protected]');
189203
}
190204

191-
if (isGCM) {
205+
if (isGCM || (isFCM && !options.vapid)) {
192206
if (typeof options.extraOptions !== 'undefined'
193207
&& typeof options.extraOptions.gcmAPIKey !== 'undefined') {
194208
assert.equal(requestDetails.headers.authorization, 'key=' + options.extraOptions.gcmAPIKey, 'Check GCM Authorization header');
@@ -398,6 +412,102 @@ suite('sendNotification', function() {
398412
subscription: {
399413
}
400414
}
415+
}, {
416+
testTitle: 'send/receive FCM',
417+
requestOptions: {
418+
subscription: {
419+
endpoint: 'https://fcm.googleapis.com/fcm/send/someSubscriptionID'
420+
}
421+
}
422+
}, {
423+
testTitle: 'send/receive FCM and you want to use VAPID (aesgcm)',
424+
requestOptions: {
425+
subscription: {
426+
endpoint: 'https://fcm.googleapis.com/fcm/send/someSubscriptionID',
427+
keys: VALID_KEYS
428+
},
429+
extraOptions: {
430+
vapidDetails: {
431+
subject: 'mailto:[email protected]',
432+
privateKey: vapidKeys.privateKey,
433+
publicKey: vapidKeys.publicKey
434+
},
435+
contentEncoding: WebPushConstants.supportedContentEncodings.AES_GCM
436+
},
437+
vapid: true
438+
}
439+
}, {
440+
testTitle: 'send/receive FCM and you want to use VAPID (aesgcm) (via global options)',
441+
requestOptions: {
442+
subscription: {
443+
endpoint: 'https://fcm.googleapis.com/fcm/send/someSubscriptionID',
444+
keys: VALID_KEYS
445+
},
446+
extraOptions: {
447+
contentEncoding: WebPushConstants.supportedContentEncodings.AES_GCM
448+
},
449+
vapid: true
450+
},
451+
globalOptions: {
452+
vapidDetails: {
453+
subject: 'mailto:[email protected]',
454+
privateKey: vapidKeys.privateKey,
455+
publicKey: vapidKeys.publicKey
456+
}
457+
}
458+
}, {
459+
testTitle: 'send/receive FCM and you want to use VAPID (aes128gcm)',
460+
requestOptions: {
461+
subscription: {
462+
endpoint: 'https://fcm.googleapis.com/fcm/send/someSubscriptionID',
463+
keys: VALID_KEYS
464+
},
465+
extraOptions: {
466+
vapidDetails: {
467+
subject: 'mailto:[email protected]',
468+
privateKey: vapidKeys.privateKey,
469+
publicKey: vapidKeys.publicKey
470+
},
471+
contentEncoding: WebPushConstants.supportedContentEncodings.AES_128_GCM
472+
},
473+
vapid: true
474+
}
475+
}, {
476+
testTitle: 'send/receive FCM and you want to use VAPID (aes128gcm) (via global options)',
477+
requestOptions: {
478+
subscription: {
479+
endpoint: 'https://fcm.googleapis.com/fcm/send/someSubscriptionID',
480+
keys: VALID_KEYS
481+
},
482+
extraOptions: {
483+
contentEncoding: WebPushConstants.supportedContentEncodings.AES_128_GCM
484+
},
485+
vapid: true
486+
},
487+
globalOptions: {
488+
vapidDetails: {
489+
subject: 'mailto:[email protected]',
490+
privateKey: vapidKeys.privateKey,
491+
publicKey: vapidKeys.publicKey
492+
}
493+
}
494+
}, {
495+
testTitle: 'send/receive FCM and you don\'t want to use VAPID (when global options set)',
496+
requestOptions: {
497+
subscription: {
498+
endpoint: 'https://fcm.googleapis.com/fcm/send/someSubscriptionID'
499+
},
500+
extraOptions: {
501+
vapidDetails: null
502+
}
503+
},
504+
globalOptions: {
505+
vapidDetails: {
506+
subject: 'mailto:[email protected]',
507+
privateKey: vapidKeys.privateKey,
508+
publicKey: vapidKeys.publicKey
509+
}
510+
}
401511
}, {
402512
testTitle: 'send/receive string with GCM',
403513
requestOptions: {
@@ -465,8 +575,22 @@ suite('sendNotification', function() {
465575
validGCMRequest.requestOptions.subscription.endpoint = 'https://android.googleapis.com/gcm/send/someSubscriptionID';
466576
}
467577

578+
validGCMRequest.globalOptions = validGCMRequest.globalOptions || {};
579+
if (validGCMRequest.globalOptions.gcmAPIKey === undefined) {
580+
validGCMRequest.globalOptions.gcmAPIKey = 'my_gcm_key';
581+
}
582+
468583
const webPush = require('../src/index');
469-
webPush.setGCMAPIKey('my_gcm_key');
584+
if (validGCMRequest.globalOptions.gcmAPIKey) {
585+
webPush.setGCMAPIKey(validGCMRequest.globalOptions.gcmAPIKey);
586+
}
587+
if (validGCMRequest.globalOptions.vapidDetails) {
588+
webPush.setVapidDetails(
589+
validGCMRequest.globalOptions.vapidDetails.subject,
590+
validGCMRequest.globalOptions.vapidDetails.publicKey,
591+
validGCMRequest.globalOptions.vapidDetails.privateKey
592+
);
593+
}
470594

471595
return webPush.sendNotification(
472596
validGCMRequest.requestOptions.subscription,

0 commit comments

Comments
 (0)