If you have added a push notification feature on your PWA. You might have stumbled upon one problem.

Identifying subscription records

How do I identify the subscription so that I can later notify it?

The easiest way is to add a user’s ID field to it and save it in your database.

For example:

Max is your user and you have the subscription object {…}

you will save it as

yourDb.pushSubscriptionCollection.save({
  user: 'Max',
  subscription: {},
});

Mind that the subscription object will update, so you will have to overwrite the record frequently.

Now an issue arises when your user uses multiple devices.

Identifying Multiple devices of a single user

Now you need to know which device your user is using so that you want to add individual notification subscription records to your database.

This can be done by adding a field called device.

The identification of a device is tricky, but one of the easiest ways is to use User-agent, to keep users’ data safer you could hash it using some algorithm, which you can decide yourself. But for now, let’s say the user agent is hashed and safe to keep in your database. Just to keep it simple, I will use a plain user agent in this example.

So now your record has 3 fields, and you will use user + device as a key to identify your record.

yourDb.pushSubscriptionCollection.save({
  user: 'Max',
  device: 'UA-string',
  subscription: {...},
});

This works nice!! …..until! …your user’s browser gets updated.

User-Agents change!

Commonly, these browsers get very frequent updates, now that let us suppose Max had BogusBrowser which got updated recently after that push subscription record was added.

When Max tries to access your app, the request will be made with his new User-agent. And your database will have a new record for the same device. It will look something like.

// yourDb.pushSubscriptionCollection
[
  {
    user: 'Max',
    device: 'Mozilla/* (Linux; Android *; XXX*A) AppleWebKit/* (KHTML, like Gecko) BogusBrowser/50.0.0',
    subscription: {...},
  },
  {
    user: 'Max',
    device: 'Mozilla/* (Linux; Android *; XXX*A) AppleWebKit/* (KHTML, like Gecko) BogusBrowser/50.0.1',
    subscription: {...},
  },
]

This is a problem.

Now, whenever you want to notify Max with a push notification, you will get two subscriptions. Most probably both of them are still valid and you don’t know which one to choose. You end up sending a notification to both. Again keep in mind that, it could have been different devices that Max uses. So now Max is bombarded by duplicate notifications on the same device.

Solution

I avoided this issue by just replacing text blocks, in the user-agent with numbers and dots for e.g. 10.2.5. with some symbol like an asterisk *.

const userAgent =
  'Mozilla/5.0 (Linux; Android 10; VENDOR1A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.10';

userAgent.replace(/[\d|.]+/g, '*');

// Mozilla/* (Linux; Android *; VENDOR*A) AppleWebKit/* (KHTML, like Gecko) Chrome/*

Now the returning string has all the relevant information minus the version numbers that we don’t care about. This information should be enough to fairly tell between devices unless the user uses multiple of the same device model.

You can then go on and hash this string and save it to your database.