Skip to content
Simple and extendable appserver for XMPP pushes (aka. XEP-0357)
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
mod_push_appserver
mod_push_appserver_apns
mod_push_appserver_fcm
tools
LICENSE
README.md

README.md

mod_push_appserver

Simple and extendable app server for XMPP push notifications as defined in XEP-0357.

The app server is implemented as a module for the Prosody XMPP server.

Currently, only push notifications to Apple's APNS and Google's FCM are implemented, but other push services can easily be added in a separate module.

Requirements

  • Prosody 0.9 or later.
  • Lua 5.1 or 5.2.
  • Installed luasec Lua library version 0.5 (Debian package: lua-sec), higher versions are untested.

Installation

Just check out the repository somewhere and point prosody at this directory using plugin_paths in its main config file.
For example: plugin_paths = { "/usr/local/lib/mod_push_appserver" }.

Then add mod_push_appserver and the submodule you need (for example mod_push_appserver_apns or mod_push_appserver_fcm) to global modules_enabled or to the enabled modules of a specific virtual host.

I will eventually add a commented minimal configuration example for prosody to this repository, too.

Usage notes (configuration)

For chat apps using VoIP pushes to APNS, the priority should be set to high. The alert text can be ignored in this case (if you only want to wakeup your device). For normal push notifications, the priorities high and silent are supported. The configured alert text (push_appserver_apns_push_alert) is ignored for silent pushes.

For pushes to FCM the priorities high and normal are supported with normal priorities being delayed while the device is in doze mode. Pushes having priority high are always delivered, even in doze mode, thus should be used for chat apps.

Configuration options (mod_push_appserver)

  • push_appserver_debugging (boolean)
    Make /push_appserver/v1/settings HTTP endpoint available. Default: false.
    This setting will also make http forms available at all POST HTTP endpoints for easier manual testing of your setup by simply using your browser of choice.
  • push_appserver_rate_limit (number)
    Allow this much requests per second. Default: 5.
    This should mitigate some DOS attacks.

Configuration options (mod_push_appserver_apns)

  • push_appserver_apns_cert (string)
    Path to your APNS push certificate in PEM format.
  • push_appserver_apns_key (string)
    Path to your APNS push certificate key in PEM format.
  • push_appserver_apns_capath (string)
    Path to CA certificates directory. Default: "/etc/ssl/certs" (Debian and Ubuntu use this path for the system CA store).
  • push_appserver_apns_ciphers (string)
    Ciphers to use when establishing a tls connection. Default: ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256
  • push_appserver_apns_sandbox (boolean) Use apns sandbox api endpoint if true, production endpoint otherwise. Default: true.
  • push_appserver_apns_push_alert (string)
    Alert text for push message. Default: "dummy".
  • push_appserver_apns_push_ttl (number)
    TTL for push notification in seconds. Default: nil (that means infinite).
  • push_appserver_apns_push_priority (string)
    Value "high" for high priority pushes always triggering a visual indication on the user's phone, "silent" for silent pushes that can be delayed or not delivered at all but don't trigger a visual indication and "auto" to let the appserver automatically decide between "high" and "silent" based on the presence of "last-message-body" in the push summary received from the XMPP server. NOTE: if you have VoIP capabilities in your app "silent" pushes will become reliable and always wake up your app without triggering any visual indications on the user's phone. In VoIP mode your app can decide all by itself if it wants to show a notification to the user or not by simply logging into the XMPP account in the backround and retrieving the stanzas that triggered the push. Default: "silent".
  • push_appserver_apns_feedback_request_interval (number)
    Interval in seconds to query Apple's feedback service for extinction of invalid tokens. Default: 24 hours.

Configuration options (mod_push_appserver_fcm)

  • push_appserver_fcm_key (string)
    Your FCM push credentials.
  • push_appserver_fcm_capath (string)
    Path to CA certificates directory. Default: "/etc/ssl/certs" (Debian and Ubuntu use this path for the system CA store).
  • push_appserver_fcm_ciphers (string)
    Ciphers to use when establishing a tls connection. Default: ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256
  • push_appserver_fcm_push_ttl (number)
    TTL for push notification in seconds, can be 4 weeks at max. Default: nil (that means 4 weeks).
  • push_appserver_fcm_push_priority (string)
    Value "high" for high priority pushes that wake up the device even when in doze mode or "normal" for normal pushes that can be delayed. Default: "high".

API (register device etc.)

This appserver implements XEP-0357 commands for sending actual push notifications. Additionally XEP-0357 requires a device to be registered on the appserver but does not dictate how this should be done.

Therefore this appserver provides two different APIs to register a (new) device on the appserver. Just use the one more convenient to you.

XEP-0050 Ad-Hoc Command API

This API resembles more or less what Conversations is doing, but with some small differences:

  • the command is named "v1-register-push" instead of "register-push-gcm"
  • the device id field is called "node" instead of "device-id"
  • the new field "type" was added. This can be used to specify the push type just as it is done using the http based API.
  • You can only register the device using this API, no unregister possible (use the HTTP API for unregistering devices)

This Gist demonstrates the changes needed to Conversations to use this appserver instead of inputmice's p2. See XEP-0050 for more info regarding Ad-Hoc Commands in general.

Keep in mind that the registration command sent to this appserver is routed through the user's xmpp server. This exposes the raw APNS/FCM push token and device id to the user's xmpp server. Use the HTTP API if you don't like this.

HTTP API

All POST endpoints can be used via GET to get back a simple html form which allows you to manually test the endpoint behaviour in your browser, if the config option push_appserver_debugging is set to true (an error is returned otherwise). This config option should be false in production environments!

  • POST to http://<host>:5280/push_appserver/v1/register or https://<host>:5281/push_appserver/v1/register
    POST data: type=<push type>&node=<device uuid>&token=<apns/fcm/etc. push token>
    function: register device for push
    result: text document separated by \n

    • first line: OK, everything else (including ERROR) is specified as error condition if ok: 2nd line: XEP-0357 push node, 3rd line: XEP-0357 push secret
      if error: 2nd and subsequent lines: error description
  • POST to http://<host>:5280/push_appserver/v1/unregister or https://<host>:5281/push_appserver/v1/unregister
    POST data: type=<push type>&node=<device uuid>
    function: unregister device
    result: text document separated by \n

    • first line: OK, everything else (including ERROR) is specified as error condition
      if ok: 2nd line: XEP-0357 push node, 3rd line: XEP-0357 push secret
      if error: 2nd and subsequent lines: error description
  • POST to http://<host>:5280/push_appserver/v1/push or https://<host>:5281/push_appserver/v1/push
    POST data: node=<device uuid>&secret=<secret obtained on register>
    function: send push notification to device
    result: text document separated by \n

    • first line: OK, everything else (including ERROR) is specified as error condition
      if ok: 2nd line: XEP-0357 push node
      if error: 2nd and subsequent lines: error description
  • GET to http://<host>:5280/push_appserver/v1/settings or https://<host>:5281/push_appserver/v1/settings
    function: get list of registered device UUIDs
    result: html site listing all registered device UUIDS as links

  • GET to http://<host>:5280/push_appserver/v1/settings/<device uuid> or https://<host>:5281/push_appserver/v1/settings/<device uuid>
    function: get internal data saved for this device UUID
    result: HTML site listing all data (serialized Lua table using penlight's pl.pretty)

Implementation notes

mod_push_appserver and its submodules use events to communicate with each other. These events are documented here.

Interaction between mod_push_appserver and its submodules

mod_push_appserver triggers the event incoming-push-to-<push type> (currently only the types apns and fcm are supported). The event handler has to return true or an error description string for failed push attempts and false for successfull ones.
Returning nil will be handled as error!
The event data always includes the following keys:

  • origin
    Prosody session the stanza came from (typically an s2s session).
  • settings
    The registered push settings which are also available at the /push_appserver/v1/settings/<device uuid> HTTP endpoint in debug mode.
  • summary
    The push summary (see XEP-0357 for more information)
  • stanza
    The incoming push stanza (see XEP-0357 for more information).

Submodules (like mod_push_appserver_apns) can trigger the event unregister-push-token. The event data has to include the following keys:

  • token
    The push token to invalidate (note: this is not the secret obtained by registering the device, but the raw token obtained from APNS, FCM etc.).
  • type
    apns, fcm etc.
  • timestamp
    The timestamp of the delete request. mod_push_appserver won't unregister the token if it was re-registered after this timestamp.

Example of internal data

{
  type = "apns",
  token = "DEADBEEFABCDEF0123456DEADBEEF112DEADBEEFABCDEF0123456DEADBEEF112",
  last_push_error = "2017-03-18T04:07:44Z",
  last_successful_push = "2017-03-18T03:54:24Z",
  registered = "2017-03-17T02:10:21Z",
  renewed = "2017-03-18T02:54:51Z",
  node = "E0FF1D8C-EB96-4E10-A912-F68B03FD8D3E",
  secret = "384e51b4b2d5e4758e5dc342b22dea9217212f2c4886e2a3dcf16f3eb0eb3807"
}
You can’t perform that action at this time.