Skip to content
This repository was archived by the owner on Nov 6, 2022. It is now read-only.

Commit 06823f5

Browse files
Deactivate repo
1 parent 1c6472e commit 06823f5

File tree

2 files changed

+390
-387
lines changed

2 files changed

+390
-387
lines changed

README.md

+1-387
Original file line numberDiff line numberDiff line change
@@ -1,389 +1,3 @@
11
# mod\_push\_appserver
22

3-
Simple and extendable app server for XMPP push notifications as defined in
4-
[XEP-0357][1].
5-
6-
The app server is implemented as a module for the [Prosody][2] XMPP server.
7-
8-
Currently, only push notifications to Apple's [APNS][3] and Google's [FCM][4]
9-
are implemented, but other push services can easily be added in a separate
10-
module.
11-
12-
## Requirements
13-
14-
- Prosody trunk/0.12 or later.
15-
- Lua 5.1, 5.2 or 5.3
16-
- Installed luasec Lua library version 0.5 (Debian package: `lua-sec`) or higher.
17-
- Installed cqueues Lua library (Debian package: `lua-cqueues`).
18-
- Installed lua-http Lua library (Debian package: `lua-http`).
19-
20-
## Installation
21-
22-
Just check out the repository somewhere and point prosody at this directory
23-
using `plugin_paths` in its main config file.
24-
For example: `plugin_paths = { "/usr/local/lib/mod_push_appserver" }`.
25-
26-
Then add the submodule you need (for example `mod_push_appserver_apns`
27-
or `mod_push_appserver_fcm`) to the enabled modules of a specific virtual host or component.
28-
29-
See this configuration example for [APNS][3] if you want to load the needed submodule as component:
30-
```
31-
Component "push.example.org" "push_appserver_apns"
32-
push_appserver_debugging = false
33-
push_appserver_apns_sandbox = false
34-
push_appserver_apns_collapse_pushes = false
35-
push_appserver_apns_push_priority = "auto"
36-
push_appserver_apns_mutable_content = true
37-
push_appserver_apns_cert = "/etc/prosody/apns_normal.crt"
38-
push_appserver_apns_key = "/etc/prosody/apns_normal.key"
39-
push_appserver_apns_topic = "my.app.bundle.id"
40-
41-
Component "voip-push.example.org" "push_appserver_apns"
42-
push_appserver_debugging = false
43-
push_appserver_apns_sandbox = false
44-
push_appserver_apns_collapse_pushes = false
45-
push_appserver_apns_push_priority = "voip"
46-
push_appserver_apns_cert = "/etc/prosody/apns_voip.crt"
47-
push_appserver_apns_key = "/etc/prosody/apns_voip.key"
48-
push_appserver_apns_topic = "my.app.bundle.id"
49-
```
50-
51-
If you want to have only one component host supporting APNS *and* FCM you can use the following:
52-
```
53-
Component "push.example.org" "push_appserver"
54-
modules_enabled = {
55-
"push_appserver_fcm";
56-
"push_appserver_apns";
57-
}
58-
push_appserver_debugging = false
59-
push_appserver_fcm_key = "someFCMkey"
60-
push_appserver_apns_sandbox = false
61-
push_appserver_apns_collapse_pushes = false
62-
push_appserver_apns_push_priority = "voip"
63-
push_appserver_apns_cert = "/etc/prosody/apns_voip1.crt"
64-
push_appserver_apns_key = "/etc/prosody/apns_voip1.key"
65-
push_appserver_apns_topic = "my.app.bundle.id"
66-
```
67-
68-
Or only use FCM:
69-
```
70-
Component "fcm-push.example.org" "push_appserver_fcm"
71-
push_appserver_debugging = false
72-
push_appserver_fcm_key = "myFCMkey"
73-
```
74-
75-
## Usage notes (configuration)
76-
77-
For chat apps using VoIP pushes to APNS, the priority should be set to `high`.
78-
The alert text can be ignored in this case (if you only want to wakeup your
79-
device). For normal push notifications, the priorities `high` and `silent` are
80-
supported. The configured alert text (`push_appserver_apns_push_alert`) is
81-
ignored for `silent` pushes.
82-
83-
For pushes to FCM the priorities `high` and `normal` are supported with `normal`
84-
priorities being delayed while the device is in doze mode.
85-
Pushes having priority `high` are always delivered, even in doze mode, thus
86-
should be used for chat apps.
87-
88-
### Configuration options (mod\_push\_appserver)
89-
90-
- **push\_appserver\_debugging** *(boolean)*
91-
Make `/push_appserver/v1/settings` HTTP endpoint available. Default: `false`.
92-
This setting will also make http forms available at all `POST` HTTP endpoints
93-
for easier manual testing of your setup by simply using your browser of choice.
94-
- **push\_appserver\_rate\_limit** *(number)*
95-
Allow only one request everey N seconds. Default: `5`.
96-
The throttle will always space out incoming requests by this timeframe and make sure
97-
that the last request of a burst will always be handled at the beginning of the next
98-
timeframe (the first request of a burst will be handled immediately, all other requests
99-
in between the first and the last one will be ignored completely).
100-
This should mitigate some DOS attacks.
101-
- **push\_appserver\_store\_plugin** *(string)*
102-
Set this to the store plugin to use, currently available: `cached`, `uncached`.
103-
Use the `uncached` store to deactivate local caching of device tokens and node settings.
104-
This is useful if you are using an SQL storage backend and this backend is
105-
replicated across several servers in an HA- or loadbalancing-setup.
106-
Default: `cached`.
107-
- **push\_appserver\_store\_params** *(table)*
108-
Set this to anything you want to pass to the storage plugin.
109-
The two default implementations `cached`, `uncached` don't take any params.
110-
Default: `nil`.
111-
112-
### Configuration options (mod\_push\_appserver\_apns)
113-
114-
- **push\_appserver\_apns\_cert** *(string)*
115-
Path to your APNS push certificate in PEM format.
116-
- **push\_appserver\_apns\_key** *(string)*
117-
Path to your APNS push certificate key in PEM format.
118-
- **push\_appserver\_apns\_topic** *(string)*
119-
The APNS push topic to use (should be your app's bundle id).
120-
- **push\_appserver\_apns\_capath** *(string)*
121-
Path to CA certificates directory. Default: `"/etc/ssl/certs"` (Debian and
122-
Ubuntu use this path for the system CA store).
123-
- **push\_appserver\_apns\_ciphers** *(string)*
124-
Ciphers to use when establishing a tls connection. Default:
125-
`ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256`
126-
- **push\_appserver\_apns\_sandbox** *(boolean)*
127-
Use apns sandbox api endpoint if `true`, production endpoint otherwise.
128-
Default: `true`.
129-
- **push\_appserver\_apns\_collapse\_pushes** *(boolean)*
130-
Instruct Apple to collapse queued pushes into one single push (`true`), or keep them separated (`false`).
131-
Setting this to false allows to get more background time for your app when sending multiple pushes in a row
132-
(this can be subject to implementation changes by Apple, though).
133-
Default: `false`.
134-
- **push\_appserver\_apns\_mutable\_content** *(boolean)*
135-
Mark high prio pushes as mutable content (only has a meaningful effect if `push_appserver_apns_push_priority` is set to
136-
`"high"` or `"auto"`). Default: `true`.
137-
- **push\_appserver\_apns\_push\_ttl** *(number)*
138-
TTL for push notification in seconds. Default: `4*7*24*3600` (that means 4 weeks from now).
139-
- **push\_appserver\_apns\_push\_priority** *(string)*
140-
Value `"high"` for high priority pushes always triggering a visual indication on the user's phone,
141-
`"silent"` for silent pushes that can be delayed or not delivered at all but don't trigger
142-
a visual indication, `voip` for voip pushes and `"auto"` to let the appserver automatically decide between `"high"` and `"silent"`
143-
based on the presence of `"last-message-body"` in the push summary received from the XMPP server. Default: `"auto"`.
144-
**NOTE 1 (iOS >= 13):** Apple decided for iOS >= 13 to not allow silent voip pushes anymore. Use `"high"` or `"auto"` on
145-
this systems and set `push_appserver_apns_mutable_content` to `true`. Then use a `Notification Service Extension` in your app
146-
to log in into your XMPP account in the background, retrieve the acutal stanzas and replace the notification with a useful
147-
one before the dummy notification sent by this appserver hits the screen.
148-
**NOTE 2 (iOS >= 10 and < 13):** if you have VoIP capabilities in your app `"silent"` pushes will become reliable and always
149-
wake up your app without triggering any visual indications on the user's phone.
150-
In VoIP mode your app can decide all by itself if it wants to show a notification to the user or not
151-
by simply logging into the XMPP account in the backround and retrieving the stanzas that triggered the push.
152-
*You have to use `voip` if you want to send voip pushes (all iOS versions).*
153-
154-
### Configuration options (mod\_push\_appserver\_fcm)
155-
156-
- **push\_appserver\_fcm\_key** *(string)*
157-
Your FCM push credentials (can be found in FCM dashboard under Settings --> Cloud Messaging --> Server key).
158-
- **push\_appserver\_fcm\_capath** *(string)*
159-
Path to CA certificates directory. Default: `"/etc/ssl/certs"` (Debian and
160-
Ubuntu use this path for the system CA store).
161-
- **push\_appserver\_fcm\_ciphers** *(string)*
162-
Ciphers to use when establishing a tls connection. Default:
163-
`ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256`
164-
- **push\_appserver\_fcm\_push\_ttl** *(number)*
165-
TTL for push notification in seconds, can be 4 weeks at max.
166-
Default: `nil` (that means 4 weeks).
167-
- **push\_appserver\_fcm\_push\_priority** *(string)*
168-
Value `"high"` for high priority pushes that wake up the device even when
169-
in doze mode or `"normal"` for normal pushes that can be delayed.
170-
Default: `"high"`.
171-
172-
## API (register device etc.)
173-
174-
This appserver implements [XEP-0357][1] commands for sending actual push notifications.
175-
Additionally [XEP-0357][1] requires a device to be registered on the appserver but does
176-
not dictate how this should be done.
177-
178-
**Therefore this appserver provides two different APIs to register a (new) device on the appserver.**
179-
**Just use the one more convenient to you.**
180-
181-
### [XEP-0050][6] Ad-Hoc Command API
182-
183-
This API resembles more or less what Conversations is doing,
184-
but with some small differences:
185-
- the command is named "v1-register-push" instead of "register-push-gcm"
186-
- the device id field is called "node" instead of "device-id"
187-
- the new field "type" was added. This can be used to specify the push
188-
type just as it is done using the http based API.
189-
- unregistering a device is not done at all in Conversations, but this appserver supports it via the v1-unregister-push command
190-
191-
[This Gist][5] demonstrates the changes needed to Conversations to use this appserver
192-
instead of inputmice's p2.
193-
See [XEP-0050][6] for more info regarding Ad-Hoc Commands in general.
194-
195-
**Keep in mind that the registration/unregistration commands sent to this appserver are routed through the user's xmpp server.**
196-
**This exposes the raw APNS/FCM push token and device id to the user's xmpp server.**
197-
**Use the HTTP API if you don't like this (HTTP will expose the user's IP, though).**
198-
199-
#### Example XMPP flow for registering a device:
200-
```
201-
<iq to="push.example.org" id="MyID-6465" type="set">
202-
<command xmlns="http://jabber.org/protocol/commands" node="v1-register-push" action="execute">
203-
<x xmlns="jabber:x:data" type="submit">
204-
<field type='hidden' var='FORM_TYPE'>
205-
<value>https://github.com/tmolitor-stud-tu/mod_push_appserver/#v1-register-push</value>
206-
</field>
207-
<field var="type">
208-
<value>fcm</value>
209-
</field>
210-
<field var="node">
211-
<value>static device id like ANDROID_ID OR some stable iOS id</value>
212-
</field>
213-
<field var="token">
214-
<value>dynamic token obtained from FirebaseInstanceId_InstanceId OR apns token</value>
215-
</field>
216-
</x>
217-
</command>
218-
</iq>
219-
220-
<iq to="[email protected]/res1" from="push.example.org" type="result" id="MyID-6465">
221-
<command xmlns="http://jabber.org/protocol/commands" status="complete" node="v1-register-push" sessionid="1559985918910">
222-
<x xmlns="jabber:x:data" type="form">
223-
<field type="jid-single" var="jid">
224-
<value>push.example.org</value>
225-
</field>
226-
<field type="text-single" var="node">
227-
<value>echoed back static device id</value>
228-
</field>
229-
<field type="text-single" var="secret">
230-
<value>some arbitrary hash-like value</value>
231-
</field>
232-
</x>
233-
</command>
234-
</iq>
235-
```
236-
237-
The two values `node` and `secret` are needed for registering push on the XMPP server afterwards,
238-
see [example 9 in XEP-0357, section 5][7].
239-
240-
#### Example XMPP flow for UNregistering a device:
241-
```
242-
<iq to="push.example.org" id="MyID-6446" type="set">
243-
<command xmlns="http://jabber.org/protocol/commands" node="v1-unregister-push" action="execute">
244-
<x xmlns="jabber:x:data" type="submit">
245-
<field type='hidden' var='FORM_TYPE'>
246-
<value>https://github.com/tmolitor-stud-tu/mod_push_appserver/#v1-unregister-push</value>
247-
</field>
248-
<field var="type">
249-
<value>fcm</value>
250-
</field>
251-
<field var="node">
252-
<value>static device id like ANDROID_ID OR some stable iOS id</value>
253-
</field>
254-
</x>
255-
</command>
256-
</iq>
257-
258-
<iq to="[email protected]/res1" from="push.example.org" type="result" id="MyID-6446">
259-
<command xmlns="http://jabber.org/protocol/commands" status="complete" node="v1-unregister-push" sessionid="1559985918910">
260-
<x xmlns="jabber:x:data" type="form">
261-
<field type="jid-single" var="jid">
262-
<value>push.example.org</value>
263-
</field>
264-
<field type="text-single" var="node">
265-
<value>echoed back static device id</value>
266-
</field>
267-
</x>
268-
</command>
269-
</iq>
270-
```
271-
272-
### HTTP API
273-
274-
All `POST` endpoints can be used via `GET` to get back a simple html page which
275-
allows you to manually test the endpoint behaviour in your browser, if the config
276-
option `push_appserver_debugging` is set to true (an error is returned otherwise).
277-
*This config option should be false in production environments!*
278-
279-
- POST to `http://<host>:5280/push_appserver/v1/register` or
280-
`https://<host>:5281/push_appserver/v1/register`
281-
POST data: `type=<push type>&node=<device uuid>&token=<apns/fcm/etc. push token>`
282-
function: register device for push
283-
result: text document separated by `\n`
284-
- first line: `OK`, everything else (including `ERROR`) is specified as error
285-
condition
286-
if ok: 2nd line: XEP-0357 push `node`, 3rd line: XEP-0357 push `secret`
287-
if error: 2nd and subsequent lines: error description
288-
289-
- POST to `http://<host>:5280/push_appserver/v1/unregister` or
290-
`https://<host>:5281/push_appserver/v1/unregister`
291-
POST data: `type=<push type>&node=<device uuid>`
292-
function: unregister device
293-
result: text document separated by `\n`
294-
- first line: `OK`, everything else (including `ERROR`) is specified as error
295-
condition
296-
if ok: 2nd line: XEP-0357 push `node`, 3rd line: XEP-0357 push `secret`
297-
if error: 2nd and subsequent lines: error description
298-
299-
- POST to `http://<host>:5280/push_appserver/v1/push` or
300-
`https://<host>:5281/push_appserver/v1/push`
301-
POST data: `node=<device uuid>&secret=<secret obtained on register>`
302-
function: send push notification to device
303-
result: text document separated by `\n`
304-
- first line: `OK`, everything else (including `ERROR`) is specified as error
305-
condition
306-
if ok: 2nd line: XEP-0357 push `node`
307-
if error: 2nd and subsequent lines: error description
308-
309-
- GET to `http://<host>:5280/push_appserver/v1/settings` or
310-
`https://<host>:5281/push_appserver/v1/settings`
311-
function: get list of registered device UUIDs
312-
result: html site listing all registered device UUIDS as links
313-
314-
- GET to `http://<host>:5280/push_appserver/v1/settings/<device uuid>` or
315-
`https://<host>:5281/push_appserver/v1/settings/<device uuid>`
316-
function: get internal data saved for this device UUID
317-
result: HTML site listing all data (serialized Lua table using penlight's
318-
`pl.pretty`)
319-
320-
- GET to `http://<host>:5280/push_appserver/v1/health` or
321-
`https://<host>:5281/push_appserver/v1/health`
322-
function: get health status of module
323-
result: html site containing the word `RUNNING` if the module is loaded properly
324-
(this GET-node is accessible even when `push_appserver_debugging` is set to `false`)
325-
326-
## Implementation notes
327-
328-
mod\_push\_appserver and its submodules use events to communicate with each
329-
other. These events are documented here.
330-
331-
### Interaction between mod\_push\_appserver and its submodules
332-
333-
mod\_push\_appserver triggers the event `incoming-push-to-<push type>`
334-
(currently only the types `apns` and `fcm` are supported).
335-
The event handler has to return `true` or an error description string
336-
for failed push attempts and `false` for successfull ones.
337-
Returning `nil` will be handled as error!
338-
The event data always includes the following keys:
339-
340-
- **origin**
341-
Prosody session the stanza came from (typically an s2s session).
342-
- **settings**
343-
The registered push settings which are also available at the
344-
`/push_appserver/v1/settings/<device uuid>` HTTP endpoint in debug mode.
345-
- **summary**
346-
The push summary (see [XEP-0357][1] for more information)
347-
- **stanza**
348-
The incoming push stanza (see [XEP-0357][1] for more information).
349-
350-
Submodules (like mod\_push\_appserver\_apns) can trigger the event
351-
`unregister-push-token`. The event data has to include the following keys:
352-
353-
- **token**
354-
The push token to invalidate (note: this is not the secret obtained by
355-
registering the device, but the raw token obtained from APNS, FCM etc.).
356-
- **type**
357-
`apns`, `fcm` etc.
358-
- **timestamp**
359-
The timestamp of the delete request. mod\_push\_appserver won't unregister the
360-
token if it was re-registered after this timestamp.
361-
362-
### Example of internal data
363-
364-
```lua
365-
{
366-
type = "apns",
367-
token = "DEADBEEFABCDEF0123456DEADBEEF112DEADBEEFABCDEF0123456DEADBEEF112",
368-
last_push_error = "2017-03-18T04:07:44Z",
369-
last_successful_push = "2017-03-18T03:54:24Z",
370-
registered = "2017-03-17T02:10:21Z",
371-
renewed = "2017-03-18T02:54:51Z",
372-
node = "E0FF1D8C-EB96-4E10-A912-F68B03FD8D3E",
373-
secret = "384e51b4b2d5e4758e5dc342b22dea9217212f2c4886e2a3dcf16f3eb0eb3807"
374-
}
375-
```
376-
377-
## Used By (incomplete list)
378-
- [Monal (iOS)][8]
379-
- [yaxim (Android)][9]
380-
381-
[1]: https://xmpp.org/extensions/xep-0357.html
382-
[2]: https://prosody.im/
383-
[3]: https://developer.apple.com/go/?id=push-notifications
384-
[4]: https://firebase.google.com/docs/cloud-messaging/
385-
[5]: https://gist.github.com/tmolitor-stud-tu/a1e877a7d75c07c2163c3ce1e0347881
386-
[6]: https://xmpp.org/extensions/xep-0050.html
387-
[7]: https://xmpp.org/extensions/xep-0357.html#example-9
388-
[8]: https://monal.im/
389-
[9]: https://yaxim.org/
3+
This project is not maintained anymore, use the follow-up project over here: https://github.com/monal-im/fpush

0 commit comments

Comments
 (0)