WebPush
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).
Installation
Add this line to the Gemfile:
gem 'web-push'
Or install the gem:
$ gem install web-push
Usage
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.
Generating VAPID keys
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
Installing a service worker
Your application must use JavaScript to register a service worker script at an appropriate scope (root is recommended).
navigator.serviceWorker.register('/service-worker.js')
Subscribing to push notifications
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
});
});
Triggering a web push notification
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:sender@example.com",
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=
)
Receiving the push event
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!
API
With a payload
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
)
Without a payload
WebPush.payload_send(
endpoint: "https://fcm.googleapis.com/gcm/send/eah7hak....",
p256dh: "BO/aG9nYXNkZmFkc2ZmZHNmYWRzZmFl...",
auth: "aW1hcmthcmFpa3V6ZQ=="
)
With VAPID
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:sender@example.com",
public_key: ENV['VAPID_PUBLIC_KEY'],
private_key: ENV['VAPID_PRIVATE_KEY']
}
)
With VAPID in PEM format
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:sender@example.com",
pem: ENV['VAPID_KEYS']
}
)
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/pushpad/web-push.
Credits
This library is a fork of zaru/webpush actively maintained by Pushpad with many improvements, bug fixes and frequent updates.