Welcome! If you’re reading this document, it means that you have volunteered (or have been volunteered) to implement a client implementation of the messages feature of Firefox Accounts.
First of all, what is this new feature? Also referred as “Pushbox” (our back-end server implementation), FxA messages is a way to send durable data payloads between devices registered on the same Firefox Account. The reasons we have started this project are stated here (be careful, a lot of API specifications are outdated!).
As for now, FxA messages are only used as the new backbone of "Send Tab". However, some other teams have expressed interest in our platform to send data quickly between devices.
This document assumes that:
- Your app has a “good-enough” client implementation of Firefox Accounts and Device registration (w/ FxA Push subscription) is working.
- You have access to primitives to encrypt/decrypt push payloads in the process that will execute the FxA messages client implementation.
Here's a high-level system view of what happens when a message is sent and received:
First, the sending device must create a durable push payload. In its cleartext version, it is a simple JSON object that looks like this:
{
"topic": "sendtab",
"data": {/*...*/}
}
topic
is a string enumeration that allows the recipient to identify what kind of message it is handling (in case it is retrieving every pending message). So far only thesendtab
topic is supported.data
is the application level data. Technically it could be anything, but by convention it is a JSON Object.
We then encrypt the payload by JSON-Stringifying the payload then using the Push aes128gcm
encryption algorithm. See the Firefox Desktop implementation or Web Push Payload Encryption.
The keys used are the ones of the recipient device. They are obtainable by calling the GET /accounts/devices endpoint (pushPublicKey
/pushAuthKey
).
The resulting encrypted durable payload is a RFC 4648 base64url (no padding) encoded string.
Nothing special here, we use the sessionToken
authenticated POST /account/devices/messages endpoint.
Note that we also specify the topic
in the request body. The FxA server uses that field to filter messages (e.g. Firefox iOS only wants sendtab
).
There's nothing actionable from you on that part. However, you should remember for later that each stored message has an "offset", called index
.
The other device receives a Push notification that looks like this:
{
"topic": "sendtab",
"url": "<URL to retrieve the payload>"
}
url
is the direct URL to retrieve the payload, without any consideration for any eventual other pending message.
You have two choices here:
- Use
url
, just like Firefox iOS does it, in order to guarantee that 1 push message equals 1 payload retrieved. However, it is a bit more work to catch out-of-band messages. If you choose that solution you can skip the next section, but pay special attention to the "Periodic polling" section. - Do not use
url
, like Firefox Desktop, and treat push notifications as tickles: simply retrieve all pending messages.
We use the sessionToken
authenticated GET /account/device/messages endpoint.
An index
searchParam can be set: in that case, the server will return all messages that have been stored after the message who had that index
.
You should persist the newly returned index locally, so you don't end-up fetching and replaying the same messages over and over again (messages are NOT deleted after a fetch!).
Decrypt the base64 payload using your own FxA push subscription encryption keys and trigger the appropriate actions!
You should call the GET /account/device/messages endpoint periodically in your app (e.g. every 20 minutes/every sync) unconditionally to check for missed messages.
If you have decided to use the URL field in the part (D), storing the last index
received is not a viable strategy because you may have missed messages.
Here's an algorithm you should use instead:
-
Every time a message is handled, append its index in a
handled_indexes
persisted variable. -
Call GET /account/device/messages periodically, with the query param
index=last_good_index
(you'll know whatlast_good_index
refers to in a moment). -
Filter the retrieved messages with
handled_indexes
: these are your missed messages, handle them. -
Once all messages are decrypted and handled, empty
handled_indexes
, and setlast_good_index
to the last index you received in 2.
FxA messages is not supported on every client implementation yet! So how do we know if it's safe to send messages to a particular device?
Enter capabilities
: each device must self-report that it has the capability of handling messages when it registers (see Device registration).
When you have completed your messages implementation, you should make sure your client implementation of FxA reports that new capability and more importantly make sure existing device registrations are updated as well!