Subject: [Jmap] PushSubscription lifetimes

From: Neil Jenkins <[email-masked]>
Date: Tue, 26 Jun 2018 22:03:56 -0400

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.
 * 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.

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.

---

From: Bron Gondwana <[email-masked]>
Date: Wed, 27 Jun 2018 12:13:38 +1000

On Wed, Jun 27, 2018, at 12:03, Neil Jenkins 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.
>  * 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.> 
> 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.> 

If a client loses its local data, this pretty much guarantees there's no
way to clean up that push subscription until it times out, and if that's
a push to a device, it's going to keep waking said device for every push
with no way to fix it :(
Personally I think the clients of a single user should use reasonable
behaviour to not screw with each other, but the data should be exposed
over the protocol such that they can clean up (e.g. putting a device ID
and software manufacturer into each push, so clients can find all the
pushes for this device and this piece of software).
You're already no entirely isolating client behaviour from each other.
Clients can do things like create new folders, move messages around in
clever ways, auto-tag messages... there's an expectation that software
does reasonable things and making push subscriptions hidden in some way
seems wrong to me.  Set behavioural expectations on clients rather than
make it impossible for them to handle lost local storage.
Bron.

--
  Bron Gondwana, CEO, FastMail Pty Ltd
  [email-masked]

---

From: Neil Jenkins <[email-masked]>
Date: Tue, 26 Jun 2018 22:28:16 -0400

On Wed, 27 Jun 2018, at 12:13 PM, Bron Gondwana wrote:
> If a client loses its local data, this pretty much guarantees there's no way to clean up that push subscription until it times out, and if that's a push to a device, it's going to keep waking said device for every push with no way to fix it :(

That's a fair point.

> Personally I think the clients of a single user should use reasonable behaviour to not screw with each other, but the data should be exposed over the protocol such that they can clean up (e.g. putting a device ID and software manufacturer into each push, so clients can find all the pushes for this device and this piece of software).

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).

Neil.

---

From: Bron Gondwana <[email-masked]>
Date: Wed, 27 Jun 2018 12:32:21 +1000

On Wed, Jun 27, 2018, at 12:28, Neil Jenkins wrote:
>> If a client loses its local data, this pretty much guarantees there's
>> no way to clean up that push subscription until it times out, and if
>> that's a push to a device, it's going to keep waking said device for
>> every push with no way to fix it :(> 
> That's a fair point.
> 
>> behaviour to not screw with each other, but the data should be
>> exposed over the protocol such that they can clean up (e.g. putting a
>> device ID and software manufacturer into each push, so clients can
>> find all the pushes for this device and this piece of software).> 
> 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):
You want both a device ID (something that the client can get from the
device in a repeatable way) and a software ID (so that if the user has
two different clients installed on their device they can avoid trampling
each other).
The core problem with the client having to store anything locally is
that said data can be removed in various circumstances (e.g. private
tabs in a browser, e.g. android and "wipe app data").
>  * *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).
Definitely hiding anything password-equivalent on get is sensible.
Mostly the client just needs to know which ones it can clean up/replace
when creating a new push.
Bron.

---

From: Neil Jenkins <[email-masked]>
Date: Tue, 26 Jun 2018 22:37:58 -0400

On Wed, 27 Jun 2018, at 12:32 PM, Bron Gondwana wrote:
> You want both a device ID (something that the client can get from the device in a repeatable way) and a software ID (so that if the user has two different clients installed on their device they can avoid trampling each other).

I was envisioning the client would generate their JMAP Push device id by something like sha256(device id . software id) – it gets you the same uniqueness, but avoids leaking information (at least to clients not on the same device; clients on the same device could enumerate common software ids if they really wanted to).

Neil.