Skip to content

Notification Service and Push

Tarik Eshaq edited this page Jul 2, 2023 · 3 revisions

The Notification Service extension is an app extension that allows Firefox iOS to intercept encrypted payloads from Autopush, decrypting them, and possibly modifying them before displaying them to the user.

This document will cover how the Notification Service is set up, and its interaction with both the Autopush and Firefox Accounts systems to deliver send tabs.

The document will also cover how it's possible to extend the notification service and push to support other, non-Firefox Accounts related push notifications.

Requirements

To understand why the Notification Service and push work the way they do today, it's important to understand some of the constraints around how Firefox Accounts delivers push notifications.

Firefox Accounts leverages the Autopush infrastructure to deliver push notifications. Because it uses Autopush, Firefox Accounts delivers encrypted payloads to the user, that need to be decrypted before being displayed.

How a Push Notification is delivered to the User

The above requirement thus requires that the application:

  1. Register with Autopush, and receive a unique URL to pass to Firefox Accounts so Firefox Accounts can send push notifications to the applications.
  2. Generate a public/private encryption key pair to encrypt the communication from Firefox Account to the application.
  3. Deliver the public encryption key to Firefox Accounts.
  4. Persist the private encryption key.
  5. When Firefox Accounts sends a push notification to Autopush, it will encrypt the message using the public key it received from the application.
  6. Autopush will send the encrypted push notification to Apple's notification service, which will deliver the encrypted notification to the app.
  7. The application would then intercept the push notification as delivered by APNS, decrypt it using a private key that was persisted earlier
  8. The application would then display the notification

At the bottom of this document is a sequence diagram visualizing the above interactions.

Step 7 above is what is done by the Notification Service because Apple requires that if the application would like to modify the content of a push notification before displaying it to the user, the application must use a Notification Service app extension.

There is some additional complexity in Step 7 this document will cover next, mainly that Send Tabs aren't delivered directly through the push notification, instead, the push notification carries the send tab's index - so the notification service must query Firefox Accounts to get the actual tab's URL and title.

How a Send Tab is delivered to the User, then opened by the application

Send tabs are a form of Device Commands. At the time of this document, they are the only device command used by Firefox.

Device commands don't carry their payload, instead, they carry an index to where the application can request their payload. For example, for send tab, the push notification (after it's decrypted by push) can look like the following:

{
  version: 1,
  command: "fxaccounts:command_received",
  data: {
    "command": "https://identity.mozilla.com/cmd/open-uri",
    "sender": "0a4abb5e6f2e378f3aadda7f97482e99",
    "url": "https://api.accounts.firefox.com/v1/device/commands?index=42&limit=1"
  }
}

The application must then query the url to receive the content of the actual tab (there is a second layer of encryption there, but that's out of scope for this document).

Due to how device commands work, the Notification Service will need to send a network request to Firefox Accounts to retrieve the content of the tab.

Finally, after the notification service has received the tab it needs to do the following:

  1. Display the tab's URL to the user as a push notification
  2. Ensure that once the user opens the app, the URL will be opened

Displaying the tab as a push notification to the user is passing the tab's information to a handler provided by a native iOS API. Ensuring that the app opens the URL is slightly more complicated, as it requires the notification service to pass data to the main process.

How the Notification Service ensures the App opens a send tab

In order to retrieve device command data (i.e send tab), the application must:

  1. Hit the commands endpoint on the server
  2. Provide an index to which command(s) to retrieve
  3. Provide a limit to the number of command(s) to retrieve.

The application persists the index of the command it retrieved lasts and regularly hits the Firefox Accounts endpoint to retrieve any missed tabs. The application will trigger this request if either:

  • It has been 24 hours since the last request, or
  • The notification service has explicitly indicated that there is a device command to retrieve

Additionally, the application will attempt to trigger the request:

  • On every startup
  • Every time the app is put in the foreground
  • When a notification is received while it is in the foreground
  • Every sync

It's important to note that the network request will only truly be triggered if one of the two conditions listed above is true (i.e the 24-hour timeout, or an explicit notification)

Additionally, this request duplicates the request the notification service already made, when the notification service retrieved the tab's information to display to the user.

This is okay, because the request the notification service made the first time, is purely to display the push notification, and not to open the tab, and additionally, the second request the app makes:

  • Retrieves all command data that hasn't been applied, (i.e a tab opened)
  • Will also include any missed commands (for example, if our push system failed to deliver a previous push notification).
  • Will update the persisted index of the last tab retrieved.

The above can be visualized in a diagram below

Diagrams

How a push notification is delivered to the User

Key generation

sequenceDiagram
participant F as Firefox iOS
participant AP as Autopush
participant FA as Firefox Accounts

F->>AP: Subscribe
activate F
activate AP
AP->>F: Push URL, Channel ID
deactivate AP
F->>F: Generate Private/public key pair
F->>F: Persist Private key
F->>FA: Public key
deactivate F


Loading

Send Tab Push Notification

sequenceDiagram
participant FA as Firefox Accounts
participant AP as Autopush
participant APNS
participant F as Firefox iOS

activate FA
FA->>FA: Encrypt notification w/push public key
FA->>AP: Send encrypted push notification
deactivate FA
activate AP
AP->>APNS: Forward push notification
deactivate AP
activate APNS
APNS->>F: Deliver push notification
deactivate APNS
activate F
F->>F: Decrypt push notification using private key
F->>F: Extract tab index from push notification
F->>FA: Get tab using index
activate FA
FA->>F: Tab URL + title
deactivate FA
F->>F: Display Notification
deactivate F
Loading

How Notification Service ensures the app opens the tab

The following happens after the notification is displayed

sequenceDiagram
participant N as Notification Service
participant F as main process
participant FA as Firefox Accounts
activate N
N->>N: Display notification
N->>F: Set pref of timestamp last poll to 0
deactivate N
F->>F: Wake up
activate F
F->>F: Check pref for last poll timestamp
F->>FA: Poll for commands since last retrieved index
activate FA
FA->>F: Tab URL and title
deactivate FA
F->>F: Update index of last retrieved command
F->>F: Open Tab
deactivate F
Loading

Links

Clone this wiki locally