Managing PushSubscriptions within shared browsers - service-worker

Working on ironing out some UX considerations around a push notification system using WebPush and ServiceWorkers. Additionally, this system should gracefully handle multiple users sharing the same browser.
More concretely, I have the following constraints:
1) A user should not be able to see notifications which are not intended for them.
2) The state of a user's push subscription should be opaque to and immutable by other users. i.e. the state of my push subscription should not be based on the behavior/actions of anybody but my own.
Satisfying the first constraint was quite straightforward. I decided to store the currently logged-in user id in IndexedDB and include the id of the intended user within the push payload. It is then straightforward to only show the push notification to the user if those 2 ids match.
However, satisfying constraint 2 has proved to be quite elusive. So far I've tried:
1) One-to-one mapping between users and push subscriptions. This was the first thing I tried, as it struck me as the most natural mapping. However, this ends up falling flat on its face as creating a new push subscription will invalidate the subscription for the previous user, thus requiring the original user to create another subscription on subsequent logins, thus failing the second constraint.
2) Shared push subscription. This has the benefit of not invalidating the push subscriptions, so subsequent logins for the original user will behave as expected. However, none of the subsequent users will actually need to grant permission to the browser, thus failing the second constraint.
3) Even if I was able to get something working with one of the above 2 options, there is still nothing preventing another user from simply going to the browser settings and disallowing notifications, thus nuking all push subscriptions. However, I imagine that this is just something I will have to live with that will have no elegant solution.
I'm certain there have been many brilliant minds working on the above problem, so I'm all ears with regards to how constraint 2 above has effectively been satisfied.
Thanks.

I had the same problem when I was developing Pushpad. We tried different solutions and some of them, such as the many to many relation between users and browsers, became a nightmare very soon. So I would recommend the following approach, which proved to be the best one for us.
Each subscription (endpoint) is a device (browser) and can be associated at most to one user at a time. Whenever possible try to keep data associated to users and not to devices. In this way a subscription (endpoint) can be transferred to a different user and you don't loose data when the endpoint expires or gets replaced. Then when you need to send a notification filter your audience based on the user data, find the recipients and send the notification to the associated devices.
The only data that you may want to associate to devices are device preferences and device preferences are global to all the users that use that browser. This is consistent with the fact that the browser permission (allow / block) is global to all users.
The above solution partially meets your requirements:
1) A user should not be able to see notifications which are not intended for them.
Yes:
when a user logs out you can remove the association between the user and the device
when a browser (user) logs in with a different account on your website you associate the device to the new user (and you remove the previous association)
2) The state of a user's push subscription should be opaque to and immutable by other users.
Yes, because you don't keep data associated to the device. You associate data to the users in your database.

There is no way to satisfy both constraints. You will need to choose either the one-to-one mapping (1) or the shared push subscription (2). You cannot use the browser to deliver your push notifications and at the same time expect certain behaviour (w.r.t. permissions, nuking) from that very same browser...

Related

How to identify the customer on a server-sent event for Apple subscriptions?

Implementing subscriptions on an iOS app means we want to handle SSEs in case of subscription status change, as to manage the user's rights inside the service.
The Apple documentation doesn't show an explicit customer GUID.
They provide, in each SSE's payload, receipts, e.g. the latest one.
The question is: how can an API server uniquely identify customers?
Should the API server keep track of every receipt, in an inheritance way?
I.e. should the API persist every receipt, and a link to the previous receipt, up to the first
one that was emitted (which can be linked to a customer through the iOS app the user used to initiate its subscription), and re-trace this history on every SSE?
Per this thread, the applicationUsername service-side UUID (used by apple, for "irregular activity checking") isn't a solution, as it isn't provided in receipts or SSEs. The thread hints that using the transaction IDs is still the best way to identify a SSE, as it is ultimately tied to a user, and it is up to the service to keep track of transactions made by the user on the Apple platform for the given service.
Isn't there an easier, more direct, way, to establish the relationship between a SSE, its apple customer, and the service's user?
After digging for a while and asking around, I reached the following conclusion:
There is only one way to tie a transaction event to a user, and that is by the
restoration mechanism one has to implement on their application.
For this reason, you need to at least keep track of original transaction IDs,
so when the restoration mechanism is triggered on your app, you can check the
currently logged user on your app, and call your back-end with the logged user,
and a list of original transaction IDs.
Usually, when a user subscribes, it is mainly done from within your app, so you
can do this link in a trivial way.
But, because you can "re-subscribe" from the AppStore after a while, without
going through your app, Apple may send a SSE to your back-end which would contain
a new (hence unknown) original transaction ID, since this new subscription is
has a new transaction.
In short,
You need to keep at least a DB of original transaction IDs you receive as SSEs
You need to implement the restoration mechanism as to contact your server every
time the app starts, with the list of transaction IDs / original transaction IDs Apple gives you
You need to accept having transactions stored in your backend without a link
to a user (because the user may have re-subscribed without launching the app yet)

Firebase notification sent to an audience does not reach all devices it should

I have an interesting situation. For some time I have been collecting an audience based on a single user property. Now it has significant amount of devices included. The audience is for ios devices.
I then sent a notification to that audience and I that see some of the devices receive that notification, some don't. Why could that be?
Experiments show that devices that don't receive the notification, receive a notification targeted to single device only. Also they receive notifications send to all users. That makes me suspect that this is related to audiences. I have verified that at least one device that does not receive the notifications has proper value for the user property in question. However, it is possible that the during the past days device has had varying values for the property, based on the account used for logging in to the app. Sometimes it has had two versions of the same app installed at the same time.
I have similar audience for Android and as far as I can tell, all members in the audience receive the notification.
The audience's are in thousands, so the information is based only on few devices I see around me, plus Firebase and Google Analytics.
From Firebase documentation I learn that "a user is a permanent member of an audience after they are assigned to it", which explains why two opposite audiences combined may be bigger than the total number of users, but it makes it even more difficult to understand why someone having the properties required for the audience would not belong to it.
Any ideas on how to debug this or what could be the reason. Or even better, how to fix this? Since this is production software, I can not send excessive number of notifications to try different things.
While I probably still don't know all of the possible reasons for this, there are at least two things that seemed to have some effect on this in our case and third one that could have been the reason if not correctly understood:
1) Our app did register list of values with single call to setUserPropertyString and in some cases the string did exceed the 24 character limit, which caused the property not to be set and the user/device not to be included in an audience.
2) When sending the notification to iOS app, the "High" prority should be selected, otherwise it is possible, or even likely that the notification does not reach the device.
3) Firebase console texts for combining multiple audiences are not all that clear, especially the "does not ..." texts could be clearer in my opinion. This is what they mean in practice:
includes all of == AND, notification is sent if user is included in all selected audiences
includes at least one of == OR, notification is sent if user is included in at least one of the selected audiences
does not include all of == NAND, notification is sent if user is not included in all of the selected audiences, but user can be included in some of them
does not include at least one of == NOR, notification is sent only if user is not included in any of the selected audiences

How to use Non-Renewable Subscriptions in multiple devices?

I'm implementing an application with Non-Renewal Subscriptions. For that i have referred many sites and most of the answers that i found as like,
We need to manage the expire date by ourself in our server side/local, once you verify a receipt with Apple.
We can implement authentication module as optional to share use the Non-Renewal Subscriptions across multiple devices.
Actually my application doesn't have authentication modules like registration/login to track user on multiple devices. So How can i able to use the Non-Renewable in multiple devices without allowing user to register/login. Do we have any common field to trace the user on multiple devices without authenticating them.
Thanks in advance, Can anyone please give any suggestions to handle this scenario.
You will need some server side support. If you are fine with receiving unauthenticated traffic, you can use the original transaction id. For each subscription purchase you make for a given Apple Id, that purchase will have a unique original transaction id (in the receipt).
If the user initially purchases the subscription on device A, let's say they get back a transaction id of 1234 (note this is not the format, I am just using this as an example). Now the user goes to device B and makes the same purchase. Note this is a subscription, so Apple will already know the purchase was made for that Apple Id. It will indicate the subscription was already purchased, but will let the person still "purchase" (they won't be charged). Upon this, they will get a receipt. This receipt will have the same transaction id.
So here is where your server would need to tie the two together. You would most likely use a UUID, such as [[UIDevice currentDevice] identifierForVendor]. Now your server will know that transaction id 1234 is used by device A and device B.
You can choose how to send this information. You can either send it by way of the receipt (in which case your server will need to unpack it to get the transaction id) or the app can do it for you.
You should do what you can to increase security measures. For example, the method above could be easily spoofed.

How to implement user suspend feature in iOS

In an iOS application, When I detect a users improper action (for example posting violent content), I wan't to suspend the user from using my application. The basic idea to implement this feature is to create and save an unique id for each application installs and suspend the usage from server api's.
My question is, how can I implement this feature even if the user re-installs the application, and still pass the Apple's iTunes submission?
I came up with two ways to technically implement this feature, but wondering how Apple would respond.
Store the IDFA (I understand that users can reset the id on their behalf)
Store an app generated udid to the Keychain (which should not be deleted even if the user deletes the app)
I know there are no perfect answers, but would appreciate to discuss this issue with anyone that have tried submitting a similar application, or anyone that is well aware of the Apple's guidelines. Thank you.
Apple will reject apps that inappropriately use the IDFA.
If your app does not use server login (at which point, whatever flags you require could be delivered to the client), keychain storage would be the only real solution.
However, if you don't use server login, you block the device, not the user. Is this your intent?
BTW, without server login, a determined user can still get around keychain storage: Reset keychain on the device
You can block a given account. Most people these days key an account with an email address. Some require a credit card (Facebook fully validates accounts using credit card numbers), others require a bank account (PayPal has to send money somewhere!) and it is growing in popularity to request a phone number (Twitter is getting there). In the end, to really be effective, you have to block something that is difficult to produce.
With email, your users can always create a new account. Check out mailinator.com. Alternatively, all you need is one domain to have as many email addresses as you want -- I use five different email accounts daily, and I use about two dozen more on a monthly basis.
Installation ids are ok but users can always just uninstall/reinstall. And if you do manage to get a device-identifying number (easy to do really, even in the post-UDID era) so that you can block a given device, your users can just get a new device, or hack your app to use some random value, or spoof your API with cURL. I own three iPhones, two iPads, two Samsung tabs, three other Android phones, two Mac Book Pros, a mini, two PCs, and I run three virtual Linux boxes, and one virtual XP box. And what happens when somebody sells a blocked device to a non-abusive user?
So just block the user's account, keep excellent log files, and keep fighting the good fight.

In App Purchase - Authenticating Old Test User

So I thought I had in-app purchase working, but my app sometimes triggers a popup user authentication action sheet with an old test user autopopulated in the action sheet. I've been creating test users with a sequential number, just because that's easy, and I can keep track of how old the users are. My current test user is #4. New test users were generally created because the old users were invalidated somehow.
Anyway, I sometimes get the popup screen for testuser #1 still (as well as #3). I figure that's because the transaction being looked at was originally dealt with using that user. But shouldn't these popups stop after the transaction is finalized using finishTransaction:?
What should I be checking? I assume I must have left something in limbo somewhere even though the products with downloads have been downloaded and all the products that were bought were finished (albeit with a different user since the original one was invalidated).
I guess what must be happening is that unfinished transactions remain on the Apple server. I can't finish the transactions because the test users used were invalidated by accidental use in production. I will try deleting those users on the theory that deleting the users will also delete the pending transactions.

Resources