WebAPI/PushAPI: Difference between revisions

(Created page with "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 se...")
 
No edit summary
 
(14 intermediate revisions by 6 users not shown)
Line 1: Line 1:
'''Mozilla has no plans on implementing this. Please see [[WebAPI/SimplePush]] instead.'''
''This page is for historical purposes only.''
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 ==
 
=== Server side ===
 
This is being implemented by [[User:willyaranda]] and [[User:frsela]] at Telefónica. Code is in [https://github.com/telefonicaid/notification_server Github]
 
=== 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 8: Line 25:


  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
===requestURL(WAToken, publicKey)===
to send notifications. When the success callback runs, <tt>request.result</tt> will be a
asks the user if they'd like to allow the app requesting to send notifications. If the app has been granted permission already and is calling <tt>requestURL()</tt> again, the original values are returned and no user interaction is required.
"JSON object" with the following structure
 
==== Arguments ====
<b><tt>WAToken</tt></b><br>
the Web App Token identifies uniquely the user (or installation) of the app (or a shared <tt>WAToken</tt>, used for broadcast). It is strongly suggested that this be a minimally 256bit random, base64 value or equivalent in order to reduce potential collisions. WATokens are limited to <b>45</b> URL safe characters.
 
<b><tt>publicKey</tt></b><br>
used to verify the origin of the notification. This is a cryptographic key generated by the AppProvider and should be unique per user.
 
==== Returns ====
<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;
  }
  }
<b><tt>status</tt></b><br />
"SUCCESS" on successful registration of <tt>WAToken</tt>, otherwise, "ERROR".
=====SUCCESS parameters =====
<b><tt>url</tt></b><br />
returns the URL the app server may use to send a Push Notification messages to the user.
<b><tt>messageType</tt></b><br />
<tt>registeredWA</tt> response to this message
<b><tt>WAToken</tt></b><br />
same value as the <tt>WAToken</tt> parameter


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.
=====Error parameters =====
<b><tt>reason</tt></b><br />
Optional value which may contain a more explicit message error.


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.
<b><tt>WAtoken</tt></b><br />
same value as the <tt>WAToken</tt> parameter


The <tt>userMessages/appMessages/appBadges</tt> properties indicate which types of push messages the website is allowed to push using the server API.
===getCurrentURL()===
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 <tt>getCurrentURL</tt> behaves the same as <tt>requestURL</tt>. However if the user hasn't yet made a decision, then <tt>getCurrentURL</tt> results in a success event, but with <tt>request.result</tt> set to null.


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.
==== Arguments ====
There are no arguments for this function


<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.
==== Returns ====
This function returns the stored values for <tt>requestURL()</tt> or Null if no stored values exist.


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


requestURL can fail with either "NetworkError" or "DeniedError"
==== Arguments ====
<b><tt>url</tt></b><br />
The URL returned by a previously successful <tt>requestURL()</tt> call.


=== Examples ===
Simple usage would look like this:
Simple usage would look like this:


Line 46: Line 94:
               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 87: Line 135:


== Server API ==
== Server API ==
On the server side we're looking at an HTTP interface that accepts POSTs
Push Notification Messages are sent to the <tt>url</tt> returned by the Client API <tt>requestURL()</tt> response (<i>NotificationURL</i>). Notification content consist of a JSON block POSTed to the <i>NotificationURL</i>
with these attributes:


* '''type''': "userMessage", "appMessage" or "appBadge"
=== Notification JSON Content ===
* '''body''': Secondary text of the notification. Only relevant for "userMessage" and "appMessage"
The Push Notification JSON block consists of the following:
* '''iconUrl''': URL of the icon to be shown with this notification. Only relevant for "userMessage"
* '''title''': Primary text of the notification. Only relevant for "userMessage"
* '''actionUrl''': URL to be opened if the user clicks on the notification. Only relevant for "userMessage"
* '''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"
* '''count''': The new badge count for the app. Only relevant for "addBadge"


The format of the payload is still TBD; it's a toss-up between
* '''messageType''': "notification". <b>Mandatory</b>
JSON blobs and form-urlencoded parameters.
* '''signature''': The text message signed with the Private Key that is verified and, if fails, the notification is thrown away and not accepted. <b>Mandatory</b>
 
* '''id''': a server side generated id to identify the notification. <b>Mandatory</b>.
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.
* '''message''': Main body. This could contain a maximum of 1024 bytes. Can be anything that fits in UTF8. <i>Optional</i>
* '''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. In seconds. <i>Optional</i>.
* '''timestamp''': Time when the notification was sent. In seconds. <i>Optional</i>.
* '''priority''': A priority value, from 1 to 4. 1 should be delivered instantly, and 4 will be delivered at the servers discretion. <i>Optional</i>


=== Response ===
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 =
 
(No feedback mechanism is listed, so I'm putting it here; feel free to point me elsewhere :-)
<i>
 
<blockquote>
* Is the "body" plain text or HTML, or something else?
* Are clients forced to support actionURL (the notification system currently used in Ubuntu, for example, specifically removed support for clicking on a notification to take an action)?
* What are the rules, if any, about cookie-sending and Referer and Origin when the actionURL is accessed?
* Are there maximum lengths for any of the fields?
* What about icons of multiple sizes?
* Does iconURL lead to a privacy issue because the site can see if the user has read the notification? Can we allow, or require, inline icons?
* Are there rules or guidelines to avoid accidentally clashing replaceIDs, such as a "org.mozilla.notification-somerandomstring" convention?
* How can we mitigate the problem of one (authorized) site spoofing notifications that look like those from another site? Will the in-browser UI show the origin of the notification?
 
-- Gerv
</blockquote>
 
<blockquote>
Responses
* plain text
* We can definitely make it optional. I wonder if we should return a set of capabilities along with the URL to let the page know what parts are supported. This will work well when we expand the API to support things like setting "badge numbers" for apps etc.
* Setting the Referer to the origin of the site making the request seems like a good idea. Cookies and Origin would be sent as normal (which means that no Origin header is sent).
* Yes, we should impose some limit to be determined.
* Good question, I don't have a good answer.
* Inline icons would be too expensive to push for each notification. One option would be to require that icons are provided when the notification URL is originally requested. That way we could handle multiple sizes too.
* replaceIDs are scoped to within the origin, so sites can use whatever scheme they want.
* How do we handle this for the Notification API?
-- Sicking
</blockquote>
 
 
<blockquote>
* How does this handle a user who uses two different Web browsers? (e.g. IE at work and Firefox at home)
-- Hixie
</blockquote>
 
<blockquote>
Responses
* UAs should allow setting custom notification services. For example, in Firefox we would by default deliver messages through a Mozilla server, but allow users to choose other notification delivery providers. By choosing the same delivery provider in all browsers, it should be quite possible to make it work. The main problem would be making sure that the delivery provider knows your identity in all browsers, possibly by making you log in when you first set up the service.
-- Sicking
</blockquote>
 
 
<blockquote>
* How is the privacy impact of following the user (IP addresses, usage times) reduced to the absolute minimum possible?
* Why the middle man Mozilla? Decentralization is a core principle of the Internet.
-- Ben Bucksch
</blockquote>
 
<blockquote>
* We "only" leak that information to the delivery service. I can't think of a way to allow the delivery service to send messages to the client without knowing the clients address. Suggestions welcome.
* Nothing in the API or protocol forces this to go through Mozilla. We can easily let the user choose any delivery service. However we need to go through a delivery service rather than having each website send messages to the device directly in order to reduce IP address/phone number/usage time leakage.
-- Sicking
 
* We don't "need" to go through Mozilla or anybody. When I browse the web, I already contact servers directly and they already see my IP address. Moreover, the server sees every webpage I visit and when. I can only prevent that by using a proxy all the time, but then the proxy can monitor me, and I'd rather avoid that. We have a similar tradeoff here. I argue the sensibility of my Internet usage times is less sensitive than the information which concrete webpages I visit, and the latter already leaks.
* This is changing core principles ("decentralization") of the Internet, and I don't see a good justification for that. You are basically forcing a proxy on everybody. If we wanted that, we'd all use HTTP proxies already.
 
-- Ben Bucksch
</blockquote>
 


<blockquote>
==== Error Response Body ====
* From a security viewpoint, the obvious choice to prevent spoofing is digital signatures. However, client-side (in this case the notifying website) developers who aren't familiar with PKI often find it too complex, resulting in a lack of third-party API implementations (see OAuth!). If Mozilla intends to develop, distribute and support the client API themselves, this is less of an issue. If this is not the case, OAuth 2.0 has some non-PKI options. Implementing all of the OAuth 2.0 protocol may be too heavyweight, but it would at least offer some inspiration.
When the notification is rejected, the NotificationURL server will return a JSON block with the following content:
* The designers should also consider verifying the integrity and source of notifications received by the browser.


-- Ryan Schipper
* '''status''': 'ERROR'
</blockquote>
* '''reason''': Reason the Status post failed to be handled. <i>Optional</i>


</i>
[[Category:Web APIs]]

Latest revision as of 23:56, 1 October 2014

Mozilla has no plans on implementing this. Please see WebAPI/SimplePush instead.

This page is for historical purposes only.

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. Code is in Github

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, publicKey)

asks the user if they'd like to allow the app requesting to send notifications. If the app has been granted permission already and is calling requestURL() again, the original values are returned and no user interaction is required.

Arguments

WAToken
the Web App Token identifies uniquely the user (or installation) of the app (or a shared WAToken, used for broadcast). It is strongly suggested that this be a minimally 256bit random, base64 value or equivalent in order to reduce potential collisions. WATokens are limited to 45 URL safe characters.

publicKey
used to verify the origin of the notification. This is a cryptographic key generated by the AppProvider and should be unique per user.

Returns

request.result will be a "JSON object" with the following structure

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

status
"SUCCESS" on successful registration of WAToken, otherwise, "ERROR".

SUCCESS parameters

url
returns the URL the app server may use to send a Push Notification messages to the user.

messageType
registeredWA response to this message

WAToken
same value as the WAToken parameter

Error parameters

reason
Optional value which may contain a more explicit message error.

WAtoken
same value as the WAToken parameter

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.

Arguments

There are no arguments for this function

Returns

This function returns the stored values for requestURL() or Null if no stored values exist.

revokeURL(url)

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

Arguments

url
The URL returned by a previously successful requestURL() call.

Examples

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

Push Notification Messages are sent to the url returned by the Client API requestURL() response (NotificationURL). Notification content consist of a JSON block POSTed to the NotificationURL

Notification JSON Content

The Push Notification JSON block consists of the following:

  • messageType: "notification". 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
  • id: a server side generated id to identify the notification. Mandatory.
  • message: Main body. This could contain a maximum of 1024 bytes. Can be anything that fits in UTF8. Optional
  • 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. In seconds. Optional.
  • timestamp: Time when the notification was sent. In seconds. Optional.
  • priority: A priority value, from 1 to 4. 1 should be delivered instantly, and 4 will be delivered at the servers discretion. Optional

Response

HTTP Status Codes in response:

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

Error Response Body

When the notification is rejected, the NotificationURL server will return a JSON block with the following content:

  • status: 'ERROR'
  • reason: Reason the Status post failed to be handled. Optional