This gem makes it possible to send push messages to web browsers from Ruby backends using the Web Push Protocol. It supports Message Encryption for Web Push and VAPID.
Note: This is an open source gem for Web Push. If you want to send web push notifications from Ruby using Pushpad, you need to use another gem (pushpad gem).
Add this line to the Gemfile:
gem 'web-push'
Or install the gem:
$ gem install web-push
Sending a web push message to a visitor of your website requires a number of steps:
- In the user's web browser, a
serviceWorker
is installed and activated and itspushManager
property is subscribed to push events with your VAPID public key, which creates asubscription
JSON object on the client side. - Your server uses the
web-push
gem to send a notification with thesubscription
obtained from the client and an optional payload (the message). - Your service worker is set up to receive
'push'
events. To trigger a desktop notification, the user has accepted the prompt to receive notifications from your site.
Use web-push
to generate a VAPID key pair (that has both a public_key
and private_key
) and save it on the server side.
# One-time, on the server
vapid_key = WebPush.generate_key
# Save these in your application server settings
vapid_key.public_key
vapid_key.private_key
# Or you can save in PEM format if you prefer
vapid_key.to_pem
Your application must use JavaScript to register a service worker script at an appropriate scope (root is recommended).
navigator.serviceWorker.register('/service-worker.js')
The VAPID public key that you generated earlier needs to be made available to JavaScript, which can be done in many ways, for example with a fetch request or when rendering the HTML template in Ruby:
<script>
// Make the VAPID public key available to the client as a Uint8Array
window.vapidPublicKey = new Uint8Array(<%= Base64.urlsafe_decode64(ENV['VAPID_PUBLIC_KEY']).bytes %>)
// Or you can pass it as a string if you prefer
window.vapidPublicKey = "<%= ENV['VAPID_PUBLIC_KEY'].delete('=') %>"
</script>
Your JavaScript code uses the pushManager
interface to subscribe to push notifications, passing the VAPID public key to the subscription settings.
// When serviceWorker is supported, installed, and activated,
// subscribe the pushManager property with the vapidPublicKey
navigator.serviceWorker.ready.then((serviceWorkerRegistration) => {
serviceWorkerRegistration.pushManager
.subscribe({
userVisibleOnly: true,
applicationServerKey: window.vapidPublicKey
});
});
In order to send web push notifications, the push subscription must be stored in the backend. Get the subscription with pushManager.getSubscription()
and store it in your database.
Then you can use this gem to send web push messages:
WebPush.payload_send(
message: message,
endpoint: subscription['endpoint'],
p256dh: subscription['keys']['p256dh'],
auth: subscription['keys']['auth'],
vapid: {
subject: "mailto:[email protected]",
public_key: ENV['VAPID_PUBLIC_KEY'],
private_key: ENV['VAPID_PRIVATE_KEY']
},
ssl_timeout: 5, # optional value for Net::HTTP#ssl_timeout=
open_timeout: 5, # optional value for Net::HTTP#open_timeout=
read_timeout: 5 # optional value for Net::HTTP#read_timeout=
)
Your service-worker.js
script should respond to 'push'
events. One action it can take is to trigger desktop notifications by calling showNotification
on the registration
property.
self.addEventListener('push', (event) => {
// Get the push message
var message = event.data;
// Display a notification
event.waitUntil(self.registration.showNotification('Example'));
});
Before the notifications can be displayed, the user must grant permission for notifications in a browser prompt. Use something like this in your JavaScript code:
Notification.requestPermission();
If everything worked, you should see a desktop notification triggered via web push. Yay!
message = {
title: "Example",
body: "Hello, world!",
icon: "https://example.com/icon.png"
}
WebPush.payload_send(
endpoint: "https://fcm.googleapis.com/gcm/send/eah7hak....",
message: JSON.generate(message),
p256dh: "BO/aG9nYXNkZmFkc2ZmZHNmYWRzZmFl...",
auth: "aW1hcmthcmFpa3V6ZQ==",
ttl: 600, # optional, ttl in seconds, defaults to 2419200 (4 weeks)
urgency: 'normal' # optional, it can be very-low, low, normal, high, defaults to normal
)
WebPush.payload_send(
endpoint: "https://fcm.googleapis.com/gcm/send/eah7hak....",
p256dh: "BO/aG9nYXNkZmFkc2ZmZHNmYWRzZmFl...",
auth: "aW1hcmthcmFpa3V6ZQ=="
)
VAPID details are given as a hash with :subject
, :public_key
, and
:private_key
. The :subject
is a contact URI for the application server as either a "mailto:" or an "https:" address. The :public_key
and :private_key
should be passed as the base64-encoded values generated with WebPush.generate_key
.
WebPush.payload_send(
endpoint: "https://fcm.googleapis.com/gcm/send/eah7hak....",
message: "A message",
p256dh: "BO/aG9nYXNkZmFkc2ZmZHNmYWRzZmFl...",
auth: "aW1hcmthcmFpa3V6ZQ==",
vapid: {
subject: "mailto:[email protected]",
public_key: ENV['VAPID_PUBLIC_KEY'],
private_key: ENV['VAPID_PRIVATE_KEY']
}
)
This library also supports the PEM format for the VAPID keys:
WebPush.payload_send(
endpoint: "https://fcm.googleapis.com/gcm/send/eah7hak....",
message: "A message",
p256dh: "BO/aG9nYXNkZmFkc2ZmZHNmYWRzZmFl...",
auth: "aW1hcmthcmFpa3V6ZQ==",
vapid: {
subject: "mailto:[email protected]",
pem: ENV['VAPID_KEYS']
}
)
Bug reports and pull requests are welcome on GitHub at https://github.com/pushpad/web-push.
This library is a fork of zaru/webpush actively maintained by Pushpad with many improvements, bug fixes and frequent updates.