Giter VIP home page Giter VIP logo

webpush's Introduction

WebPush

Code Climate Test Coverage Build Status Gem Version

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 to send messages securely from server to user agent.

Payload is supported by Chrome 50+, Firefox 48+, Edge 79+.

webpush Demo app here (building by Sinatra app).

Installation

Add this line to your application's Gemfile:

gem 'webpush'

And then execute:

$ bundle

Or install it yourself as:

$ gem install webpush

Usage

Sending a web push message to a visitor of your website requires a number of steps:

  1. Your server has (optionally) generated (one-time) a set of Voluntary Application server Identification (VAPID) keys. Otherwise, to send messages through Chrome, you have registered your site through the Google Developer Console and have obtained a GCM sender id and GCM API key from your app settings.
  2. A manifest.json file, linked from your user's page, identifies your app settings.
  3. Also in the user's web browser, a serviceWorker is installed and activated and its pushManager property is subscribed to push events with your VAPID public key, with creates a subscription JSON object on the client side.
  4. Your server uses the webpush gem to send a notification with the subscription obtained from the client and an optional payload (the message).
  5. 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 webpush to generate a VAPID key that has both a public_key and private_key attribute to be saved 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

Declaring manifest.json

Check out the Web Manifest docs for details on what to include in your manifest.json file. If using VAPID, no app credentials are needed.

{
  "name": "My Website"
}

For Chrome web push, add the GCM sender id to a manifest.json.

{
  "name": "My Website",
  "gcm_sender_id": "1006629465533"
}

The file is served within the scope of your service worker script, like at the root, and link to it somewhere in the <head> tag:

<!-- index.html -->
<link rel="manifest" href="/manifest.json" />

Installing a service worker

Your application javascript must register a service worker script at an appropriate scope (we're sticking with the root).

// application.js
// Register the serviceWorker script at /serviceworker.js from your server if supported
if (navigator.serviceWorker) {
  navigator.serviceWorker.register('/serviceworker.js')
  .then(function(reg) {
     console.log('Service worker change, registered the service worker');
  });
}
// Otherwise, no push notifications :(
else {
  console.error('Service worker is not supported in this browser');
}

Subscribing to push notifications

With VAPID

The VAPID public key you generated earlier is made available to the client as a UInt8Array. To do this, one way would be to expose the urlsafe-decoded bytes from Ruby to JavaScript when rendering the HTML template. (Global variables used here for simplicity).

window.vapidPublicKey = new Uint8Array(<%= Base64.urlsafe_decode64(ENV['VAPID_PUBLIC_KEY']).bytes %>);

Your application javascript uses the navigator.serviceWorker.pushManager to subscribe to push notifications, passing the VAPID public key to the subscription settings.

// application.js
// 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
  });
});

Without VAPID

If you will not be sending VAPID details, then there is no need generate VAPID keys, and the applicationServerKey parameter may be omitted from the pushManager.subscribe call.

// application.js
// When serviceWorker is supported, installed, and activated,
// subscribe the pushManager property with the vapidPublicKey
navigator.serviceWorker.ready.then((serviceWorkerRegistration) => {
  serviceWorkerRegistration.pushManager
  .subscribe({
    userVisibleOnly: true
  });
});

Triggering a web push notification

Hook into an client-side or backend event in your app to deliver a push message. The server must be made aware of the subscription. In the example below, we send the JSON generated subscription object to our backend at the "/push" endpoint with a message.

// application.js
// Send the subscription and message from the client for the backend
// to set up a push notification
$(".webpush-button").on("click", (e) => {
  navigator.serviceWorker.ready
  .then((serviceWorkerRegistration) => {
    serviceWorkerRegistration.pushManager.getSubscription()
    .then((subscription) => {
      $.post("/push", { subscription: subscription.toJSON(), message: "You clicked a button!" });
    });
  });
});

Imagine a Ruby app endpoint that responds to the request by triggering notification through the webpush gem.

# app.rb
# Use the webpush gem API to deliver a push notiifcation merging
# the message, subscription values, and vapid options
post "/push" do
  Webpush.payload_send(
    message: params[:message],
    endpoint: params[:subscription][:endpoint],
    p256dh: params[:subscription][:keys][:p256dh],
    auth: params[:subscription][:keys][:auth],
    vapid: {
      subject: "mailto:[email protected]",
      public_key: ENV['VAPID_PUBLIC_KEY'],
      private_key: ENV['VAPID_PRIVATE_KEY']
    },
    ssl_timeout: 5, # value for Net::HTTP#ssl_timeout=, optional
    open_timeout: 5, # value for Net::HTTP#open_timeout=, optional
    read_timeout: 5 # value for Net::HTTP#read_timeout=, optional
  )
end

Note: the VAPID options should be omitted if the client-side subscription was generated without the applicationServerKey parameter described earlier. You would instead pass the GCM api key along with the api request as shown in the Usage section below.

Receiving the push event

Your /serviceworker.js script may respond to 'push' events. One action it can take is to trigger desktop notifications by calling showNotification on the registration property.

// serviceworker.js
// The serviceworker context can respond to 'push' events and trigger
// notifications on the registration property
self.addEventListener("push", (event) => {
  let title = (event.data && event.data.text()) || "Yay a message";
  let body = "We have received a push message";
  let tag = "push-simple-demo-notification-tag";
  let icon = '/assets/my-logo-120x120.png';

  event.waitUntil(
    self.registration.showNotification(title, { body, icon, tag })
  )
});

Before the notifications can be displayed, the user must grant permission for notifications in a browser prompt, using something like the example below.

// application.js

// Let's check if the browser supports notifications
if (!("Notification" in window)) {
  console.error("This browser does not support desktop notification");
}

// Let's check whether notification permissions have already been granted
else if (Notification.permission === "granted") {
  console.log("Permission to receive notifications has been granted");
}

// Otherwise, we need to ask the user for permission
else if (Notification.permission !== 'denied') {
  Notification.requestPermission(function (permission) {
    // If the user accepts, let's create a notification
    if (permission === "granted") {
      console.log("Permission to receive notifications has been granted");
    }
  });
}

If everything worked, you should see a desktop notification triggered via web push. Yay!

Note: if you're using Rails, check out serviceworker-rails, a gem that makes it easier to host serviceworker scripts and manifest.json files at canonical endpoints (i.e., non-digested URLs) while taking advantage of the asset pipeline.

API

With a payload

message = {
  title: "title",
  body: "body",
  icon: "http://example.com/icon.pn"
}

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:[email protected]",
    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:[email protected]"
    pem: ENV['VAPID_KEYS']
  }
)

With GCM api key

Webpush.payload_send(
  endpoint: "https://fcm.googleapis.com/gcm/send/eah7hak....",
  message: "A message",
  p256dh: "BO/aG9nYXNkZmFkc2ZmZHNmYWRzZmFl...",
  auth: "aW1hcmthcmFpa3V6ZQ==",
  api_key: "<GCM API KEY>"
)

ServiceWorker sample

see. https://github.com/zaru/web-push-sample

p256dh and auth generate sample code.

navigator.serviceWorker.ready.then(function(sw) {
  Notification.requestPermission(function(permission) {
    if(permission !== 'denied') {
      sw.pushManager.subscribe({userVisibleOnly: true}).then(function(s) {
        var data = {
          endpoint: s.endpoint,
          p256dh: btoa(String.fromCharCode.apply(null, new Uint8Array(s.getKey('p256dh')))).replace(/\+/g, '-').replace(/\//g, '_'),
          auth: btoa(String.fromCharCode.apply(null, new Uint8Array(s.getKey('auth')))).replace(/\+/g, '-').replace(/\//g, '_')
        }
        console.log(data);
      });
    }
  });
});

payloads received sample code.

self.addEventListener("push", function(event) {
  var json = event.data.json();
  self.registration.showNotification(json.title, {
    body: json.body,
    icon: json.icon
  });
});

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/zaru/webpush.

webpush's People

Contributors

collimarco avatar glennr avatar kitaindia avatar kzaitsev avatar medetaiakaru avatar mohamedhafez avatar morgoth avatar mplatov avatar nicolas-fricke avatar rossta avatar stevenharman avatar tonytonyjan avatar waheedel avatar wolfer avatar xronos-i-am avatar ykzts avatar zaru avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

webpush's Issues

Bump to version 1.0.0 after PR #84 gets merged

Hey @zaru, considering that this is now a stable library thats fit for production use at this point, I think it would be a good idea to bump the version to 1.0.0 after #84 gets merged to communicate that. If more people think its a usable library instead of still in development and not ready for production use, we're likely to get more users, some of whom will also help contribute to the project

Errors raised by 410 and 404 incorrectly switched

It looks like a previous commit by @collimarco switched the errors we raise on 410 and 404 in https://github.com/zaru/webpush/blob/master/lib/webpush/request.rb#L29-L32

However according to https://developers.google.com/web/fundamentals/push-notifications/common-issues-and-reporting-bugs#http_status_codes, it was correct initially, with 410 meaning "Subscription No Longer Valid" and 404 meaning "Subscription Expired". RFC 8030 also explicitly says 404 means "Subscription Expired" (410 is more complicated, meaning that the push service couldn't reach the user agent, is done retrying, and won't send anything for this subscription any more)

Authorization error 401 from chrome as well as firefox

  • From chrome browser
    Webpush::ResponseError (host: fcm.googleapis.com, #<Net::HTTPForbidden 403 Forbidden readbody=true>
    body:
    exp claim MUST NOT be more than 24 hours from the time of the request
    ):

  • From Firefox browser
    Webpush::ResponseError (host: updates.push.services.mozilla.com, #<Net::HTTPUnauthorized 401 Unauthorized readbody=true>
    body:
    {"code": 401, "errno": 109, "error": "Unauthorized", "more_info": "http://autopush.readthedocs.io/en/latest/http.html#error-codes", "message": "Request did not validate Invalid bearer token: Auth > 24 hours in the future"}):

Getting Error with ruby 1.9.3

@rossta I tried to send payload on chrome but I am getting below mentioned error:
NoMethodError: undefined method auth_tag=' for #<OpenSSL::Cipher:0x00000006510d28> from .rvm/gems/ruby-1.9.3-p551@test/gems/webpush-0.2.1/lib/webpush/encryption.rb:63:in encrypt_payload' from .rvm/gems/ruby-1.9.3-p551@test/gems/webpush-0.2.1/lib/webpush/encryption.rb:33:in encrypt' from .rvm/gems/ruby-1.9.3-p551@test/gems/webpush-0.2.1/lib/webpush.rb:42:in build_payload' from .rvm/gems/ruby-1.9.3-p551@test/gems/webpush-0.2.1/lib/webpush.rb:32:in payload_send' from (irb):2
I am using ruby 1.9.3 and I tried to hit Webpush.payload_send method via rails console but getting auth tag issue.

Windows Push Notification Services: 406 Not Acceptable

We see a few delivery errors (Webpush::ResponseError) from our logs for the Windows Push Notification Services (WNS):

host: wns2-sg2p.notify.windows.com, #<Net::HTTPNotAcceptable 406 Not Acceptable readbody=true>
body:

All the browsers that we have tested on Windows 11 (including Edge - v105) work properly. The notifications are delivered successfully and no exception is raised.

This is probably some misconfiguration in a specific server of WNS, some transient errors on WNS or some strange browser version that doesn't work properly (e.g. returning wrong push subscriptions that create an anomalous response from WNS).

In any case I open this issue in case anyone can reproduce this problem or has more information.

404 responses for Firefox endpoints

I've been getting some 404 responses for Firefox endpoints recently. 410 means the user unsubscribed, but I'm not sure what 404 means or how to handle it. It seems to be happening for some older subscriptions, perhaps they've expired and need to be renewed or something?

We should catch this error and raise an appropriate exception, the same way we raise InvalidSubscription exceptions for 410 responses.

Add rake task to generate VAPID keys

Currently its necessary to open the rails console in order to generate a new key for a new environment. This makes it more difficult to script new environments since theres no rake task that can produce a new public and private key.

Maybe something like this? (first is public and second is private)

$ rake webpush:generate_key
BOuA6thueNgTtxIxt0ZoQb6nhCwzeKXPIZDrT461vm9rCaE328vKOa5wODA9w6G_R4pqK4fIlg1THXLG679JLQs=
2LjsdvOo6tUvQdQ50V0FD4bn9jAkgddllSPPWYfoHoI=

Webpush::ExpiredSubscription on my device even that I haven't unsubscribed

I have been testing on Chrome Android, Opera & Firefox all latest versions to this date. When I first subscribe, everything goes fine but after 2-4 notifications I get Webpush::ExpiredSubscription, even that I haven't unsubscribed and when I check my browser I still see in browser settings that I have "allowed" notifications.

This is the error I get when I test send:
host: fcm.googleapis.com, #<Net::HTTPGone 410 NotRegistered readbody=true> body: <HTML> <HEAD> <TITLE>NotRegistered</TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF" TEXT="#000000"> <H1>NotRegistered</H1> <H2>Error 410</H2> </BODY> </HTML>

I'm not sure why this is still happening and I have tested with several devices with the same result.

Does anyone have any clue about this? I appreciate any help and thanks in advance!

unexpected, temporary 401 responses from Google

Just since the last few days, I've been getting a lot of temporary 401 HTML responses from just Google, however retrying a few minutes later works with no issue. the fact that its an HTML response makes me think it might be a glitch on Google's end? here's an example response:

<!DOCTYPE html>
<html lang=en>
  <meta charset=utf-8>
  <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
  <title>Error 401 (Unauthorized)!!1</title>
  <style>
    *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/logos/errorpage/error_logo-150x54.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/logos/errorpage/error_logo-150x54-2x.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/logos/errorpage/error_logo-150x54-2x.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/logos/errorpage/error_logo-150x54-2x.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
  </style>
  <a href=//www.google.com/><span id=logo aria-label=Google></span></a>
  <p><b>401.</b> <ins>That’s an error.</ins>
  <p>  <ins>That’s all we know.</ins>

If you guys agree this is just some kind of temporary glitch, perhaps we shouldn't be raising an Unauthorized error at https://github.com/zaru/webpush/blob/master/lib/webpush/request.rb#L33 that would cause the receiver to think that the registration token is no longer valid?

wrong vapid keys?

HI there!

I've generated VAPID keys as follows

web-push generate-vapid-keys --json

and save it to a json file.

Then, on my node app, I serve the public key to auth clients:

  getPublicKey() {
    return Buffer.from(config.publicKey).toString('base64');
  }

Then on the client I use this function from firebase-sdk:

const base64ToArrayBuffer = (base64String) => {
  const padding = '='.repeat((4 - base64String.length % 4) % 4);
  const base64 = (base64String + padding)
    .replace(/\-/g, '+')
    .replace(/_/g, '/');

  const rawData = atob(base64);
  const outputArray = new Uint8Array(rawData.length);

  for (let i = 0; i < rawData.length; ++i) {
    outputArray[i] = rawData.charCodeAt(i);
  }
  return outputArray;
}

However I have this error over and over :|

Unable to subscribe to push. DOMException: Failed to execute 'subscribe' on 'PushManager': The provided applicationServerKey is not valid.

What im doing wrong?? :\

Thanks!

Safari 16 on MacOS now supports the Push API, but push messages fail

Safari 16 on MacOS now supports the Push API (instead of the legacy APNs protocol).

Unfortunately we see that all messages sent with this gem fail with this error:

400 Bad Request
{"reason":"BadUrgency"}

This seems a bug related to the Apple push service (since "normal", "high", etc. are valid values), however I report it here since it also affects this gem.

You can also use https://feedbackassistant.apple.com/ to report this bug (as we already did), so that they will prioritize this issue.

ArgumentError: invalid base64

When i am trigerring push notifications, in case of chrome they are delievering successfully but in case of firefox they are always giving invalid base64 erro,. after this

 Webpush.payload_send(
        endpoint: device.endpoint,
        p256dh: device.p256dh,
        auth: device.auth,
        cert_store: OpenSSL::X509::Store.new.tap(&:set_default_paths),
        message: message_json(notification),

        api_key: ENV["GCM_KEY"]
      )

Please suggest some solution.

Generating keys on every push unnecessarily

It looks like every time we send out a push, we are generating new public and private keys, and then immediately overwriting them at https://github.com/zaru/webpush/blob/master/lib/webpush/vapid_key.rb#L10-L12

From what I understand, generating keys is relatively expensive, no? If so perhaps this would use unnecessary CPU for high-volume users? I don't have much experience dealing directly with OpenSSL, but I'm guessing there's a way to recreate the OpenSSL::PKey::EC object without having to generate a throwaway set of keys first, @rossta just checking was there a reason for this I'm missing before I dig into this further?

where to place VAPID keys

Docs says

# Save these in your application server settings
vapid_key.public_key
vapid_key.private_key

can you clarify like put in application.rb or what?

Undefined method `end_with?' for nil:NilClass

I successfully installed and used this gem in my local env but when pushing to heroku i get the error below:
Heroku is running Ruby 2.4.4, I tried forking the gem and using just the "decode64" vs the url_safe one but nothing doing.
See screenshots with all info.

Tested it on heroku console:
image

Keys:
image

Keys to test:
p256dh: "BHvZT7egzCyP6kABrWw4MzddvWfpnHmgis41VoAJdHxPf0L9VwUV4wrBTG0CK_PKHirthgdXQViwNNdTs_qQ8E4="
auth: "TCiIvtlR3snOu80-E-60fQ=="

RSpec testing?

Hello, thank you for such a fantastic gem that is so easy to use!

I was wondering if there are any known strategies for testing with RSpec - to write request tests that can report if a Webpush notification was in fact sent. Forgive me if this is inherently obvious to others.

Intermittent Webpush::Unauthorized errors from FCM

Only in the last month or so, I've intermittently been getting Webpush::Unauthorized errors, with message invalid push subscription endpoint for Chrome users. It only affects 2-3 users, keeps up for about a day for every message sent to those 2-3 users, and then without me changing anything messages to those users start going through without issue. But then a different 2-3 users start having the problem for about a day.

Anybody else experiencing this? Any idea what could be going on?

Importing users from OneSignal gives Net::HTTPForbidden 403 MismatchSenderId

I've built my own platform for internal usage and imported all of my users from OneSignal over to my application, but when I sent any notification to any of them, I get: Webpush::Unauthorized exception & Net::HTTPForbidden 403 MismatchSenderId

Webpush::Unauthorized in PushSenderController#push_tester_sender

host: gcm-http.googleapis.com, #<Net::HTTPBadRequest 400 UnauthorizedRegistration readbody=true> body: <HTML> <HEAD> <TITLE>UnauthorizedRegistration</TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF" TEXT="#000000"> <H1>UnauthorizedRegistration</H1> <H2>Error 400</H2> </BODY> </HTML>

skjermbilde 2019-03-06 18 55 24

Anyone knows how I can solve this?

I appreciate any help and thanks in advance! 💯

How to handle Net::HTTPBadRequest 400 InvalidTokenFormat?

From time to time we're getting this error:

vendor/bundle/ruby/2.4.0/gems/webpush-0.3.4/lib/webpush/request.rb:39:in `perform': host: fcm.googleapis.com, #<Net::HTTPBadRequest 400 InvalidTokenFormat readbody=true>
body:
<HTML>
<HEAD>
<TITLE>InvalidTokenFormat</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>InvalidTokenFormat</H1>
<H2>Error 400</H2>
</BODY>
</HTML>
 (Webpush::ResponseError)
    from vendor/bundle/ruby/2.4.0/gems/webpush-0.3.4/lib/webpush.rb:42:in `payload_send'

Any reason why this isn't handled as Webpush::InvalidSubscription by the gem?

Extract logic to ece gem?

I was looking into writing a webpush ruby implementation and came across your gem. Nice work!

In my research I can across another gem, ece, that abstracts the HTTP encrypted content protocol.

Would you be interested in a PR to introduce ece as a dependency to extract the encryption logic in your gem?

Raising errors when sending request fails, detecting when subscription no longer valid

Right now, when a request fails, nothing happens and the only way the user would know is by inspecting the response object returned from Webpush::Request#perform. The user could parse the response to figure out why the failure happened, but I think this should be taken care of by the gem.

I've got a patch to detect when the failure is because the subscription/endpoint is no longer valid and then raise a InvalidSubscription error, and then if the request fails for any other reason a ResponseError error is raised, and the only way the method would return without raising an exception is if the request went through. If that sounds good to everyone I'll issue a pull request

webpush fails with new jwt and Chrome.

The jwt gem was just updated to version 2.0.0 and that version causes webpush to no longer send push messages to Chrome.

I had to downgrade jwt to version 1.5.6 for Chrome to start working again so it would be nice to include that in the Gemfile of webpush for now. I can create a PR if you agree.

Where should the post endpoint be placed

I've been unable to make the endpoint work the way it is stated on the read me. It says:

"Imagine a Ruby app endpoint that responds to the request by triggering notification through the webpush gem."

And the code itself says it should go on app.rb but so far I've had errors on server start up by placing the code inside application.rb

I also tried creating a route and executing the code inside a controller but I get the following error:

NoMethodError in DemoController#push
undefined method `gsub' for nil:NilClass

I'm using rails 4.2.6

What am I missing to make this work?

HTTPBadRequest 400 UnauthorizedRegistration on Chrome only

I'm getting this 400 error when sending notifications to Chrome, but on firefox everything works as expected.

Is there some additional parameters that need to be send on chrome? My request is sent using:

    Webpush.payload_send(
      message: message,
      endpoint: endpoint_url,
      p256dh: p256dh,
      auth: auth,
      vapid: {
        public_key: vapid_keypair['public_key'],
        private_key: vapid_keypair['private_key']
      }
    )

Got an error: 403 Forbidden, exp more than 24 hours

My webpush doesn't work on AWS server and got this error: "403 forbidden, exp claim must not more than 24 hours from the time of the request". But it works fine on local console.
My code:

Webpush.payload_send(
  endpoint: s.end_point,
  message: {
    title: message_body,
    url: url.to_s
  }.to_json,
  p256dh: s.p256dh,
  auth: s.auth,
  ttl: 24 * 60 * 60,
  vapid: {
    subject: "mailto:[email protected]",
    public_key: Rails.application.secrets.WEB_PUSH_PUBLIC_KEY,
    private_key: Rails.application.secrets.WEB_PUSH_PRIVATE_KEY
  }
)

Is it because I miss something in the payload_send method for authentication or should I add exp at somewhere

Sending notifications to a specific service worker

If I want to fire off notifications each time a sale is made, how would I do that since the notifications involve two separate service workers? One is registered to the seller and one is registered to the buyer - As far as I can tell, to send a notification they must both be registered the same service worker.

Looking for a new maintainer?

Hello,

I have opened some issues and pull requests over a year ago for this repository.

I see that you are probably busy or focused on other projects at the moment. @zaru

Currently I use a fork of this gem with various improvements.

I was wondering if you are interested in merging the changes or maybe if you are looking for a new maintainer.

I would be really happy to contribute / maintain this project - since I actively use it. In case, let me know.

Web push failed? only for chrome after succeed once

I have created a simple web push demo. So far, I can send notification to firefox without problem.

However, I can only do it once for chrome for some unknown reasons.

The way I have setup:

  • by generating VAPID public and private keys to send notification to chrome (v73, mac os)
  • adding api_key to webpush from backend
  • adding project number (obtained from google console) which is also known as gcm_sender_id to manifest.json

Here is the dashboard from google console, in case it helps
google_console_fcm

Also, there was an issue mentioned about the gem would not work with jwt above v2? It would be great if anyone can clarify about this.

WebPush, 0.2.5, Webpush::InvalidSubscription (#<Net::HTTPBadRequest 400 UnauthorizedRegistration readbody=true>):

Hi

I'm making a first venture in push notifications using gcm and your timely gem.

I'm suffering from the error above, even when I send without payload.

Double and triple checked, 1) the id in manifest.json 2) the endpoint, the keys 3) the apikey.

I'm getting everything back from the service worker as I expect but I'm failing on this bit:

Webpush.payload_send( message: JSON.generate(message), endpoint: endpoint, p256dh: p256dh, auth: auth, api_key: api_key)

Any ideas of further things I could try? I'm kind of stuck.

Thanks!

Tom

Push messages silently fail in Safari 16.4 on Mac OS Ventura

Hi. I managed to setup this gem with my Rails application & I can send push notifications to both Chrome and Firefox now. (Yeah!)

However, unfortunately, push notifications to Safari (version 16.4 on Mac OS Ventura) are still (silently!) failing.

When I send a test message from the console, I get always get a success message like this...

#<Net::HTTPCreated 201 Created readbody=true>

...even when sending to Safari. However, the message then never pops up in Safari, despite having notifications enabled all over the place (in Safari settings, Mac OS settings, etc.).

What am I missing here?

HTTPBadRequest 400 UnauthorizedRegistration

Some days ago i noticed all my pushes have been rejected by server. But a week ago all was OK.

Webpush.payload_send(message:  JSON.generate(message),
                             endpoint: sub.gcm_url,
                             p256dh:   sub.key_p,
                             auth:     sub.key_a,
                             api_key:  GOOGLE_API_KEY)

returns me #<Net::HTTPBadRequest 400 UnauthorizedRegistration readbody=true>

May be google have made any changes in GCM?

VAPID support

Chrome 52 and Firefox 48, both currently in beta and due to be released late July / early August, are both implementing VAPID, are there any plans to update this library to support that?

Please update this library to use RFC 8188 Content encoding spec

First off, thanks for this library.

RFC 8188 has been out for a while, and services will soon start deprecating the "aesgcm" encoding type. The differences aren't that much (the salt is included in the data prefix, and the nonce phrases changed to reflect the new content encoding schema).

Thanks!

web push notifications does not show up if there is already unopened notification

I have followed the instructions and i have success implemented it, (i got the push notification). But, there is a problem i faced right now when i have received my push notification.

If i didn't open/closed my previous notification, i couldn't get any new push notification until i closed my previous notification, i have tried to shorten the ttl (expiration time), but it won't closed by itself after the expiration time is expired (it didn't happen anything)

Webpush.payload_send( message: params[:message], endpoint: admin.subscription[:endpoint], p256dh: admin.subscription[:keys][:p256dh], auth: admin.subscription[:keys][:auth], ttl: 10, //10 seconds based on documentation vapid: { subject: 'mailto:[email protected]', public_key: ENV['VAPID_PUBLIC_KEY'], private_key: ENV['VAPID_PRIVATE_KEY'] } )

how long is a "user" token+auth valid?

it looks like the google docs say just trust them for 24h? so if a user got a token he only gets web push notifications for 24h and have to revisit my page to get new tokens?

Method `request#perform` does not return a detailed response

I am using webpush gem to send web push to GCM, its really easy to use, but I found that when I got some errors, I can not get the response details from Webpush. payload_send, so I am wondering if PR #10 is welcome to make it return the raw http response or raise a error when an unexpected error happened?

HTTP status codes raise the wrong exceptions

This is the correct code:

 if resp.is_a?(Net::HTTPNotFound) # 404
   raise InvalidSubscription.new(resp, uri.host)
 elsif resp.is_a?(Net::HTTPGone) # 410
    raise ExpiredSubscription.new(resp, uri.host)

Basically in your current code you have inverted Net::HTTPNotFound and Net::HTTPGone.

I am sure about this because I have run a push service for many years and we remove expired subscriptions when they return 410. The code 404 is very rare and you never see it in normal conditions.

I would also move Net::HTTPBadRequest (UnauthorizedRegistration) to a different exception since it is caused by an authentication error (for example wrong vapid keys):

elsif resp.is_a?(Net::HTTPUnauthorized) || # Mozilla autopush
  resp.is_a?(Net::HTTPBadRequest) && resp.message == "UnauthorizedRegistration" # Google FCM
  raise Unauthorized(resp, uri.host)

I would be happy to provide a pull request if you agree with the above changes.

Webpush::InvalidSubscription: #<Net::HTTPBadRequest 400 UnauthorizedRegistration

Hi there. I'm trying to implement VAPID, but it works very strange: some pushes has been received, and some pushed raising an error (Webpush::InvalidSubscription: #<Net::HTTPBadRequest 400 UnauthorizedRegistration readbody=true> or Webpush::InvalidSubscription: #<Net::HTTPGone 410 NotRegistered readbody=true>). Why does FCM rejecting 50% of pushes? Thanks in advance

Rescue for exeptions in loop when sending out push

First of all, great gem 🥇

In my application, I ran a loop over subscribers in my controller and send a notification to them.

subscribers.each do |sub|
  begin
    Webpush.payload_send(
      endpoint: sub.endpoint,
      message: JSON.generate(data),
      p256dh: sub.p256dh_key,
      auth: sub.auth_key,
      vapid: {
        subject: 'https://my-site.com',
        public_key: 'vapid_public',
        private_key: 'vapid_private'
      }
    )
  rescue Webpush::Unauthorized
    sub.update(status: 0)
    next
  rescue OpenSSL::PKey::EC::Point::Error
    sub.update(status: 0)
    next
  end
end

I'm now suing rescue for Webpush::Unauthorized & OpenSSL::PKey::EC::Point::Error for updating sunscriber's status.

PS: I got Webpush::Unauthorized when I inserted wrong endpoint and OpenSSL::PKey::EC::Point::Error when p256dh was wrong

I'm not sure if there are other exceptions/rescue I can add or a better way to add rescue so the loop doesn't stop and can continue in case any user data is not correct.

Any help is appreciated and thanks in advance! 🍻

pkeys are immutable on OpenSSL 3.0 (OpenSSL::PKey::PKeyError)

server.generate_key

I have an app on heroku and when i try to do a payload_send it fails but in my local environment it works fine.

Apparently it is because the implementation of the generate_key method of the Encryption module is no longer supported. And I get the following error: /webpush/encryption.rb:15:in generate_key!': pkeys are immutable on OpenSSL 3.0 (OpenSSL::PKey::PKeyError)

In my local environment:

irb>> OpenSSL::OPENSSL_VERSION
=> "OpenSSL 1.1.1n 15 Mar 2022"

In production on Heroku:

irb>> OpenSSL::OPENSSL_VERSION
=> "OpenSSL 3.0.1 14 Dec 2021"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.