-
Notifications
You must be signed in to change notification settings - Fork 339
Conversation
push-payload/index.js
Outdated
var response = await fetch('./vapidPublicKey') | ||
var vapidPublicKey = await response.text(); | ||
// Convert the URL safe base64 string to a Uint8Array | ||
var convertedVapidKey = urlBase64ToUint8Array(vapidPublicKey); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can probably use the base64 string directly, see https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't seem to work on Chrome:
TypeError: Failed to execute 'subscribe' on 'PushManager': The provided value is not of type '(ArrayBuffer or ArrayBufferView)'
Edit: But it does work in Firefox, and it should according to the spec: (BufferSource or DOMString)? applicationServerKey = null;
. So this is probably a bug in Chrome?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, so I guess it's not implemented yet in Chrome. Unfortunately I don't think there's a way to share the code between the recipes, so we will have to copy it everywhere.
Please also add a comment explaining why it is needed and that it can be removed once Chrome supports passing a base64 string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
push-payload/index.js
Outdated
if (subscription) { | ||
return subscription; | ||
var currentKey = new Uint8Array(subscription.options.applicationServerKey); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that we need to take into account the possibility of the vapid key changing in these examples.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't store the key somewhere (as the example is right now), it will change every time the server process restarts. When the key has changed the example will no longer work: server.js
will attempt to send the push message and the endpoint will return an 400 UnauthorizedRegistration response (Google) or 401 Unauthorized response with "Request did not validate missing authorization header" (Mozilla).
This obviously happens a lot when developing locally. I'm not sure what kind of infrastructure serviceworke.rs is running on, but say you try the example one day, the server restarts overnight, and you try it again the following day, it will no longer work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are using Heroku. I think we can store the VAPID key in a file so it doesn't get recreated every time.
It will still be recreated from time to time, but not every time you reload.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is still going to be a pain though... Hopefully we can figure out a better solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think the current check is a pretty good solution. Even in production scenarios you might need to change the keys sometimes (when the key is compromised or lost). This seems a sensible place to check if the subscription we have is still usable. And as a bonus we don't have to extend the demo with persistent storage (or something behind-the-scenes specifically for serviceworke.rs).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have another idea. We can put the VAPID key in a Heroku environment variable, so it will never change.
This is fine, we are going to remove it from the |
By the way, if we no longer use GCM, the |
Yes, we can remove it as part of this PR. |
b1fd19b makes things a lot more readable (from web-push-libs: "The expected format is the same output as JSON.stringify'ing a PushSubscription in the browser.") If there aren't any more concerns (except the handling of changing server keys), I'll continue with the other recipes. |
push-payload/index.js
Outdated
|
||
endpoint = subscription.endpoint; | ||
// Keep the subscription for easy access | ||
mySubscription = subscription; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't need this, we can use subscription
directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mySubscription
is reused in the doIt.onclick function. If we don't save it here we need to add the following there (fine with me either way, but this adds some more code):
navigator.serviceWorker.ready.then(function(registration) {
registration.pushManager.getSubscription().then(function(subscription) {
...
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can bring doIt.onclick in the scope here, it needs to wait for the subscription anyway.
push-payload/index.js
Outdated
// This function is needed because Chrome doesn't accept a base64 encoded string | ||
// as value for applicationServerKey in pushManager.subscribe yet | ||
// https://bugs.chromium.org/p/chromium/issues/detail?id=802280 | ||
function urlBase64ToUint8Array(base64String) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can put this function in a utils.js script in the top-directory, which we include in each recipe before index.js. What do you say?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems like the cleanest solution.. Unfortunately I'm not sure how to do this. If we put the file in the top-directory, it isn't served by the server process. I think it has to be specified in gulpfile.js
to be served? Also not ideal.. Is there another way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, probably the gulpfile.js
has to copy the file to dist/
.
@delapuente any other idea how to do this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1620dfc works, but might not be the best solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That looks sane enough, maybe move it to the top repo directory though instead of src/js/.
I'm still undecided whether the best solution is to have the function in a separate file or not. One one hand, it's a pain to copy and paste it in every recipe, on the other hand, the recipes are supposed to be self-contained.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Sorry if the discussion is taking long, but once we have decided here we will apply the same rules to all recipes, so it will be way faster then).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep I agree that it's both not optimal.. Its a shame that Chrome hasn't even started their native implementation yet.. otherwise we could maybe leave the function out completely. But since it's not even on the horizon that's not really a possibility either.
(I've moved tools.js to the top directory in 1bb1db5)
So what's the next step here? I'm not sure how to handle the key with a Heroku variable (will that even work locally on my machine?). Maybe it would be easier if you do that. |
As far as the application is concerned, a Heroku variable is just an environment variable. So you can emulate everything locally by defining an env variable. |
Ok, done in 68aebbc. I'm still not convinced that removing the public key check in the browser is a good idea. If the server's key is (accidentally) changed it is not clear what's the problem (the navigator.serviceWorker.getRegistration()
.then(registration => registration.pushManager.getSubscription())
.then(subscription => subscription.unsubscribe()); |
push-payload/index.js
Outdated
if (subscription) { | ||
return subscription; | ||
} | ||
|
||
// Otherwise, subscribe the user (userVisibleOnly allows to specify that we don't plan to | ||
// Subscribe the user (userVisibleOnly allows to specify that we don't plan to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: don't change this comment.
push-payload/index.js
Outdated
// urlBase64ToUint8Array() is defined in /tools.js | ||
var convertedVapidKey = urlBase64ToUint8Array(vapidPublicKey); | ||
|
||
// See if we already have a subscription with the server | ||
if (subscription) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this at the beginning of the function.
push-payload/index.js
Outdated
// If a subscription was found, return it. | ||
.then(async function(subscription) { | ||
// Get the server's public key | ||
var response = await fetch('./vapidPublicKey') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: use let
and const
everywhere instead of var
when possible.
LGTM overall, I think we can convert the other recipes too now. |
Converted all recipes and rebased. I think we should remove push-simple, as it's pretty much identical to push-payload now, see https://github.com/mozilla/serviceworker-cookbook/issues/290#issuecomment-369330773. |
So many changes! |
@delapuente do you want to take a look too? |
I'll do. Moved to Windows environment and I also need to set everything up again. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks great to me. Everything is working except for one recipe. I highlighted the required changes.
@marco-c are we comfortable with using async
functions?
push-get-payload/server.js
Outdated
endpoint: req.body.endpoint, | ||
TTL: req.body.ttl, | ||
}) | ||
payloads[req.body.endpoint] = payload; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to index the payloads
map by the endpoint which is now inside the subscription
object:
payloads[req.body.subscription.endpoint] = payload;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops! Strange I didn't notice this. Fixed.
The client sends the endpoint as a property of the subscription object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Working fine. @marco-c if you are OK with using async
functions, then we can merge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, using async functions sounds OK to me.
I've generated VAPID keys and set the two env variables on Heroku. |
webPush.setVapidDetails( | ||
'https://serviceworke.rs/', | ||
process.env.VAPID_PUBLIC_KEY, | ||
process.env.VAPID_PRIVATE_KEY, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heroku is failing because of the comma here.
Looking over the changes, I see that subscribe() is called when the service worker registration promise resolves. Considering that the service worker may not actually be ready at that point, should we change it so that the subscribe() is called when navigator.serviceWorker.ready resolves? |
@aliams see #170 for a discussion on that. (fine with me either way) |
Thanks for the extra context. Because the ready promise is meant to be resolved when the registration has an active service worker and subscribe() requires an active service worker, I believe it would make sense to call subscribe when ready resolves. Currently, as it is, it will not work in insider builds of Edge, but if it's changed to use ready, it will work. |
#170 was closed because @aliams could you file an issue about this? |
Thank you @marco-c. I've just opened #302. |
This is a start for implementing #290
Mostly adapted the example from https://github.com/web-push-libs/web-push
Some questions/notes before I do the other push demos:
urlBase64ToUint8Array
is a big and ugly, and will be needed in every push example. Should this be moved to a separate library file?Uint8Array
s without usingtoString()
?