WebAPI/PushAPI: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 1: Line 1:
Push notifications are a way for websites to send small messages to users when
Push notifications are a way for websites to send small messages to users when
the user is not on the site. iOS and Android devices already support their own
the user is not on the site. iOS and Android devices already support their own
push notification services, but we want to make notifications available to the
push notification services so we want to develop it to [[FirefoxOS]].
whole web.


== Status ==
== Status ==
The server API needs to be verified by someone that knows server-infrastructure better than me.


We should also verify this API against the experiences from Google here: [http://research.google.com/pubs/archive/37474.pdf]
=== Server side ===
 
This is being implemented by [[User:willyaranda]] and [[User:frsela]] at Telefónica.
 
=== Gecko implementation ===
 
[[User:Thinker]] has implemented the Gecko side.
 
[https://bugzilla.mozilla.org/show_bug.cgi?id=763198 Bug #763198] is the implementation bug.
 
[https://bugzilla.mozilla.org/show_bug.cgi?id=776501 Bug#776501] is the security bug.


== Client API ==
== Client API ==
Line 13: Line 21:


  interface PushManager {
  interface PushManager {
   DOMRequest requestURL();
   DOMRequest requestURL(jsval watoken, jsval PbK);
   DOMRequest getCurrentURL();
   DOMRequest getCurrentURL();
   DOMRequest revokeURL();
   DOMRequest revokeURL(jsval URL);
  }
  }


<tt>requestURL()</tt> asks the user if they'd like to allow this site
<tt>requestURL(watoken, PbK)</tt> asks the user if they'd like to allow the app requesting
to send notifications. When the success callback runs, <tt>request.result</tt> will be a
to send notifications. It requires two arguments, a "watoken" that identifies uniquely the user (or installation) of the app (or a shared watoken, used for broadcast) and the app public key, that will be used to verify the origin of the notification.
"JSON object" with the following structure
 
When the success callback runs, <tt>request.result</tt> will be a "JSON object" with the following structure


  dictionary PushURLResult {
  dictionary PushURLResult {
   DOMString url;
   DOMString status;
   DOMString[] supportedVersions;
   DOMString reason?;
   boolean userMessages;
   DOMString url?;
   boolean appMessages;
   DOMString messageType;
   boolean appBadges;
   DOMString WAtoken;
  }
  }


The <tt>url</tt> property contains the URL which the site can send messages to this user. It's up to the site to send the URL to the backend for storage.
The <tt>status</tt> property says if the registration of a given WA token is correct or not:
* a <tt>ERROR</tt> status is sent, then, the <tt>reason</tt> could contain a more explicit message error.
* a <tt>SUCCESS</tt> status is sent when it's correct. Then, the field <tt>url</tt> is defined.


The <tt>supportedVersions</tt> property contains a list of server-protocol versions that the server supports. The website should always use the first version in this array that it supports. Later versions in the array are more likely to be deprecated first.
The <tt>reason</tt> is only defined if the <tt>status</tt> field (shown above) is <tt>ERROR</tt>. Not mandatory.


The <tt>userMessages/appMessages/appBadges</tt> properties indicate which types of push messages the website is allowed to push using the server API.
The <tt>url</tt> property contains the URL which the app server can send messages to this user. It's up to the app to send the URL to the server backend for storage.


If the site has been granted permission already and is calling <tt>requestURL()</tt> again, we can return the same URL without bothering the user.
The <tt>messageType</tt> is <tt>registerWA</tt> which is the response to this kind of messages.


<tt>getCurrentURL()</tt> lets the site ask Firefox if the site has a push URL without bothering the user. The function behaves the same way as <tt>requestURL()</tt> except for the case when <tt>requestURL()</tt> would prompt the user. I.e. if the user has already granted permission, or if the user has permanently denied permission, then getCurrentURL behaves the same as requestURL. However if the user hasn't yet made a decision, then getCurrentURL results in a success event, but with request.result set to null.
The <tt>WAtoken</tt> is the app token that was used to register. This can be used by the user agent to identify what URL belongs to a specified token or if there is some error.


<tt>revokeURL()</tt> lets the website indicate that it no longer wants to be able to push messages using the indicated URL.
If the app has been granted permission already and is calling <tt>requestURL()</tt> again, we can return the same URL without bothering the user.


requestURL can fail with either "NetworkError" or "DeniedError"
<tt>getCurrentURL()</tt> lets the app ask Firefox if the app has a push URL without bothering the user. The function behaves the same way as <tt>requestURL()</tt> except for the case when <tt>requestURL()</tt> would prompt the user. I.e. if the user has already granted permission, or if the user has permanently denied permission, then getCurrentURL behaves the same as requestURL. However if the user hasn't yet made a decision, then getCurrentURL results in a success event, but with request.result set to null.
 
<tt>revokeURL(url)</tt> lets the app indicate that it no longer wants to be able to push messages using the indicated URL for this installation.


Simple usage would look like this:
Simple usage would look like this:
Line 51: Line 64:
               navigator.webkitPush);
               navigator.webkitPush);
   // Ask the user to allow notifications
   // Ask the user to allow notifications
   var request = push.requestURL();
   var request = push.requestURL(watoken, PbK);
   request.onsuccess = function() {
   request.onsuccess = function() {
     var url = request.result.url;
     var url = request.result.url;
Line 92: Line 105:


== Server API ==
== Server API ==
On the server side we're looking at an HTTP interface that accepts POSTs
On the server side we're looking at an HTTP interface that accepts a JSON POST
with these attributes:
with these attributes:


* '''type''': "userMessage", "appMessage" or "appBadge"
* '''messageType''': "notification". Mandatory
* '''body''': Secondary text of the notification. Only relevant for "userMessage" and "appMessage"
* '''id''': a server side generated id to identify the notification. Not mandatory.
* '''iconUrl''': URL of the icon to be shown with this notification. Only relevant for "userMessage"
* '''message''': Main body. This could contain a maximum of 1024 bytes. Can be anything that fits in UTF8. Mandatory
* '''title''': Primary text of the notification. Only relevant for "userMessage"
* '''signature''': The text message signed with the Private Key that is verified and, if fails, the notification is thrown away and not accepted. Mandatory
* '''actionUrl''': URL to be opened if the user clicks on the notification. Only relevant for "userMessage"
* '''ttl''': Maximum time of live of the notification. The server will discard any notification in a maximum time or in the ttl time, whatever first occurs. Not mandatory.
* '''replaceId''': A string which identifies a group of like messages. If the user is offline, only the last message with the same <tt>replaceId</tt> will be sent when the user comes back online. Only relevant for "userMessage"
* '''timestamp''': Time when the notification was sent. Not mandatory.
* '''count''': The new badge count for the app. Only relevant for "addBadge"
* '''priority''': A priority value, from 1 to 4. 1 should be delivered instantly, and 4 co
 
The format of the payload is still TBD; it's a toss-up between
JSON blobs and form-urlencoded parameters.
 
The server can also issue a DELETE request to the URL to indicate that it no longer wants to send push messages. This is useful for example if the developer accidentally leaked the URL.


HTTP Status Codes in response:
HTTP Status Codes in response:


* '''200''': Notification accepted for delivery
* '''200''': Notification accepted for delivery
* '''404''': We don't have that URL in our database
* '''400''': Notification rejected.
* '''410''': The user isn't receiving these notifications, e.g. access was revoked
 
To support bulk sends, we may be able to use SPDY to get longer-lived connections sending
a bunch of requests while still keeping the same HTTP interface.


= Feedback =
HTTP Body response:


Please put feedback on the [[Talk:WebAPI/PushAPI|Talk Page]]
When received a 400, there is a JSON with the status ERROR and a possible reason.

Revision as of 13:21, 18 September 2012

Push notifications are a way for websites to send small messages to users when the user is not on the site. iOS and Android devices already support their own push notification services so we want to develop it to FirefoxOS.

Status

Server side

This is being implemented by User:willyaranda and User:frsela at Telefónica.

Gecko implementation

User:Thinker has implemented the Gecko side.

Bug #763198 is the implementation bug.

Bug#776501 is the security bug.

Client API

The API will be an object at navigator.push with this interface:

interface PushManager {
  DOMRequest requestURL(jsval watoken, jsval PbK);
  DOMRequest getCurrentURL();
  DOMRequest revokeURL(jsval URL);
}

requestURL(watoken, PbK) asks the user if they'd like to allow the app requesting to send notifications. It requires two arguments, a "watoken" that identifies uniquely the user (or installation) of the app (or a shared watoken, used for broadcast) and the app public key, that will be used to verify the origin of the notification.

When the success callback runs, request.result will be a "JSON object" with the following structure

dictionary PushURLResult {
  DOMString status;
  DOMString reason?;
  DOMString url?;
  DOMString messageType;
  DOMString WAtoken;
}

The status property says if the registration of a given WA token is correct or not:

  • a ERROR status is sent, then, the reason could contain a more explicit message error.
  • a SUCCESS status is sent when it's correct. Then, the field url is defined.

The reason is only defined if the status field (shown above) is ERROR. Not mandatory.

The url property contains the URL which the app server can send messages to this user. It's up to the app to send the URL to the server backend for storage.

The messageType is registerWA which is the response to this kind of messages.

The WAtoken is the app token that was used to register. This can be used by the user agent to identify what URL belongs to a specified token or if there is some error.

If the app has been granted permission already and is calling requestURL() again, we can return the same URL without bothering the user.

getCurrentURL() lets the app ask Firefox if the app has a push URL without bothering the user. The function behaves the same way as requestURL() except for the case when requestURL() would prompt the user. I.e. if the user has already granted permission, or if the user has permanently denied permission, then getCurrentURL behaves the same as requestURL. However if the user hasn't yet made a decision, then getCurrentURL results in a success event, but with request.result set to null.

revokeURL(url) lets the app indicate that it no longer wants to be able to push messages using the indicated URL for this installation.

Simple usage would look like this:

function getPushURL() {
  var push = (navigator.push ||
              navigator.mozPush ||
              navigator.webkitPush);
  // Ask the user to allow notifications
  var request = push.requestURL(watoken, PbK);
  request.onsuccess = function() {
    var url = request.result.url;
    console.log('Push URL: ' + url);
    // We got a new push URL, store it on the server.
    jQuery.post('/push-urls/', {url: url});
  };
}

If a website wants to display a button which allows the user to start using the push feature for the website, it could do something like this

function displayPushButton(buttonElement) {
  var push = (navigator.push ||
              navigator.mozPush ||
              navigator.webkitPush);
  // Ask the user to allow notifications
  var request = push.getCurrentURL();
  request.onsuccess = function() {
    var result = request.result;
    if (result) {
      // Hide button as we already have the push URL
      buttonElement.hidden = true;
    }
    else {
      // Display button
      buttonElement.hidden = false;
      button.disabled = false;
      button.textContent = "Enable push notifications";
      button.onclick = getPushURL;
    }
  };
  request.onerror = function() {
    if (request.error.name == "DeniedError") {
      // Indicate to user that it's disabled due to user choice
      button.disabled = true;
      button.textContent = "Disabled, change settings to enable";
    }
  }
}

Server API

On the server side we're looking at an HTTP interface that accepts a JSON POST with these attributes:

  • messageType: "notification". Mandatory
  • id: a server side generated id to identify the notification. Not mandatory.
  • message: Main body. This could contain a maximum of 1024 bytes. Can be anything that fits in UTF8. Mandatory
  • signature: The text message signed with the Private Key that is verified and, if fails, the notification is thrown away and not accepted. Mandatory
  • ttl: Maximum time of live of the notification. The server will discard any notification in a maximum time or in the ttl time, whatever first occurs. Not mandatory.
  • timestamp: Time when the notification was sent. Not mandatory.
  • priority: A priority value, from 1 to 4. 1 should be delivered instantly, and 4 co

HTTP Status Codes in response:

  • 200: Notification accepted for delivery
  • 400: Notification rejected.

HTTP Body response:

When received a 400, there is a JSON with the status ERROR and a possible reason.