WebAPI/SimplePush/Protocol: Difference between revisions
No edit summary |
No edit summary |
||
Line 7: | Line 7: | ||
== Purpose == | == Purpose == | ||
The SimplePush protocol is closely based on [http://static.googleusercontent.com/external_content/untrusted_dlcp/research.google.com/en/us/pubs/archive/37474.pdf Thialfi: A Client Notification Service for Internet-Scale Applications] and makes the same delivery guarantees, soft server state and client driven recovery. It is a | The SimplePush protocol is closely based on [http://static.googleusercontent.com/external_content/untrusted_dlcp/research.google.com/en/us/pubs/archive/37474.pdf Thialfi: A Client Notification Service for Internet-Scale Applications] and makes the same delivery guarantees, soft server state and client driven recovery. It is a signaling and not a data carrying system. | ||
The goal: To notify clients of changes to application server state in a reliable manner by ensuring that the client will always eventually learn of the latest version of an object for which it has expressed interest. | |||
== Definitions == | == Definitions == | ||
Line 16: | Line 18: | ||
;UserAgent | ;UserAgent | ||
A device or program that implements the client side of the Push Protocol. | A device or program that implements the client side of the Push Protocol. | ||
;UAID | |||
:A globally unique UserAgent ID. Used by the PushServer to associate channelIDs with a client. Stored by the UserAgent, but opaque to it. | |||
;Channel | ;Channel | ||
Line 23: | Line 28: | ||
:Unique identifier for a Channel. Generated by UserAgent for a particular application. Opaque identifier for both UserAgent and PushServer | :Unique identifier for a Channel. Generated by UserAgent for a particular application. Opaque identifier for both UserAgent and PushServer | ||
; | ;Endpoint | ||
;A REST-ful HTTP URL uniquely associated to a channel. Requests to this URL should update the PushServer state for the channel. | |||
;Version | ;Version | ||
Line 33: | Line 38: | ||
== Protocol Overview == | == Protocol Overview == | ||
The SimplePush protocol defines how UserAgents and PushServers communicate to ensure reliable delivery of the latest version of a channel from the PushServer to the UserAgent. | |||
The SimplePush communication channel is WebSockets, and the wire protocol is JSON, with messages defined below. All messages MUST use TLS (wss:// protocol for WebSockets). In addition, Endpoints generated by PushServer's MUST use HTTPS URLs. | |||
The PushServer should maintain a mapping of channelIDs and their versions and pushEndpoints. | |||
SimplePush is backed by a best effort delivery mechanism over WebSocket | |||
The protocol has some request-response driven actions, where the UserAgent MUST make requests, and the PushServer MAY respond. One message, <tt>notification</tt>, MAY be sent at any time by the PushServer. | |||
=== ChannelID, UAIDs and Endpoints === | |||
Since the UAID is the only unique identifier of a UserAgent, with no authentication information, it is important that UAIDs are not compromised. Only the UserAgent to which the UAID was assigned, and the PushServer, should know the UAID. An attacker can use a leaked UAID to pretend to be the UserAgent with that UAID. Although SimplePush does not transfer any private information, the attacker would still receive the notifications meant for the victim. If any party detects a compromise, they SHOULD reset the UAID. For this, the UserAgent may send a blank UAID and get a new one from the PushServer. Similarly the PushServer may send a new UAID during a handshake. | |||
ChannelIDs are also unique. It is RECOMMENDED that channelIDs be associated with a UAID, so that an attacker cannot receive notifications for a compromised channelID without also having accessed the UAID. | |||
It is RECOMMENDED that both UAID and channelIDs be UUIDv4 tokens. | |||
The format of the Endpoint is not specified by this protocol. There are two requirements that MUST be satisfied: | |||
* PushServers MUST use HTTPS, so the Endpoint URL will begin with "https://". | |||
* ChannelIDs and Endpoints MUST have a one-one mapping and Endpoints may not 'expire' while their channelID is still active. | |||
It is RECOMMENDED that Endpoints simply be a prefix followed by the channelID or some transformation of the channelID which is referentially transparent. PushServers SHOULD avoid exposing UAIDs in the Endpoints since they are sent to AppServers. | |||
== Messages == | == Messages == | ||
All messages are encoded as JSON. All messages MUST have the following fields: | |||
;messageType '''string''' | |||
:Defines the message type | |||
=== Handshake === | === Handshake === | ||
After the WebSocket is established, the UserAgent begins communication by | |||
sending a <tt>hello</tt> message. The hello message contains the UAID if the | |||
UserAgent has one. The UserAgent also transmits the channelIDs it knows so the | |||
server may synchronize its state. | |||
The server is MAY respect this UAID, but it is at liberty to ask the UserAgent | |||
to change its UAID in the response. | |||
If the UserAgent receives a new UAID, it MUST delete all existing channelIDs | |||
and their associated versions. It MAY then wake up all registered applications | |||
immediately or at a later date by sending them a <tt>push-register</tt> | |||
message. | |||
The handshake is considered ''complete'', once the UserAgent has received a reply. | |||
An UserAgent MUST transmit a <tt>hello</tt> message '''only once''' on its | |||
WebSocket. If the handshake is not completed in the first try, it MUST | |||
disconnect the WebSocket and begin a new connection. | |||
'''NOTE:''' Applications may request registrations or unregistrations from the | |||
UserAgent, before or when the handshake is in progress. The UserAgent MAY | |||
buffer these or report errors to the application. But it MUST NOT send these | |||
requests to the PushServer until the handshake is completed. | |||
==== UserAgent -> PushServer ==== | |||
; messageType = "hello" | |||
: Begin handshake | |||
; uaid string '''REQUIRED''' | |||
: If the UserAgent has a previously assigned UAID, it should send it. Otherwise send an empty string. | |||
; channelIDs list of strings '''REQUIRED''' | |||
: If the UserAgent has a list of channelIDs it wants to be notified of, it must pass these, otherwise an empty list. | |||
Extra fields: | |||
The UserAgent MAY pass any extra JSON data to the PushServer. This data may include information required to wake up the UserAgent out-of-band. The PushServer MAY ignore this data. | |||
===== Example ===== | |||
{ | |||
"messageType": "hello", | |||
"uaid": "1234567890", | |||
"channelIDs": ["r2d2", "c3po"] | |||
} | |||
==== PushServer -> UserAgent ==== | |||
PushServers MUST send only respond to a hello once. | |||
UserAgents MUST ignore multiple hello replies. | |||
; messageType = "hello" | |||
: Responses generally have the same messageType as the request | |||
; uaid string '''REQUIRED''' | |||
: If the UserAgent sent no UAID, generate a new one. If the UserAgent send a valid UAID and the PushServer is in sync with the UserAgent, send back the same UAID, otherwise send a new UAID. | |||
===== Example ===== | |||
{ | |||
messageType: “hello”, | |||
uaid: <if not provided by previous call, a new valid one will be generated> | |||
} | |||
=== Register === | === Register === | ||
Line 48: | Line 144: | ||
== Acheiving reliable delivery == | == Acheiving reliable delivery == | ||
At any time, if the PushServer reaches an inconsistent condition, due to database failure, network failure, or any other reason, it MAY drop all state. It MUST then disconnect all active clients, forcing them to reconnect and begin a handshake to get synchronized. | |||
== Alternative communication == | |||
In environments or devices where maintaining an always alive WebSocket is difficult, UserAgents and PushServer's MAY implement alternative means to notify UserAgents about updates. This out of band notification SHOULD be restricted to request the UserAgent to establish the WebSocket. SimplePush state SHOULD NOT be transmitted out of band, since reliable delivery may not be guaranteed. | |||
API Push Client - Push Server | API Push Client - Push Server |
Revision as of 21:42, 7 March 2013
This page describes the protocol used for communication by the PushServer and the UserAgent.
Status: Draft
Everything here applies to Version 1 of the protocol. Major versions may change underlying protocols, message formats and anything else.
Purpose
The SimplePush protocol is closely based on Thialfi: A Client Notification Service for Internet-Scale Applications and makes the same delivery guarantees, soft server state and client driven recovery. It is a signaling and not a data carrying system.
The goal: To notify clients of changes to application server state in a reliable manner by ensuring that the client will always eventually learn of the latest version of an object for which it has expressed interest.
Definitions
- PushServer
A publicly accessible server that implements the server side of the Push Protocol and exposes an HTTP API for AppServer's to notify it.
- UserAgent
A device or program that implements the client side of the Push Protocol.
- UAID
- A globally unique UserAgent ID. Used by the PushServer to associate channelIDs with a client. Stored by the UserAgent, but opaque to it.
- Channel
- The flow of information from AppServer through PushServer to UserAgent.
- ChannelID
- Unique identifier for a Channel. Generated by UserAgent for a particular application. Opaque identifier for both UserAgent and PushServer
- Endpoint
- A REST-ful HTTP URL uniquely associated to a channel. Requests to this URL should update the PushServer state for the channel.
- Version
Monotonically increasing 64-bit integer describing the application state. This holds meaning as a primary key or similar only to the AppServer. The PushServer and UserAgent and App should use this only for detecting changes.
Protocol Overview
The SimplePush protocol defines how UserAgents and PushServers communicate to ensure reliable delivery of the latest version of a channel from the PushServer to the UserAgent.
The SimplePush communication channel is WebSockets, and the wire protocol is JSON, with messages defined below. All messages MUST use TLS (wss:// protocol for WebSockets). In addition, Endpoints generated by PushServer's MUST use HTTPS URLs.
The PushServer should maintain a mapping of channelIDs and their versions and pushEndpoints. SimplePush is backed by a best effort delivery mechanism over WebSocket
The protocol has some request-response driven actions, where the UserAgent MUST make requests, and the PushServer MAY respond. One message, notification, MAY be sent at any time by the PushServer.
ChannelID, UAIDs and Endpoints
Since the UAID is the only unique identifier of a UserAgent, with no authentication information, it is important that UAIDs are not compromised. Only the UserAgent to which the UAID was assigned, and the PushServer, should know the UAID. An attacker can use a leaked UAID to pretend to be the UserAgent with that UAID. Although SimplePush does not transfer any private information, the attacker would still receive the notifications meant for the victim. If any party detects a compromise, they SHOULD reset the UAID. For this, the UserAgent may send a blank UAID and get a new one from the PushServer. Similarly the PushServer may send a new UAID during a handshake.
ChannelIDs are also unique. It is RECOMMENDED that channelIDs be associated with a UAID, so that an attacker cannot receive notifications for a compromised channelID without also having accessed the UAID.
It is RECOMMENDED that both UAID and channelIDs be UUIDv4 tokens.
The format of the Endpoint is not specified by this protocol. There are two requirements that MUST be satisfied:
- PushServers MUST use HTTPS, so the Endpoint URL will begin with "https://".
- ChannelIDs and Endpoints MUST have a one-one mapping and Endpoints may not 'expire' while their channelID is still active.
It is RECOMMENDED that Endpoints simply be a prefix followed by the channelID or some transformation of the channelID which is referentially transparent. PushServers SHOULD avoid exposing UAIDs in the Endpoints since they are sent to AppServers.
Messages
All messages are encoded as JSON. All messages MUST have the following fields:
- messageType string
- Defines the message type
Handshake
After the WebSocket is established, the UserAgent begins communication by sending a hello message. The hello message contains the UAID if the UserAgent has one. The UserAgent also transmits the channelIDs it knows so the server may synchronize its state.
The server is MAY respect this UAID, but it is at liberty to ask the UserAgent to change its UAID in the response.
If the UserAgent receives a new UAID, it MUST delete all existing channelIDs and their associated versions. It MAY then wake up all registered applications immediately or at a later date by sending them a push-register message.
The handshake is considered complete, once the UserAgent has received a reply.
An UserAgent MUST transmit a hello message only once on its WebSocket. If the handshake is not completed in the first try, it MUST disconnect the WebSocket and begin a new connection.
NOTE: Applications may request registrations or unregistrations from the UserAgent, before or when the handshake is in progress. The UserAgent MAY buffer these or report errors to the application. But it MUST NOT send these requests to the PushServer until the handshake is completed.
UserAgent -> PushServer
- messageType = "hello"
- Begin handshake
- uaid string REQUIRED
- If the UserAgent has a previously assigned UAID, it should send it. Otherwise send an empty string.
- channelIDs list of strings REQUIRED
- If the UserAgent has a list of channelIDs it wants to be notified of, it must pass these, otherwise an empty list.
Extra fields: The UserAgent MAY pass any extra JSON data to the PushServer. This data may include information required to wake up the UserAgent out-of-band. The PushServer MAY ignore this data.
Example
{ "messageType": "hello", "uaid": "1234567890", "channelIDs": ["r2d2", "c3po"] }
PushServer -> UserAgent
PushServers MUST send only respond to a hello once. UserAgents MUST ignore multiple hello replies.
- messageType = "hello"
- Responses generally have the same messageType as the request
- uaid string REQUIRED
- If the UserAgent sent no UAID, generate a new one. If the UserAgent send a valid UAID and the PushServer is in sync with the UserAgent, send back the same UAID, otherwise send a new UAID.
Example
{
messageType: “hello”, uaid: <if not provided by previous call, a new valid one will be generated>
}
Register
Unregister
Notification
Synchronization of server and client state
Acheiving reliable delivery
At any time, if the PushServer reaches an inconsistent condition, due to database failure, network failure, or any other reason, it MAY drop all state. It MUST then disconnect all active clients, forcing them to reconnect and begin a handshake to get synchronized.
Alternative communication
In environments or devices where maintaining an always alive WebSocket is difficult, UserAgents and PushServer's MAY implement alternative means to notify UserAgents about updates. This out of band notification SHOULD be restricted to request the UserAgent to establish the WebSocket. SimplePush state SHOULD NOT be transmitted out of band, since reliable delivery may not be guaranteed.
API Push Client - Push Server
This should be considered a translation to WebSockets of the API defined here - https://wiki.mozilla.org/WebAPI/SimplePush/ServerAPI Status/Error codes documented in that document apply here unless explicitly marked otherwise.
Push API endpoint for WebSocket connection is : wss://push.server.com/v1/
C->S:
{ messageType: "hello", uaid: "<a valid UAID>",
channelIDs: [channelID1, channelID2, …],
interface: {
ip: "<current device IP address>",
port: "<TCP or UDP port in which the device is waiting for wake up notifications>" }, mobilenetwork: {
mcc: "<Mobile Country Code>", mnc: "<Mobile Network Code>"
},
protocol: <wakeup protocol. OPTIONAL. By default: UDP>
}
uaid can be null, then, a new uaid is created (see next)
S->C:
{ messageType: “hello”, status: xxx, <200, ...>, uaid: <if not provided by previous call, a new valid one will be generated> }
NOTE: All pending registration/unregistration requests stay on ‘hold’ until the client has transmitted new state and the server has concurred.
Recovery protocol
The client “hello” contains the “uaid” field. If the “uaid” field is known by the server, the server should check that the list of channelIDs sent by the client matches what it has. If there is even a single channelID that the server does not know about, it should generate a new UAID for the client, and drop all state about the current UAID. This will cause the client to generate new channelIDs by waking up all apps and having them register. This way apps get latest state since they contact their server.
If the server has ‘extra’ channelIDs associated with the UAID, it can simply delete them. Channel Registration:
A client channel registration request:
{ messageType: "register",
channelID: "<ChannelID>
}
The server response can be:
{ messageType: “register”, status: xxx, pushEndpoint: <pushendpoint>, channelID: <channelId> }
Channel Deletion:
{
messageType: "unregister", channelID: <channelId> }
The server response can be:
{
messageType: "unregister",
status: xxx,
channelID: <channelId> }
Channel Update:
Server -> Client { messageType: “notification”, updates: [{"channelID": "id", "version": "XXX"}, ...] }
Client -> Server { messageType: “ack”, updates: [{"channelID": channelID, “version”: xxx}, ...] } API Application Server - Notification Server
NOTIFICATION
https://server:port/notify/<hashed channelID> [Actually Opaque as far as anyone other than the push server is concerned]
Method PUT Payload:
version=<version>
April Discussion Below:
DESKTOP NOTIFICATION
https://server:port/notify/<hashed channelID>
Method PUT Payload:
body=<any text>[&ttl=<ttl>]
GROUPS MANAGEMENT [WIP]
CRUD API: C: POST https://server:port/groups Returns URL (pushEndpoint)
R: GET https://pushEndpoint Returns: [endPointURL1, endPointURL2, …]
U: PUT https://pushEndpoint url=endPointURLN&op=[ADD|DELETE] Returns: 200 OK or 404 No group found
D: DELETE https://pushEndpoint Removes the group Returns: 200 OK or 404 No group found
�
WAKEUP API [WIP]
This (WIP) API will be offered by the carriers in order to be able to wakeup handsets inside their own mobile networks.
Connection to this API will be protected with client certificates signed by the carrier so only trusted 3rd. party notification servers will be able to send datagrams to the handsets.
GET
mcc=<mcc>&mnc=<mnc>
RETURNS
200 OK (We’ve a wake up server in that network !)
404 Error (We don’t have a wake up server)
PUT
ip=<ip>&port=<port>&mcc=<mcc>&mnc=<mnc>
RETURNS
200 OK
�
Channel Update:
Server -> Client { messageType: “notification”, updates: [{"channelID": "id", "version": "XXX"}, ...] }
{
messageType: "desktopNotification",
updates: [{"channelID": "version", _internal_id: ..., "body": "body"}, ...]
}
Return Status codes
WIP: https://github.com/telefonicaid/notification_server/blob/mozAPI/src/common/constants.js nikhil: I think you’ll just want to use the ones from the mozilla spec that are required, like 404 and so on. Most of the other status codes are not required in my opinion.