Subject: Re: [Jmap] PushSubscription lifetimes

From: Neil Jhaveri <[email-masked]>
Date: Thu, 5 Jul 2018 00:05:42 -0700

> On Jun 26, 2018, at 7:03 PM, Neil Jenkins <[email-masked]> wrote:
> 
> Neil Jhaveri raised a good point about Push subscriptions: at the moment you can only have a single subscription per "session". The concept of a session however is no longer defined since we took auth out of the spec, so I think we need to change this.
> 
> There is an interesting question here of should one client be allowed to see the Push Subscription objects created by another client? I would say ideally "no", both for user privacy and to stop one client interfering with another (you install app B and it deletes all push subscriptions so you no longer get pushes on app A; that would be weird). This used to be enforced by the sessions (each client created its own session when it logged in, and could only get/set a single push subscription which was tied to the session). With this no longer the case, I suggest we change this to:
> * PushSubscription becomes a standard type allowing multiple instances, but with no /query and fetching only allowed by specific id (can't request all with `ids: null`).
> * Servers MUST ensure ids allocated are random with sufficient entropy to make them hard to guess. Clients MUST keep track of the id returned when they create a push subscription in order to be able to update or destroy it.

Per the other discussion in this thread, I support the suggestion that the client be responsible for generating a unique token, and documenting in the spec that it should be easy to re-generate, not depend on persisted state, be different from device to device, and be different from other vendors. 

Neil Jenkins' sha256(deviceID, softwareID) suggestion seems like a reasonable suggested algorithm if the platform doesn’t already provide something. For instance, on iOS, there is already API to get a globally unique (device, app) token that needs to be passed back to Apple push servers, so using this token would be a natural fit. It’s generated by the system, and not part of an app’s local storage, so it automatically handles cases like a new device being restored with content from a backup.

Would this token then be an argument to PushSubscription/get?

> * When the credentials set by the client have their own expiry (i.e. it is a session with a timeout), the server SHOULD NOT set an explicit expiry for the push subscription, but MUST expire it when the session expires.
> * When the credentials are not time bounded (e.g. basic auth), the server SHOULD set an expiry time for the push subscription. This MUST be at least 24h in the future.

> * Clients can do a standard JMAP update to the expiry time to extend the lifetime of a push subscription.

When using an expiring credential, I think it would be good to be able to extend the lifetime of the push subscription beyond the lifetime of the credential. Is that allowed by your proposal? The scenario I’m thinking of is a low-traffic account not receiving any new content, a user not might launch their client, so for instance an iOS developer would have to use the background app refresh mechanism to extend the push subscription, and I’d start getting nervous with any expectation of getting called in less than 24 hours.

In addition, is there a maximum limit on the expiration time? I could easily see an iOS developer wanting a week, or even up to 30 days, to behave better in cases where the user has disabled background app refresh.

> On Wed, Jun 27, 2018, at 12:28, Neil Jenkins wrote:

> A device id would work (something random but persistent from the client). I guess the question is are the following properties sensitive enough that they should not be returned (again to stop other clients finding their values):
> * *url* (This contains some kind of secret token to allow you to route a push message to the device that created the subscription).
> * *keys* (The public key + auth secret for encrypting the push; given it's a public key this is probably less of an issue?).
> This would be doable (you always just return the empty string for the url if you /get a PushSubscription).

Seems fine to not return these on /get.

---

From: Neil Jhaveri <[email-masked]>
Date: Thu, 5 Jul 2018 10:44:03 -0700

Also, with PushSubscriptions, it might be good to think about how they could be used with FCM and APNS. (Note, I know nothing about FCM and I have only done light reading about APNS — never actually built anything using it.)

Ideally, as an iOS client author, a JMAP server could push directly to APNS without me needing to deploy any kind of relay service.

Last year, I asked engineers at Apple working on APNS if they thought they could support RFC 8030 semantics, and they said maybe they could expose the 8030-style push endpoints (as opposed to hour-life-token-based auth or certificate-based auth funneling to a single URI). But, I think even if they did that, there would still likely be a need for custom payload content <https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_updates_to_your_app_silently>, and potentially custom headers <https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns> on every push.

For instance, the payload may need to contain:
{ “aps” : { “content-available” : 1 }, “changes” : […] }
Or use the content-modifiable key, since the actual payload will not contain the full notification content, and the client will have to be launched/foregrounded on iOS to do some syncing work and generate a local notification, or modify the existing remote notification. 

It would be a bit of a shot in the dark, but do we want to support the ability to add custom payload dictionary content, and custom headers?

Does anybody here have experience with FCM, or actual experience building something end-to-end with APNS?

Or do we envision JMAP servers implementing vendor-specific extensions to support these push servers?

> Some kind of rate limiting needs to be in place here now it's no longer a single push subscription per session. Again, the trouble is preventing different clients from interfering with each other. If you just expire the oldest subscriptions when there are >X, you can get a single client creating a flood and affecting other clients. Not sure this is avoidable though; I guess just note in the spec that when using session-based authentication, the limit should be per-session to isolate clients behaviour from each other.
> 
> Any thoughts, queries, feedback on all this?
> 
> Neil._______________________________________________
> Jmap mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/jmap

---

From: Neil Jenkins <[email-masked]>
Date: Thu, 05 Jul 2018 23:09:18 -0400

> Would this token then be an argument to PushSubscription/get?

I've made it just a property on the PushSubscription object. You can fetch the currently active subscriptions and then filter out those that don't have the appropriate id.

> When using an expiring credential, I think it would be good to be able to extend the lifetime of the push subscription beyond the lifetime of the credential. Is that allowed by your proposal?

Yes, this is just a standard "update" to the expires property of the subscription object.

> In addition, is there a maximum limit on the expiration time? I could easily see an iOS developer wanting a week, or even up to 30 days, to behave better in cases where the user has disabled backgr…

I've made this entirely server dependent, but said it MUST be at least 48h and SHOULD be at least 7 days.

Pull request with proposed changes here <https://github.com/jmapio/jmap/pull/227>, review welcome! In particular, I've specified there are no state strings (and so no /changes method) for PushSubscription objects. This feels right to me, as they are not like standard objects; they may not be in a persistent store, they're not tied to a specific account, and the client should only be interested in subscriptions it creates itself. But if people think otherwise, pipe up!

Cheers,
Neil.

---

From: Neil Jenkins <[email-masked]>
Date: Thu, 05 Jul 2018 23:18:21 -0400

> Also, with PushSubscriptions, it might be good to think about how they could be used with FCM and APNS. (Note, I know nothing about FCM and I have only done light reading about APNS — never actuall…

My understanding is that you are going to need a lightweight proxy server of your own whatever we do, as you need to authenticate the connection to APNs using a private certificate that should not be shared (so shouldn't be built into the app). Given this, it can trivially add the other data/headers while authenticating the request.

I'm not deeply familiar with APNs (and even less so with FCM), so I might be wrong about this.

> Last year, I asked engineers at Apple working on APNS if they thought they could support RFC 8030 semantics, and they said maybe they could expose the 8030-style push endpoints (as opposed to hour-…

Ah, so they might add something in the future that would avoid the authentication problem then? If so, it would be nice to be able to get the JMAP server to push directly without needing a proxy. However, I'm hesitant to add extra complexity to the spec when we don't actually know exactly what will be required yet. I think this is probably better left for a future extension when the requirements are known.

If someone with more experience working with these systems would like to comment, I'd love to hear it.

Neil.

---

From: Neil Jhaveri <[email-masked]>
Date: Sun, 8 Jul 2018 21:52:01 -0700

> On Jul 5, 2018, at 8:09 PM, Neil Jenkins <[email-masked]> wrote:
> 

Ok, that seems reasonable to me too.

> Yes, this is just a standard "update" to the expires property of the subscription object.

Ok, great.

> I've made this entirely server dependent, but said it MUST be at least 48h and SHOULD be at least 7 days.

Great, that seems reasonable to me. 48h is a good lower bound, and I think it should work fine for most iOS developers (assuming we got to a point where they could do this without a push proxy server).

> Pull request with proposed changes here <https://github.com/jmapio/jmap/pull/227>, review welcome! In particular, I've specified there are no state strings (and so no /changes method) for PushSubsc…

I agree with leaving out /changes.

---

From: Neil Jhaveri <[email-masked]>
Date: Sun, 8 Jul 2018 22:01:02 -0700

> On Jul 5, 2018, at 8:18 PM, Neil Jenkins <[email-masked]> wrote:
> 

Yeah, I think deferring that probably makes the most sense. 

But I still think a JMAP server being able to push directly to APNS / FCM without a proxy server, if those push services supported the relevant portions of RFC 8030, would be really, really nice.

> If someone with more experience working with these systems would like to comment, I'd love to hear it.

Let me see if I can get somebody from Apple’s APNS team to reply to this thread and provide their opinions. Unfortunately, I don’t know anybody that works on FCM, or has any experience with it.