11

I've implemented a Web Push API with the help of this Google tutorial: https://developers.google.com/web/fundamentals/getting-started/codelabs/push-notifications/

The tutorial includes a method, which could be used to send the subscription to a backend server:

function updateSubscriptionOnServer(subscription) {
  // TODO: Send subscription to application server
  ...
}

I want to store the subscription together with a user identifier, so I can choose which users I want to send a notification. Now I'm wondering, which part of the subscription I've to store in the database. My subscription objects look like this:

{"endpoint":"https://fcm.googleapis.com/fcm/send/...","keys":{"p256dh":"...","auth":"..."}}

Should I just create a table "Subscription" with columns (Id|User_Id|Subscription), which includes the whole JSON subscription? What is best practice for that? Is the JSONs validity unlimited? Which field can I use to identify a subscription in case of unsubscription?

mana
  • 6,347
  • 6
  • 50
  • 70
AndrejDelany
  • 423
  • 4
  • 18
  • I'm storing the whole JSON, but I'm also stumped as to how to delete the subscription row when a user unsubscribes. – Flimm May 12 '17 at 09:30

3 Answers3

6

To allow anonymous web-push-users to use our service, we've come up with the idea to hash the subscription and use the hash as the backend's primary key (id).

// pseudo-code
const id = hash(subscription);
https.post(`${SERVER}/subscription/${id}`, subscription).then(...);

As I understand correctly, the subscription does not change over time and therefore, the hash is a constant. Collisions are not to be expected since every subscription is unique and you may choose an appropriate hash function (e.g. SHA-512).

You can then re-validate the subscription in your Service Worker or handle pushsubscriptionchange events as long as you can access the original subscription and re-create the hash/id.

This should cover most of the cases. But still, when sending the push-notification through Google's and Firefox's push-servers, you'll still need to respect the response codes (HTTP 401) for invalid subscriptions and remove them from your database.

See Javascript Crypto functionality for available hash-functions. Supported by FF and Chrome.

mana
  • 6,347
  • 6
  • 50
  • 70
  • 1
    This identifier is also handy when you need to tell apart multiple subscriptions from the same user on different devices. The device will send the hash and then update 'lastSeen' field in the DB, on that specific subscription. Later on, I can use it to remove inactive subscriptions. – Jette Feb 14 '19 at 10:03
  • What happens if a specific user decides to send multiple subscriptions to the backend server's endpoint that is used to save subscriptions? and therefore flooding the database. What is the mechanism of control here. – astralmaster Feb 26 '20 at 15:38
  • What part of the PushSubscrption do you hash? The whole object or just the endpoint URL? Also, where have you read that the subscription does not change over time? Is it part of the spec – Mikey Jul 06 '20 at 22:24
4

Storing your Subscription in the database

Your database should look like if you are using a document db:

userId: {type:String, required: true},
endpoint: { type: String, unique: true},
keys: {
 p256dh: {type: String},
 auth: {type: String}
},
userAgent: {type: String}, // optional, just an additional tracking field
deviceId: {type: String} // just additional tracking fields

If you are using a relational database you can store the keys part in individual columns or use a JSON type column.

The main thing to know is that webpush.sendNotification(...) takes an object with the following interface:

export interface PushSubscription {
    endpoint: string;
    keys: {
        p256dh: string;
        auth: string;
    };
}

which means basically these are the properties your database will need to keep track of subscriptions. You may choose to add userId, and others for your own specific use case

0

You can store web-push subscription as a string of JSON array with 3 elements: [endpoint, p256dh, auth].

Your table might look like this:

CREATE TABLE push_subscriptions(
  id BIGINT PRIMARY KEY AUTOINCREMENT,
  user_id BIGINT NOT NULL,
  user_agent VARCHAR(100),
  technology VARCHAR(10),
  subscription VARCHAR(255) UNIQUE,
  created_at TIMESTAMPZ
);

Where technology migh be: webpush, firebase, apn

Aqua
  • 716
  • 8
  • 10