@@ -125,7 +125,10 @@ class Account:
125125```
126126
127127The client is responsible for ensuring that these secrets are stored
128- as securely as possible, including excluding them from device backups.
128+ as securely as possible, including excluding the entire Account table
129+ from device backups. In addition to the security motivation for doing
130+ so, the server relies on there being no duplicate `(user_id,
131+ push_account_id)` pairs.
129132
130133After a backup/restore process, clients should just register a new
131134token and key.
@@ -382,7 +385,7 @@ Some steps may happen repeatedly due to retries, as detailed below.
382385
383386 Initiate a timeout to check the ` push_account_ids `
384387 map provided in the [ register-queue] ( https://zulip.com/api/register-queue )
385- response (see the [ Miscellaneous ] ( #miscellaneous ) section for details), to
388+ response (see the [ In register-queue ] ( in-register-queue ) section for details), to
386389 verify whether the asynchronous step has succeeded. If the status remains
387390 ` "pending" ` , the client displays a notice to inform the user that push
388391 notifications are not yet set up.
@@ -395,7 +398,7 @@ Some steps may happen repeatedly due to retries, as detailed below.
395398
396399 1 . Use ` bouncer_public_key ` to look up the corresponding
397400 ` private_key ` in ` push_registration_encryption_keys ` map. If
398- not found, fail with an ` invalid_bouncer_public_key ` error code.
401+ not found, fail with an ` INVALID_BOUNCER_PUBLIC_KEY ` error code.
399402
400403 1 . Decrypt the given ` encrypted_push_registration ` with the
401404 resulting ` private_key ` , to produce ` push_registration ` .
@@ -438,44 +441,34 @@ Some steps may happen repeatedly due to retries, as detailed below.
438441 The client is now successfully registered and ready to receive
439442 notifications.
440443
441- * On ` invalid_bouncer_public_key ` error:
444+ * On ` INVALID_BOUNCER_PUBLIC_KEY ` error:
442445
443446 Delete the ` PushDevice ` record.
444447
445- Include an additional ` reason ` field with the value
446- ` "invalid_bouncer_public_key" ` in the event sent to remove the
447- ` push_account_id ` from the ` push_account_ids ` map (see the
448- [ Miscellaneous ] ( #miscellaneous ) section). The client uses the
449- ` reason ` field to inform the user that they need to update the
450- app to continue using push notifications.
448+ Use ` "INVALID_BOUNCER_PUBLIC_KEY" ` as the ` error_code ` in the
449+ event sent to remove the ` push_account_id ` from the
450+ ` push_account_ids ` map (see the [ In
451+ register-queue ] ( in-register-queue ) section). The client uses this
452+ ` error_code ` to inform the user that they need to update the app
453+ to continue using push notifications.
451454
452455 * On error due to a stale request:
453456
454457 Delete the ` PushDevice ` record.
455458
456- This error often indicates a long-lasting outage somewhere
457- between the server and the bouncer, most likely in either the
458- server or its local network configuration.
459+ This error indicates a long-lasting outage somewhere between the
460+ server and the bouncer, most likely in either the server or its
461+ local network configuration.
459462
460463 Report the error to the server admins in the same way as other
461464 errors indicating an operational issue, for example in the server
462465 log and by email.
463466
464- Additionally, an additional ` reason ` field with the value
465- ` "request_expired" ` in the event sent to remove the
466- ` push_account_id ` from the ` push_account_ids ` map (see the
467- [ Miscellaneous] ( #miscellaneous ) section). The client may use the
468- ` reason ` field to know it needs to try again, but this is not
469- required, since the .
470-
471- - If the request gets replayed, this could result in a working
472- ` PushDevice ` being deleted. This failure mode is plausible in
473- case of restoring a backup taken while the request was in
474- flight. It is unclear whether to expect the restored backup can
475- successfully deliver mobile notifications.
476-
477- TBD: Consider whether to check whether we already have a valid
478- registration before processing the deletion here.
467+ User ` "REQUEST_EXPIRED" ` as the ` error_code ` in the event sent to
468+ remove the ` push_account_id ` from the ` push_account_ids ` map (see
469+ the [ In register-queue] ( in-register-queue ) section). The client
470+ uses this ` error_code ` to know it should attempt registration
471+ again.
479472
480473 * On other errors, including 5xx errors and network errors:
481474
@@ -486,33 +479,62 @@ Some steps may happen repeatedly due to retries, as detailed below.
486479 Since the server does not have ` timestamp ` , it cannot check if
487480 the request is already past the
488481 ` PUSH_REGISTRATION_LIVENESS_TIMEOUT ` threshold and therefore
489- stale. It is thus reliant on the server to reject such requests.
482+ stale. It is thus reliant on the bouncer to reject such requests.
490483
491484 (Forgetting the ` PushDevice ` record will cause the client to retry
492485 the operation from scratch, if the client is still in use.
493486 That way if the client keeps being used, we keep retrying and keep
494487 sending the server admin error messages about it — appropriately so
495488 — but we don’t accumulate ghost clients.)
496489
490+ - If a request gets replayed at least
491+ ` PUSH_REGISTRATION_LIVENESS_TIMEOUT ` after the original request
492+ succeeds, this could in theory result in a working ` PushDevice `
493+ being deleted by the ` "REQUEST_EXPIRED" ` handler. It's the
494+ responsibility of the server implementation and network layer to
495+ avoid faults that could have this failure mode.
496+
497+ There is a known plausible scenario for a replayed request: An
498+ unlikely race starting with a server backup taken for the purpose
499+ of testing a Zulip server upgrade while the request was in a
500+ queue:
501+
502+ 1 . The original server successfully completes the registration
503+ and continues running normally.
504+
505+ 1 . The backup is restored on a duplicate server at least
506+ ` PUSH_REGISTRATION_LIVENESS_TIMEOUT ` after being taken, and
507+ notably, the original request is restored as part of this
508+ process.
509+
510+ 1 . The duplicate server would send the request from its queue,
511+ getting this error and deleting its ` PushDevice ` record due
512+ to the stale request error.
513+
514+ 1 . However, the bouncer and original server continue running
515+ normally.
516+
517+ Thus, that unlikely race would not lead to a fault.
497518
498519### In register-queue
499520
500521In the data which the server sends the client in the
501522[ register-queue] ( https://zulip.com/api/register-queue ) response,
502523and keeps updated via events, we add the following:
503524
504- * ` push_account_ids ` , a JSON object,
505- with one entry for each of the user's ` PushDevice ` records.
525+ * ` push_account_ids ` , a JSON object, with one entry for each of the
526+ user's ` PushDevice ` records.
506527
507528 * The key is ` PushDevice.push_account_id ` .
508529
509- * The value is either ` "active" ` or ` "pending" ` ,
510- representing the status of the device's registration.
511-
512- The status is pending if ` PushDevice.bouncer_device_id ` is null,
513- and active otherwise.
530+ * The value is an object with the following fields:
531+ - ` status ` : ` "active" ` , ` "pending" ` , or ` "failed" ` , representing
532+ the status of the device's registration.
514533
515- TODO This value is now an object.
534+ The status is pending if ` PushDevice.bouncer_device_id ` is null,
535+ and active otherwise.
536+ - ` error_code ` : If the status if ` "failed" ` , the error code
537+ indicating the cause of the failure.
516538
517539The client should look up in this map the ` push_account_id ` value from
518540the corresponding ` Account ` record. It uses the result in two ways:
@@ -537,10 +559,6 @@ the corresponding `Account` record. It uses the result in two ways:
537559 surprised that push notifications aren't working for them. The
538560 client might show a warning banner to the user in this case.
539561
540- * These data structures should be appropriately linked to future
541- data structures allowing lists of clients and deleting an
542- ` api_key ` .
543-
544562### Expired tokens
545563
546564- If APNs or FCM indicates to the push bouncer that the token is invalid
@@ -573,6 +591,13 @@ should:
573591 (Optional because we expect APNs/FCM to report the token to the
574592 bouncer as invalid in this case.).
575593
594+ ### Periodic repeat registrations
595+
596+ The client should also repeat the registration procedure monthly as
597+ detailed in
598+ [ #F322] ( https://github.com/zulip/zulip-flutter/issues/322 ) , to support
599+ garbage-collection for clients that no longer exist.
600+
576601## Background info
577602
578603### What is a “push token”?
0 commit comments