Initial commit
This commit is contained in:
commit
810e0130eb
121
README.md
Normal file
121
README.md
Normal file
@ -0,0 +1,121 @@
|
||||
# Notes from attempting to setup web push notifications
|
||||
|
||||
## Files included
|
||||
|
||||
- `service-worker.js` - Service worker file, it listens for push events and displays notifications
|
||||
- `example.html` - Main file, it will immediately ask for permission to send notifications and log the subscription object
|
||||
to the console. You might have to refresh after giving permissions to see the subscription object.
|
||||
|
||||
## Steps to setup
|
||||
|
||||
### Browser setup
|
||||
|
||||
1. run `npx serve`
|
||||
2. Open the browser and go to `http://localhost:3000`
|
||||
3. Open the console and you should see the subscription object
|
||||
|
||||
### Server setup
|
||||
|
||||
(Or you can use [web-push](https://www.npmjs.com/package/web-push) package)
|
||||
|
||||
#### Generate VAPID keys
|
||||
|
||||
1. Run `openssl ecparam -name prime256v1 -genkey -noout -out private.pem`
|
||||
1. You might also want to generate a PKCS8 key for the private key
|
||||
- Run `openssl pkcs8 -topk8 -nocrypt -in private.pem -out private.pkcs8.pem`
|
||||
2. Run `openssl ec -in private.pem -pubout -out public.pem`
|
||||
|
||||
Now you can your favourite http request library to send a POST request to the url returned by the
|
||||
`pushManager.subscribe` method.
|
||||
|
||||
When you do you should include the following header (and no body!):
|
||||
|
||||
```http
|
||||
TTL: 60
|
||||
Authorization: `Bearer ${jwtToken}`
|
||||
Crypto-Key: `p256ecdsa=${publicKey}`
|
||||
Topic: `An optional topic`
|
||||
Urgency: `An optional urgency`
|
||||
```
|
||||
|
||||
The `jwtToken` is a JWT token that you can generate using the private key you generated earlier. The payload should
|
||||
include the `aud` field which should be the url returned by the `pushManager.subscribe` method.
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
const {default: fs} = await import("fs");
|
||||
const {default: crypto} = await import("crypto");
|
||||
const {default: jwt} = await import("jsonwebtoken");
|
||||
|
||||
const privateKeyBuffer = fs.readFileSync('vapid_private.pem');
|
||||
const privateKey = crypto.createPrivateKey(privateKeyBuffer);
|
||||
|
||||
const payload = {
|
||||
aud: 'https://fcm.googleapis.com',
|
||||
sub: 'mailto:example@example.com',
|
||||
};
|
||||
|
||||
const jwtToken = jwt.sign(payload, privateKey, {algorithm: 'ES256', expiresIn: '1h'});
|
||||
console.log(jwtToken);
|
||||
```
|
||||
|
||||
It has to use the ES256 algorithm and the `exp` field should be set to a timestamp in the future.
|
||||
|
||||
The `publicKey` is the public key you generated earlier however it should be encoded as JWK and then base64 url encoded.
|
||||
You can use the following code to do that:
|
||||
|
||||
```javascript
|
||||
const {default: fs} = await import("fs");
|
||||
const {default: crypto} = await import("crypto");
|
||||
|
||||
const publicKeyBuffer = fs.readFileSync('public.pem');
|
||||
const publicKey = crypto.createPublicKey(publicKeyBuffer);
|
||||
|
||||
const jwk = {
|
||||
kty: 'EC',
|
||||
crv: 'P-256',
|
||||
x: publicKey.export({format: 'jwk'}).x,
|
||||
y: publicKey.export({format: 'jwk'}).y,
|
||||
};
|
||||
|
||||
const base64UrlEncode = (str) => {
|
||||
return str.toString('base64')
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
.replace(/=/g, '');
|
||||
};
|
||||
|
||||
const base64UrlEncodedPublicKey = base64UrlEncode(publicKey.export({format: 'jwk'}).x) + '.' + base64UrlEncode(publicKey.export({format: 'jwk'}).y);
|
||||
|
||||
console.log(base64UrlEncodedPublicKey);
|
||||
```
|
||||
|
||||
This should be enough to get you started with web push notifications.
|
||||
|
||||
### Notes
|
||||
|
||||
- The `pushManager.subscribe` method may require depending on the browser a `userVisibleOnly` field or `applicationServerKey` field. The `userVisibleOnly`
|
||||
field should be set to `true` and the `applicationServerKey` field should be set to the public key you generated earlier.
|
||||
|
||||
- For sending a payload additional changes need to happen, you can't just send a payload with the request.
|
||||
- For it to work you will have to update the headers:
|
||||
- Encoding the payload, it's complex and I haven't done it yet but you can look at the [Mozilla Blog](https://blog.mozilla.org/services/2016/08/23/sending-vapid-identified-webpush-notifications-via-mozillas-push-service/) for more information.
|
||||
- The `Crypto-Key` header should be set to 3 values:
|
||||
- `p256ecdsa=${publicKey}` - The public key you generated earlier
|
||||
- `dh=${publicHalfKey}` - The first half of the shared secret when the payload is encoded
|
||||
- `keyid=p256dh` - The key id
|
||||
- The `Encryption` header should be set to 2 values:
|
||||
- `salt=${salt}` - The salt used to generate the shared secret
|
||||
- `keyid=p256dh` - The key id
|
||||
- The `Content-Encoding` header should be set to `aesgcm`
|
||||
|
||||
For more information you can check the useful links below.
|
||||
|
||||
### Useful links
|
||||
|
||||
- [The Web Push Protocol ](https://web.dev/articles/push-notifications-web-push-protocol)
|
||||
- [Web Push Book](https://web-push-book.gauntface.com/)
|
||||
- [JWT.io](https://jwt.io/)
|
||||
- [WebPushDataTestPage](https://mozilla-services.github.io/WebPushDataTestPage/)
|
||||
- [Mozilla Blog](https://blog.mozilla.org/services/2016/08/23/sending-vapid-identified-webpush-notifications-via-mozillas-push-service/)
|
||||
- [Mozilla docs](https://developer.mozilla.org/en-US/docs/Web/API/PushSubscription)
|
42
example.html
Normal file
42
example.html
Normal file
@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Game Site</title>
|
||||
<script>
|
||||
this.onpush = (event) => {
|
||||
console.log(event.data);
|
||||
// From here we can write the data to IndexedDB, send it to any open
|
||||
// windows, display a notification, etc.
|
||||
};
|
||||
|
||||
navigator.serviceWorker
|
||||
.register("service-worker.js")
|
||||
.then((serviceWorkerRegistration) => {
|
||||
serviceWorkerRegistration.pushManager.subscribe().then(
|
||||
(pushSubscription) => {
|
||||
const subscriptionObject = {
|
||||
endpoint: pushSubscription.endpoint,
|
||||
keys: {
|
||||
p256dh: pushSubscription.getKey('p256dh'),
|
||||
auth: pushSubscription.getKey('auth'),
|
||||
},
|
||||
encoding: PushManager.supportedContentEncodings,
|
||||
/* other app-specific data, such as user identity */
|
||||
};
|
||||
console.log(pushSubscription.endpoint, pushSubscription, subscriptionObject);
|
||||
// The push subscription details needed by the application
|
||||
// server are now available, and can be sent to it using,
|
||||
// for example, the fetch() API.
|
||||
},
|
||||
(error) => {
|
||||
console.error(error);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
31
service-worker.js
Normal file
31
service-worker.js
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
self.addEventListener("install", (event) => {
|
||||
console.log("Service worker installed");
|
||||
});
|
||||
|
||||
self.addEventListener("push", (event) => {
|
||||
if (!(self.Notification && self.Notification.permission === "granted")) {
|
||||
return;
|
||||
}
|
||||
console.log("Received a push message", event);
|
||||
|
||||
const data = event.data?.json() ?? {};
|
||||
const title = data.title || "Something Has Happened";
|
||||
const message =
|
||||
data.message || "Here's something you might want to check out.";
|
||||
const icon = "images/new-notification.png";
|
||||
|
||||
event.waitUntil(self.registration.showNotification(title, {
|
||||
body: message,
|
||||
tag: "simple-push-demo-notification",
|
||||
icon,
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
self.addEventListener("notificationclick", (event) => {
|
||||
console.log("Notification click Received.", event);
|
||||
event.notification.close();
|
||||
event.waitUntil(clients.openWindow(
|
||||
"https://molodetz.nl",));
|
||||
});
|
Loading…
Reference in New Issue
Block a user