HTML5 Push Notifications without google

Hello,

I just enabled HTML5 push notifications, and it works really well.
however I would like to have a solution without google.
now I need a google vapid key, not really sure what a vapid key is.
but is this possible using another party, or maybe without registering at all?

Look at this

Or might I recommend one of these: Integrations - Home Assistant

Note that the mobile app, which is what I use, uses google on the backend for notifications (I think). Mobile App - Home Assistant

at the moment I already use this one

@tmjpugh I haven’t had the time to fully investigate the source,
but if I read it correctly it says, I only need gcm for pushnotifications on android.
gcm in HA is replaced by vapid, I don’t know the details.

But that would mean, If I want pushnotifications in firefox only, In theory I wouldn’t need a vapid key.

I think to fully understand this I need to create a poc on my own. But with my current planning it will take a couple of weeks before I have the time.

1 Like

I couldn’t find any reason in a commit, PR, or issue on why we should use Firebase. Does anyone here know why we should use Firebase generated vapid keys, or can we generate our own keys without Firebase, and use them instead?

Yeah, the need to provide a private key to an outside service in this scenario would seem to entirely defeat the purpose of using asymmetric crypto. I got suspicious and dug into the specs.

So. Long story short: no, Firebase is not required, you can generate the keys locally and they’ll just work. You need an elliptic-curve key of type prime256v1, urlsafely-base64-encoded without padding (example code below).

The way this works is: your browser may have a certain push service configured, and when asked via Push API (MDN), it returns, among other things, an endpoint that should be used for sending notifications to that device.

You can see those in the html5_push_registrations.conf file next to your config once you enable push notifications somewhere. Chrome returns endpoints at fcm.googleapis.com, Firefox at updates.push.services.mozilla.com. Such a service should be possible to self-host, but haven’t looked into that.


I generated my own keys using this snippet of Ruby, guided by this Google Developers post and the RFC8292 on VAPID:

require "openssl"
require "base64"

key = OpenSSL::PKey::EC.generate("prime256v1")
format = proc { |k| Base64.urlsafe_encode64(k.to_bn.to_s(2), padding: false) }
print "vapid_prv_key: \"", format.(key.private_key), "\"\n"
print "vapid_pub_key: \"", format.(key.public_key), "\"\n"

(You don’t have to use Ruby, it’s probably easier in NodeJS using the examples in the Google Devs post, I just wanted a different implementation to validate my understanding; it probably too uses OpenSSL underneath, but to me that’s good enough)

Just inserted these into Home Assistant’s config as per the documentation, without registering it anywhere else.

And it worked! :heavy_check_mark:
…mostly.

Oddly enough, it worked only with Chrome — but not Firefox, neither on WIndows or Android. But that may be breakage on my end — the fact that it works with Chrome would confirm that it’s configured correctly.
Bromite (a fork of Chromium-Android with privacy enhancements) doesn’t seem to have a push service configured at all, or maybe my device is missing something that it needs.


PS: key generation via Node.js:

function format(k) {
  // + and / replacements are all that's needed to produce urlsafe-base64
  // out of normal base64. And they're available natively in NodeJS v14!
  // I had v12 😐
  return k.toString('base64').replace("+", "-").replace("/", "_").replace("=", "");
}
var curve = crypto.createECDH('prime256v1');
curve.generateKeys();
format(curve.getPublicKey());
format(curve.getPrivateKey());
1 Like

I still need to figure out what’s up with notifications not working on Firefox. Do they work for anyone? I tried the same experiment on a clean install — no dice. But my network, let me put it this way, is riddled with holes and random malfunctions graciously provided by our government. Oh well. Should try a VPN I guess.

So-o… I mean, I could just update documentation on the subject anyway, but I’d have to provide a reasonably accessible way to generate a key like I did.

Ruby or Node.js do work, but they aren’t necessarily common on machines running Home Assistant.

And I utterly failed at generating these through just shell scripting with openssl. I can’t quite find a way to get it to export a key without a container.

Python would work, but at that point maybe it should just be built into Home Assistant itself, and it would be a great time to transition this integration to be configurable from a GUI — because as far as I can tell, while push services are indeed in the cloud, they are the, in the grand scheme of things, the client side of the solution. The server side, that remembers recipients and sends notifications, is contained entirely in HA.

1 Like

Html5 Push works in firefox when i used 1yr ago

There is method for testing but i forget and will post if i find it

It has worked for me in the past. But for a few months back it stopped working in ff. In edge all still works.

Apparently the Web Push API didn’t support VAPID for identification when it was first introduced. We had to use an alternative authentication method provided by the browser. Google Chrome and some other (chromium) browsers used the proprietary Google Cloud Messaging (GCM) API which was later replaced by Firebase Cloud Messaging (FCM). GCM required that all messages should be send and authenticated through Google. Home Assistant and other applications had to send notifications to the GCM API, authenticate with a gcm_api_key and gcm_sender_id (provided by Google in a cloud project), and GCM would relay these messages to the browser. When VAPID was introduced and added to the Push API in 2017, it was no longer necessary to use GCM, but we kept using it. In 2018 GCM got deprecated and users were asked to move to FCM and so did Home Assistant. I expect that this is the reason why the integration documentation asks for the Firebase keys instead of generating your own. We shouldn’t have moved to FCM since we never needed the advanced features of FCM and GCM, we only want to send notifications which is now possible with our own key.

1 Like

This will be fixed in 2022.12.0, see #82556.

1 Like

Thanks,
I was just able to test and it works,
still need to add a dummy email for “vapid_email”

There may be some value in providing an actual email there. See the RFC on why.

You can use your browser to generate the key pair:

  1. Open your browser console: Ctrl+Shift+K (or Right click → Inspect → Console).
  2. Paste the following code ans press Enter:
 crypto.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, ['sign'])
  .then((keyPair) => {
    crypto.subtle.exportKey('jwk', keyPair.privateKey)
    .then((jwk) => {
      const publicKeyArray = "\x04"
        + atob((jwk.x).replace(/\-/g, "+").replace(/\_/g, "/"))
        + atob((jwk.y).replace(/\-/g, "+").replace(/\_/g, "/"));
      const publicKeyBase64 = btoa(publicKeyArray)
        .replace(/\=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
      console.log("Public: " + publicKeyBase64);
      console.log("Private: " + jwk.d);
    });
  });

Or you can use openssl:

openssl ecparam -name prime256v1 -genkey -noout -out key.pem
echo "Public: $(openssl ec -in ./key.pem -pubout -outform DER|tail -c 65|base64|tr -d '=\n' |tr '/+' '_-')"
echo "Private: $(openssl ec -in ./key.pem -outform DER|tail -c +8|head -c 32|base64|tr -d '=' |tr '/+' '_-')"
3 Likes