Skip to content

Commit 292b68a

Browse files
authored
Merge pull request #183 from gauntface/vapid-chrome
Moving Chrome to using VAPID
2 parents c92e04f + ab468a6 commit 292b68a

File tree

6 files changed

+85
-27
lines changed

6 files changed

+85
-27
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ Send a Push notification to an endpoint. *params* contains optional parameters:
2323
Note that, in order to encrypt the *payload*, *userPublicKey* and *userAuth* are required.
2424

2525
The properties of the *vapid* objects are:
26-
- *audience*, the origin of the application server;
2726
- *subject*, a contact URI for the application server (either 'mailto:' or 'https:');
2827
- *privateKey*;
2928
- *publicKey*.

index.js

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,46 @@ function generateVAPIDKeys() {
3737
};
3838
}
3939

40+
function getVapidHeaders(vapid) {
41+
if (!vapid.audience) {
42+
throw new Error('No audience set in vapid.audience');
43+
}
44+
45+
if (!vapid.subject) {
46+
throw new Error('No subject set in vapid.subject');
47+
}
48+
49+
if (!vapid.publicKey) {
50+
throw new Error('No key set vapid.publicKey');
51+
}
52+
53+
if (!vapid.privateKey) {
54+
throw new Error('No key set in vapid.privateKey');
55+
}
56+
57+
var header = {
58+
typ: 'JWT',
59+
alg: 'ES256'
60+
};
61+
62+
var jwtPayload = {
63+
aud: vapid.audience,
64+
exp: Math.floor(Date.now() / 1000) + 86400,
65+
sub: vapid.subject,
66+
};
67+
68+
var jwt = jws.sign({
69+
header: header,
70+
payload: jwtPayload,
71+
privateKey: toPEM(vapid.privateKey),
72+
});
73+
74+
return {
75+
Authorization: 'Bearer ' + jwt,
76+
'Crypto-Key': 'p256ecdsa=' + urlBase64.encode(vapid.publicKey)
77+
};
78+
}
79+
4080
function WebPushError(message, statusCode, headers, body) {
4181
Error.captureStackTrace(this, this.constructor);
4282

@@ -152,30 +192,15 @@ function sendNotification(endpoint, params) {
152192

153193
if (vapid && !isGCM) {
154194
// VAPID isn't supported by GCM.
195+
vapid.audience = urlParts.protocol + '//' + urlParts.hostname;
155196

156-
var header = {
157-
typ: 'JWT',
158-
alg: 'ES256'
159-
};
160-
161-
var jwtPayload = {
162-
aud: vapid.audience,
163-
exp: Math.floor(Date.now() / 1000) + 86400,
164-
sub: vapid.subject,
165-
};
166-
167-
var jwt = jws.sign({
168-
header: header,
169-
payload: jwtPayload,
170-
privateKey: toPEM(vapid.privateKey),
171-
});
197+
const vapidHeaders = getVapidHeaders(vapid);
172198

173-
options.headers['Authorization'] = 'Bearer ' + jwt;
174-
var key = 'p256ecdsa=' + urlBase64.encode(vapid.publicKey);
199+
options.headers['Authorization'] = vapidHeaders.Authorization;
175200
if (options.headers['Crypto-Key']) {
176-
options.headers['Crypto-Key'] += ';' + key;
201+
options.headers['Crypto-Key'] += ';' + vapidHeaders['Crypto-Key'];
177202
} else {
178-
options.headers['Crypto-Key'] = key;
203+
options.headers['Crypto-Key'] = vapidHeaders['Crypto-Key'];
179204
}
180205
}
181206

test/data/demo/index.html

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,22 @@
55
<link rel="manifest" href="manifest.json">
66
</head>
77
<body>
8+
<h1>Test Page</h1>
9+
810
<script type="text/javascript">
11+
function base64UrlToUint8Array(base64UrlData) {
12+
const padding = '='.repeat((4 - base64UrlData.length % 4) % 4);
13+
const base64 = (base64UrlData + padding)
14+
.replace(/\-/g, '+')
15+
.replace(/_/g, '/');
16+
const rawData = atob(base64);
17+
const buffer = new Uint8Array(rawData.length);
18+
for (let i = 0; i < rawData.length; ++i) {
19+
buffer[i] = rawData.charCodeAt(i);
20+
}
21+
return buffer;
22+
}
23+
924
(function() {
1025
if (!('serviceWorker' in navigator)) {
1126
return;
@@ -23,9 +38,21 @@
2338
}
2439
reg.active.postMessage('setup', [channel.port2]);
2540

26-
return reg.pushManager.subscribe({ userVisibleOnly: true });
41+
var subscribeOptions = { userVisibleOnly: true };
42+
// Figure out the vapid key
43+
var searchParam = window.location.search;
44+
vapidRegex = searchParam.match(/vapid=(.[^&]*)/);
45+
if (vapidRegex) {
46+
// Convert the base 64 encoded string
47+
subscribeOptions.applicationServerKey = base64UrlToUint8Array(vapidRegex[1]);
48+
}
49+
50+
console.log(subscribeOptions);
51+
52+
return reg.pushManager.subscribe(subscribeOptions);
2753
})
2854
.then(function(subscription) {
55+
console.log(JSON.stringify(subscription));
2956
window.subscribeSuccess = true;
3057
window.testSubscription = JSON.stringify(subscription);
3158
})

test/helpers/create-server.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ function createServer(options, webPush) {
1616
var server = http.createServer(function(req, res) {
1717
try {
1818
if (req.method === 'GET') {
19-
if (req.url === '/') {
19+
// Ignore query parameters which are used to inject application keys
20+
var urlParts = req.url.split('?');
21+
if (urlParts[0] === '/') {
2022
req.url = '/index.html';
2123
}
2224

test/testSelenium.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
return;
88
}
99

10+
const urlBase64 = require('urlsafe-base64');
1011
const seleniumAssistant = require('selenium-assistant');
1112
const webdriver = require('selenium-webdriver');
1213
const seleniumFirefox = require('selenium-webdriver/firefox');
@@ -23,7 +24,6 @@
2324

2425
const PUSH_TEST_TIMEOUT = 120 * 1000;
2526
const VAPID_PARAM = {
26-
audience: 'https://www.mozilla.org/',
2727
subject: 'mailto:[email protected]',
2828
privateKey: new Buffer('H6tqEMswzHOFlPHFi2JPfDQRiKN32ZJIwvSPWZl1VTA=', 'base64'),
2929
publicKey: new Buffer('BIx6khu9Z/5lBwNEXYNEOQiL70IKYDpDxsTyoiCb82puQ/V4c/NFdyrBFpWdsz3mikmV6sWARNuhRbbbLTMOmB0=', 'base64'),
@@ -50,6 +50,7 @@
5050
.then(function(server) {
5151
globalServer = server;
5252
testServerURL = 'http://127.0.0.1:' + server.port;
53+
5354
if (browser.getSeleniumBrowserId() === 'firefox') {
5455
// This is based off of: https://bugzilla.mozilla.org/show_bug.cgi?id=1275521
5556
// Unfortunately it doesn't seem to work :(
@@ -87,6 +88,11 @@
8788
})
8889
.then(function(driver) {
8990
globalDriver = driver;
91+
92+
if (options.vapid) {
93+
testServerURL += '?vapid=' + urlBase64.encode(options.vapid.publicKey);
94+
}
95+
9096
// Tests will likely expect a native promise with then and catch
9197
// Not the web driver promise of then and thenCatch
9298
return new Promise(function(resolve, reject) {
@@ -128,6 +134,7 @@
128134
})
129135
.then(function(subscribeError) {
130136
if (subscribeError) {
137+
console.log('subscribeError: ', subscribeError);
131138
throw subscribeError;
132139
}
133140

test/testSendNotification.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ suite('sendNotification', function() {
102102
var decoded = jws.decode(jwt);
103103
assert.equal(decoded.header.typ, 'JWT');
104104
assert.equal(decoded.header.alg, 'ES256');
105-
assert.equal(decoded.payload.aud, 'https://www.mozilla.org/');
105+
assert.equal(decoded.payload.aud, 'https://127.0.0.1');
106106
assert(decoded.payload.exp > Date.now() / 1000);
107107
assert.equal(decoded.payload.sub, 'mailto:[email protected]');
108108
}
@@ -456,7 +456,6 @@ suite('sendNotification', function() {
456456
userAuth: urlBase64.encode(userAuth),
457457
payload: 'hello',
458458
vapid: {
459-
audience: 'https://www.mozilla.org/',
460459
subject: 'mailto:[email protected]',
461460
privateKey: vapidKeys.privateKey,
462461
publicKey: vapidKeys.publicKey,
@@ -486,7 +485,6 @@ suite('sendNotification', function() {
486485
.then(function() {
487486
return webPush.sendNotification('https://android.googleapis.com/gcm/send/someSubscriptionID', {
488487
vapid: {
489-
audience: 'https://www.mozilla.org/',
490488
subject: 'mailto:[email protected]',
491489
privateKey: vapidKeys.privateKey,
492490
publicKey: vapidKeys.publicKey,

0 commit comments

Comments
 (0)