WebPush Channel Broadcast Documentation¶
Overview¶
What is WebPush?¶
WebPush is the web standard to allow a server to notify its client that something changed.
In your webapp you ask the browser for a subscription, you start a service-worker that will handle notifications and you send your subscription to the server so that it can send you notifications.
For more about WebPush, do not hesitate to read this article
What is this WebPush Channel Broadcasting service about?¶
tl;dr Broadcasting notifications using WebPush made simple.
Handling sending notifications to a large number of users for a web service might be a problem.
- It can slow down the request response time by a lot, or you need to spawn tasks workers (i.e celery, rq))
- You need to store and retrieve the subscription and have endpoint for your clients to manage them.
- You need to handle errors, retry, etc.
The WebPush Channel broadcasting service follow the micro-services philosophy and handle all these tasks for you:
- It will allow you to notify all your users with one simple HTTP POST request that will be accepted in milliseconds.
- It will automatically handle the load of sending thousands of notifications, encrypt them specifically for each subscription.
- It will allow your users to manage their subscriptions and the list of channels they are listening to.
- It will handle errors and retry.
1.x¶
Full reference¶
Full detailed API documentation:
Authentication¶
A word about users¶
First of all, WebPush Channels doesn’t provide users management.
There is no such thing as user sign-up, password modification, etc.
However, users are uniquely identified.
How is that possible?¶
WebPush Channels uses the request headers to authenticate the current user.
Depending on the authentication methods enabled in configuration, the HTTP method to authenticate requests may differ.
WebPush Channels can rely on a third-party called «Identity provider» to authenticate the request and assign a user id.
There are many identity providers solutions in the wild. The most common are OAuth, JWT, SAML, x509, Hawk sessions...
A policy based on OAuth2 bearer tokens is recommended, but not mandatory.
Multiple policies¶
It is possible to enable several authentication methods.
In the current implementation, when multiple policies are configured, the first one in the list that succeeds is picked.
User identifiers are prefixed with the policy name being used.
OAuth Bearer token¶
If the configured authentication policy uses OAuth2 bearer tokens, authentication shall be done using this header:
Authorization: Bearer <oauth_token>
The policy will verify the provided OAuth2 bearer token on a remote server.
notes: | If the token is not valid, this will result in a 401 Unauthorized error response. |
---|
Portier¶
In order to enable authentication with Portier, install and configure Kinto/kinto-portier.
Firefox Accounts¶
In order to enable authentication with Firefox Accounts, install and configure mozilla-services/kinto-fxa.
Channels¶
Basically the idea being channels is the same as TV channels where one broadcast a message and others can listen to it.
The idea is that people can listen to channels even if they don’t exists.
The service configuration as well as the permission backend define a list of users that can publish on given channels.
Register to a channel¶
-
PUT
/channels/
(channel_id)/registration
¶ synopsis: Subscribe the user to the channel Requires authentication
Example Request
$ http PUT http://localhost:9999/v0/channels/formbuilder-collections-update/registration Authorization:"Portier dccd8ac07f3e45c9907da638e994ff98" -v
PUT /v0/channels/formbuilder-collections-update/registration HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Authorization: Portier dccd8ac07f3e45c9907da638e994ff98 Connection: keep-alive Host: localhost:9999 User-Agent: HTTPie/0.9.2
Example Response
HTTP/1.1 202 Accepted Access-Control-Expose-Headers: Backoff, Retry-After, Alert Date: Thu, 18 Jun 2015 17:02:23 GMT Server: waitress {"code": 202, "message": "Accepted"}
Unsubscribing from a channel¶
-
DELETE
/channels/
(channel_id)/registration
¶ synopsis: Unsubscribe the user from the channel Requires authentication
Example Request
$ http delete http://localhost:9999/v0/channels/formbuilder-collections-write/registration Authorization:"Portier dccd8ac07f3e45c9907da638e994ff98" -v
DELETE /v0/channels/formbuilder-collections-update/registration HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Authorization: Portier dccd8ac07f3e45c9907da638e994ff98 Connection: keep-alive Host: localhost:9999 User-Agent: HTTPie/0.9.2
Example Response
HTTP/1.1 202 Accepted Access-Control-Expose-Headers: Backoff, Retry-After, Alert Date: Thu, 18 Jun 2015 17:02:23 GMT Server: waitress {"code": 202, "message": "Accepted"}
Getting channels informations¶
-
GET
/channels/
(channel_id)¶ Synopsis: Retrieve channel informations Example Request
$ http get http://localhost:9999/v0/channels/formbuilder-collection-write Authorization:"Portier dccd8ac07f3e45c9907da638e994ff98" -v
GET /v0/channels/formbuilder-collection-write HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Authorization: Basic Ym9iOg== Connection: keep-alive Host: localhost:9999 User-Agent: HTTPie/0.9.2
Example Response
HTTP/1.1 200 OK Access-Control-Expose-Headers: Backoff, Retry-After, Alert, Last-Modified, ETag Content-Length: 211 Content-Type: application/json; charset=UTF-8 Date: Thu, 18 Jun 2015 17:29:59 GMT Etag: "1434648599199" Last-Modified: Thu, 18 Jun 2015 17:29:59 GMT Server: waitress { "data": { "id": "formbuilder-collection-write", "registrations": 1, "push": 0 } }
- registration contains the number of users that subscribed to the channel.
- push contains the number of push that were sent to the channel.
Broadcasting a push notification¶
For the first version, only users configured in the service configuration can broadcast notifications.
However in the future we aim at adding a permissions management feature to the channel.
-
POST
/channels/
(channel_id)¶ synopsis: Push a notification Requires authentication
Example Request
$ http post http://localhost:9999/v0/channels/formbuilder-collections-write Authorization:"Portier dccd8ac07f3e45c9907da638e994ff98" -v
POST /v0/channels/formbuilder-collections-update HTTP/1.1 Accept: application/json Accept-Encoding: gzip, deflate Authorization: Basic Ym9iOg== Connection: keep-alive Content-Length: 25 Content-Type: application/json Host: localhost:9999 User-Agent: HTTPie/0.9.2 { "data": { "last_modified": 1434647996969 } }
Example Response
HTTP/1.1 202 Accepted Access-Control-Expose-Headers: Backoff, Retry-After, Alert Date: Thu, 18 Jun 2015 17:02:23 GMT Server: waitress {"code": 202, "message": "Accepted"}
The data
payload will be encrypted for each subscriptions and sent
authenticated through the endpoint.
Subscriptions¶
Subscriptions belongs to an user.
A user can have multiple subscriptions (one per browser session or device).
A subscription consist on the information that you can get from a requesting a Push subscription on the browser.
{
"data": {
"endpoint": "https://updates.push.services.mozilla.com/wpush/v1/gAAAAABYZNChoTLTAeA9vv-_zeqGuZiM4ESpiV7oiT5XtrN8aI01fiCQ7-_hC8lhqXanjUEWp5MFRoq35QmzdplCkRhp5nRgjwneGCGO8WXYH9psZaD_xInKLWm7K8-tzFAp-vRNHx79",
"keys": {
"auth": "pnipzxpMvKBNYZAcxc-MAA",
"p256dh": "BEVoH6cOlNPuvYR0aVJo4GVv84nbymzpXxNff7hpKYjVIFcuIEtqiLtIe4rLOXF_A2w3KWRJoCYJEjUedrXcNpc"
}
}
}
Add a new user subscription¶
-
POST
/subscriptions
¶ synopsis: Store a subscription. The ID will be assigned automatically. Requires authentication
Example Request
$ echo '{"data": {"endpoint": "URL", "keys": {}}}' | http post http://localhost:9999/v0/subscriptions Authorization:"Portier dccd8ac07f3e45c9907da638e994ff98" -v
POST /v0/subscriptions HTTP/1.1 Accept: application/json Accept-Encoding: gzip, deflate Authorization: Portier dccd8ac07f3e45c9907da638e994ff98 Connection: keep-alive Content-Length: 25 Content-Type: application/json Host: localhost:9999 User-Agent: HTTPie/0.9.2 { "data": { "endpoint": "https://updates.push.services.mozilla.com/wpush/v1/gAAAAABYZNChoTLTAeA9vv-_zeqGuZiM4ESpiV7oiT5XtrN8aI01fiCQ7-_hC8lhqXanjUEWp5MFRoq35QmzdplCkRhp5nRgjwneGCGO8WXYH9psZaD_xInKLWm7K8-tzFAp-vRNHx79", "keys": { "auth": "pnipzxpMvKBNYZAcxc-MAA", "p256dh": "BEVoH6cOlNPuvYR0aVJo4GVv84nbymzpXxNff7hpKYjVIFcuIEtqiLtIe4rLOXF_A2w3KWRJoCYJEjUedrXcNpc" } } }
Example Response
HTTP/1.1 201 Created Access-Control-Expose-Headers: Backoff, Retry-After, Alert Content-Length: 199 Content-Type: application/json; charset=UTF-8 Date: Thu, 18 Jun 2015 17:02:23 GMT Server: waitress { "data": { "endpoint": "https://updates.push.services.mozilla.com/wpush/v1/gAAAAABYZNChoTLTAeA9vv-_zeqGuZiM4ESpiV7oiT5XtrN8aI01fiCQ7-_hC8lhqXanjUEWp5MFRoq35QmzdplCkRhp5nRgjwneGCGO8WXYH9psZaD_xInKLWm7K8-tzFAp-vRNHx79", "keys": { "auth": "pnipzxpMvKBNYZAcxc-MAA", "p256dh": "BEVoH6cOlNPuvYR0aVJo4GVv84nbymzpXxNff7hpKYjVIFcuIEtqiLtIe4rLOXF_A2w3KWRJoCYJEjUedrXcNpc" } } }
Validation¶
If the posted values are invalid (e.g. field value is not an integer)
an error response is returned with 400 Bad Request
.
HTTP Status Codes¶
200 OK
: This object already exists, the one stored on the database is returned201 Created
: The object was created400 Bad Request
: The request body is invalid401 Unauthorized
: The request is missing authentication headers403 Forbidden
: The user is not allowed to perform the operation, or the resource is not accessible406 Not Acceptable
: The client doesn’t accept supported responses Content-Type412 Precondition Failed
: List has changed since value inIf-Match
header415 Unsupported Media Type
: The client request was not sent with a correct Content-Type
Retrieving user’s subscriptions¶
-
GET
/subscriptions
¶ Synopsis: Retrieve all the subscriptions for the user. Requires authentication
Example Request
$ http get http://localhost:9999/v0/subscriptions Authorization:"Portier dccd8ac07f3e45c9907da638e994ff98"
GET /v0/subscriptions HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Authorization: Portier dccd8ac07f3e45c9907da638e994ff98 Connection: keep-alive Host: localhost:9999 User-Agent: HTTPie/0.9.2
HTTP/1.1 200 OK Access-Control-Expose-Headers: Backoff, Retry-After, Alert, Next-Page, Total-Records, Last-Modified, ETag Content-Length: 110 Content-Type: application/json; charset=UTF-8 Date: Thu, 18 Jun 2015 17:24:38 GMT Etag: "1434648278603" Last-Modified: Thu, 18 Jun 2015 17:24:38 GMT Server: waitress Total-Records: 1 { "data": [ { "endpoint": "https://updates.push.services.mozilla.com/wpush/v1/gAAAAABYZNChoTLTAeA9vv-_zeqGuZiM4ESpiV7oiT5XtrN8aI01fiCQ7-_hC8lhqXanjUEWp5MFRoq35QmzdplCkRhp5nRgjwneGCGO8WXYH9psZaD_xInKLWm7K8-tzFAp-vRNHx79", "keys": { "auth": "pnipzxpMvKBNYZAcxc-MAA", "p256dh": "BEVoH6cOlNPuvYR0aVJo4GVv84nbymzpXxNff7hpKYjVIFcuIEtqiLtIe4rLOXF_A2w3KWRJoCYJEjUedrXcNpc" }, "id": "89881454-e4e9-4ef0-99a9-404d95900352", "last_modified": 1434647996969 } ] }
Delete user’s subscriptions¶
-
DELETE
/subscriptions
¶ Synopsis: Delete all the user’s subscriptions Requires authentication
Example Request
$ http delete http://localhost:9999/v0/subscriptions Authorization:"Portier dccd8ac07f3e45c9907da638e994ff98"
DELETE /v0/subscriptions HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Authorization: Portier dccd8ac07f3e45c9907da638e994ff98 Connection: keep-alive Host: localhost:9999 User-Agent: HTTPie/0.9.2
Example Response
HTTP/1.1 200 OK Access-Control-Expose-Headers: Backoff, Retry-After, Alert, Last-Modified, ETag Content-Length: 211 Content-Type: application/json; charset=UTF-8 Date: Thu, 18 Jun 2015 17:29:59 GMT Etag: "1434648599199" Last-Modified: Thu, 18 Jun 2015 17:29:59 GMT Server: waitress { "data": [{ "deleted": true, "id": "89881454-e4e9-4ef0-99a9-404d95900352", "last_modified": 1434648749173 }] }
Deleting a single subscription¶
-
DELETE
/subscriptions/
(subscription_id)¶ Synopsis: Delete a subscription by its ID. Example Request
$ http delete http://localhost:9999/v0/subscriptions/89881454-e4e9-4ef0-99a9-404d95900352 Authorization:"Portier dccd8ac07f3e45c9907da638e994ff98"
DELETE /v0/subscriptions/89881454-e4e9-4ef0-99a9-404d95900352 HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Authorization: Portier dccd8ac07f3e45c9907da638e994ff98 Connection: keep-alive Content-Length: 0 Host: localhost:9999 User-Agent: HTTPie/0.9.2
Example Response
HTTP/1.1 200 OK Access-Control-Expose-Headers: Backoff, Retry-After, Alert Content-Length: 99 Content-Type: application/json; charset=UTF-8 Date: Thu, 18 Jun 2015 17:32:29 GMT Server: waitress { "data": { "deleted": true, "id": "89881454-e4e9-4ef0-99a9-404d95900352", "last_modified": 1434648749173 } }
Utility endpoints for OPS and Devs¶
GET /¶
The returned value is a JSON mapping containing:
Changed in version 3.0.
project_name
: the name of the service (e.g."reading list"
)project_docs
: The URL to the service documentation. (this document!)project_version
: complete application/project version ("3.14.116"
)http_api_version
: the MAJOR.MINOR version of the exposed HTTP API ("1.1"
) defined in the project.url
: absolute URI (without a trailing slash) of the API (can be used by client to build URIs)eos
: date of end of support in ISO 8601 format ("yyyy-mm-dd"
, undefined if unknown)settings
: a mapping with the values of relevant public settings for clientsbatch_max_requests
: Number of requests that can be made in a batch request.readonly
: Only requests with read operations are allowed.
capabilities
: a mapping used by clients to detect optional features of the API.Example:
{ "fxa": { "description": "Firefox Account authentication", "url": "http://github.com/mozilla-services/kinto-fxa" } }
Optional
user
: A mapping with anid
field and a list ofprincipals
for the currently connected user id. The field is not present when no Authorization header is provided.
Note
The project_version
contains the source code version, whereas the http_api_version
contains the exposed HTTP API version.
The source code of the service can suffer changes and have its project version incremented, without impacting the publicly exposed HTTP API.
GET /__heartbeat__¶
Return the status of each service the application depends on. The returned value is a JSON mapping containing:
storage
true if storage backend is operationalcache
true if cache backend operational
If kinto-fxa
is installed, an additional key is present:
oauth
true if FxA authentication is operational
If kinto-portier
is installed, an additional key is present:
portier
true if portier authentication is operational
Return 200 OK
if the connection with each service is working properly
and 503 Service Unavailable
if something doesn’t work.
GET /__lbheartbeat__¶
Always return 200 OK
with empty body.
Unlike the __heartbeat__
health check endpoint, which return an error
when backends and other upstream services are unavailable, this should
always return 200 OK
.
This endpoint is suitable for a load balancer membership test. It the load balancer cannot obtain a response from this endpoint, it will stop sending traffic to the instance and replace it.
GET /contribute.json¶
The returned value is a JSON mapping containing open source contribution information as advocated by https://www.contributejson.org
GET /__version__¶
Return a JSON mapping containing information about what distribution has been deployed by OPs.
{
"name":"webpush-channels",
"version":"1.0.1",
"commit":"ab8db089ee63dc8e14f4bcfc427a86f311dd7e52",
"source":"https://github.com/mansimarkaur/webpush-channels.git"
}
The content of this view comes from a file, whose location is
specified via the webpush_channels.version_json_path
setting or
WEBPUSH_CHANNELS_VERSION_JSON_PATH
environment variable (default
location is version.json
in current working directory).
Return 404 Not Found
if no version.json
file is found.
Backoff indicators¶
Backoff header on heavy load¶
A Backoff
header will be added to the success responses (>=200 and
<400) when the server is under heavy load. It provides the client with
a number of seconds during which it should avoid doing unnecessary
requests.
Backoff: 30
Note
The back-off time is configurable on the server.
Note
In other implementations at Mozilla, there was
X-Weave-Backoff
and X-Backoff
but the X-
prefix for
header has been deprecated since.
Retry-After indicators¶
A Retry-After
header will be added if response is an error (>=500).
See more details about error responses.
Error responses¶
API description¶
Every response is JSON.
If the HTTP status is not OK (<200 or >=400), the response contains a JSON mapping, with the following attributes:
code
: matches the HTTP status code (e.g400
)errno
: stable application-level error number (e.g.109
)error
: string description of error type (e.g."Bad request"
)message
: context information (e.g."Invalid request parameters"
)info
: online resource (e.g. URL to error details)details
: additional details (e.g. list of validation errors)
Example response
{
"code": 412,
"errno": 114,
"error": "Precondition Failed",
"message": "Resource was modified meanwhile",
"info": "https://server/docs/api.html#errors",
}
Refer yourself to the set of errors codes.
Retry-After indicators¶
A Retry-After
header will be added to error responses (>=500),
telling the client how many seconds it should wait before trying
again.
Retry-After: 30
Validation errors¶
When multiple validation errors occur on a request, the first one is presented in the message.
The full list of validation errors is provided in the details
field.
{
"code": 400,
"errno": 109,
"error": "Bad Request",
"message": "Invalid posted data",
"info": "https://server/docs/api.html#errors",
"details": [
{
"description": "42 is not a string: {'name': ''}",
"location": "body",
"name": "name"
}
]
}
Deprecation¶
A track of the client version will be kept to know after which date each old version can be shutdown.
The date of the end of support is provided in the API root URL (e.g. /v1
)
Using the Alert
response header, the server can communicate any potential warning
messages, information, or other alerts.
The value is JSON mapping with the following attributes:
code
: one of the strings"soft-eol"
or"hard-eol"
;message
: a human-readable message (optional);url
: a URL at which more information is available (optional).
A 410 Gone
error response can be returned if the
client version is too old, or the service had been remplaced with
a new and better service using a new API version.
See details in configuration to activate deprecation.