-
Notifications
You must be signed in to change notification settings - Fork 430
MSC4174: Web push #4174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
MSC4174: Web push #4174
Changes from all commits
24473e5
2eae5a3
0346332
201ddc9
2f71bf3
1f5b73c
e413f9e
faa3101
216cf01
6bc8165
a9f7e48
c9d0b79
5ebd199
706e440
92ae561
1452aa8
8473849
55aeaf5
9b8ca9d
75b73c4
3874fe0
4b7642b
1d7dd7a
373dbc0
df81404
aee6877
9b41d6d
52ba762
9ee94eb
39e3687
3a37cec
8b376de
d2485a2
2561254
7f1849e
f3f96be
5bab3c4
83dc378
6b70438
f59cb97
bd30e72
246db23
8a67d56
becdd6e
5217c69
a3850f7
6d59575
7a00947
a31ae62
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| # MSC4174: Web push | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [non-blocking] This MSC refers to the feature inconsistently as "WebPush" or "Web Push" (with or without the space), but it's hard to fault this MSC for that, since the RFCs do the same thing. However, when the spec gets written, we should pick one and stick with it. |
||
|
|
||
| This MSC supersedes and replaces [MSC3013](https://github.com/matrix-org/matrix-spec-proposals/pull/3013), which introduced push notification encryption first. | ||
|
Member
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that we only want to use the encryption defined in here for WebPush (and only because it isn't easy to replace the encryption in WebPush with something else). If we want to encrypt push notifictions over other systems, we should use a different mechanism, which will probably be something different from what 3013 defines. So 3013 will be probably replaced, but not by this MSC. Basically, this first sentence should be removed.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cf. #3013 (comment) |
||
|
|
||
| Push notifications typically go through third-party push providers in order to be delivered: 1) a push gateway (sygnal) and | ||
| 2) e.g. FCM (Google) or APNs (Apple). In order to prevent these push providers and | ||
| push gateways from being able to read any sensitive information, the `event_id_only` format was introduced, where the push content only contains the `event_id` and `room_id` of an event. After receiving the push message the client uses | ||
| `GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}` to fetch the full event itself, and creates the notification based | ||
| on that. | ||
|
|
||
| Leaking the room and event id to third parties is problematic and can be avoided. | ||
|
|
||
|
|
||
| Web Push is a standard for (E2EE) push notifications, defined by 3 RFCs: | ||
| - [RFC8030](https://www.rfc-editor.org/rfc/rfc8030) defines the application server to push server communications | ||
| - [RFC8291](https://www.rfc-editor.org/rfc/rfc8291) defines the encryption: | ||
| - the subscribing client (user agent in RFC8030) generates a P-256 key pair and the *auth* secret, and sends the P-256 public key and the *auth* secret to the homeserver during registration | ||
| - the homeserver encrypts outgoing push notifications with the client keys | ||
| - the notifications are then decrypted by the subscribing client | ||
| - [RFC8292](https://www.rfc-editor.org/rfc/rfc8292), VAPID: defines the authorization: | ||
| - it is supposed to be *Voluntary* (optional) but many big push servers require it | ||
| - the homeserver generates a single P-256 key pair, and any client can request the public key | ||
| - the client passes the VAPID public key while requesting a new registration to their push server | ||
p1gp1g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - the push server is then able to allow only requests which have an authorization header signed with the VAPID key | ||
| - the homeserver adds the authorization header to all push notifications | ||
|
|
||
| Many libraries are already available and robust: they are reviewed, and acknowledged by experts. | ||
|
|
||
| Adding a `kind` of `webpush` to [`POST /_matrix/client/v3/pushers/set`](https://spec.matrix.org/v1.16/client-server-api/#post_matrixclientv3pushersset) would allow the following types of client to to receive encrypted push notifications without the need for an external gateway: | ||
| - Web apps and desktop apps | ||
| - Android apps using [UnifiedPush](https://codeberg.org/UnifiedPush/specifications/src/branch/main/specifications/android.md#resources). This MSC would make [MSC2970](https://github.com/matrix-org/matrix-spec-proposals/pull/2970) redundant. | ||
| - Android apps using FCM ([It is possible to push to FCM with Web Push standard](https://unifiedpush.org/news/20250131_push_for_decentralized/)) | ||
| - Maybe others? For example, Apple are starting to move starting to move towards web push [support](https://developer.apple.com/documentation/usernotifications/sending-web-push-notifications-in-web-apps-and-browsers). | ||
| - iOS apps would still require some sort "gateway" (to transform Web Push push notifications to a format that the Apple Push Notification Service (APNS) will accept). But such a gateway is trivial compared to what matrix push gateways implement today. Mastodon's [webpush-apn-relay](https://github.com/mastodon/webpush-apn-relay) is one such example. Notification content remains encrypted between the homeserver all the way to the client. | ||
|
|
||
| ## Proposal | ||
p1gp1g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| The MSC introduces a new pusher `kind`, for use with [`/pushers/set`](https://spec.matrix.org/v1.17/client-server-api/#post_matrixclientv3pushersset): `webpush`. | ||
|
|
||
| [`PusherData`](https://spec.matrix.org/v1.17/client-server-api/#post_matrixclientv3pushersset_request_pusherdata) is extended as follows: | ||
| - `url`: is updated to be required if `kind` is `http`, or if `kind` is `webpush`. If `kind` is `webpush`, this is the URL defined as a `push resource` by RFC8030, and MUST be an HTTPS URL. | ||
| - `auth`: is introduced, required if `kind` is `webpush`, not used otherwise. This holds the authentication secret as | ||
| specified by [RFC8291 section 3.2](https://www.rfc-editor.org/rfc/rfc8291#section-3.2) - 16 random bytes encoded in URL-safe Base64 without padding. | ||
|
|
||
| `PusherData` also gets new field, `activated`, a boolean, that must not be set by the client, and the server must add to the responses that include the data. During a first registration, or during a re-registration where the Pusher `pushkey`, `PusherData.url` or `PusherData.auth` is updated, `activated` is set to false until the pusher is activated with the request to | ||
| `/_matrix/client/v3/pushers/ack` (cf. below). Re-subscribing an existing pusher, with the same `pushkey`, `PusherData.url` and `PusherData.auth` doesn't change its value. | ||
|
|
||
| The homeserver doesn't send any notification - except the one to validate the pusher - to the pusher, until the Pusher is activated. | ||
|
|
||
| The POST request to the endpoint dedicated to the creation, modification and deletion of pushers, | ||
| `POST /_matrix/client/v3/pushers/set` now supports a new `kind`: `webpush`. | ||
| - `kind`: is updated to introduce `webpush` which makes a | ||
| pusher that sends Web Push encrypted messages. | ||
| - `pushkey`: is updated, if the `kind` is `webpush`, this is the user agent public key. | ||
| Per [RFC8291 section 3.1](https://www.rfc-editor.org/rfc/rfc8291#section-3.1), this is the public part of a | ||
| P-256 keypair, converted to bytes using the uncompressed form ([SEC 1](https://www.secg.org/sec1-v2.pdf), | ||
| section 2.3.3, replicated from X9.62), and encoded in URL-safe Base64 without padding. | ||
|
|
||
| If the request creates a new pusher or modifies values under `pushkey` , `PusherData.url`, or `PusherData.auth`, then | ||
| the server MUST respond with 201, "The pusher is set but needs to be activated". The server MUST then send a push notification to the | ||
| url, encrypted with `pushKey` and `PusherData.auth`, authenticated with the VAPID key, with a message containing | ||
| `app_id` and `ack_token`. `ack_token` MUST be a unique identifier conforming to [the opaque identifier grammar](https://spec.matrix.org/v1.17/appendices/#opaque-identifiers). | ||
| To ensure sufficient entropy is used, it is recommended to use a UUIDv4 token in hyphen form. | ||
|
|
||
| The server must expire the `ack_token` after 5 minutes. In this case, the registration remains inactivated until the client tries to register again, then the homeserver sends a new `ack_token`. | ||
| Example of a push notification containing a validation token: | ||
|
|
||
p1gp1g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ``` | ||
| { | ||
| "app_id": "im.vector.app.android", | ||
| "ack_token": "6fc76b70-5fad-4eb7-93ea-a1af7a03258b" | ||
| } | ||
| ``` | ||
|
|
||
p1gp1g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| A new endpoint is introduced, dedicated to pusher validation. This is called by the matrix client to validate the pusher once it has received the `ack_token` from the validation push message: | ||
| - POST `/_matrix/client/v3/pushers/ack` | ||
| - Rate limited: No, Requires authentication: Yes | ||
dbkr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - The request body contains the `app_id` and `ack_token` parameters, received with the push notification. | ||
| - The response contains the following HTTP code and error code: | ||
| - 404, M_NOT_FOUND: if no pusher with this app_id exists | ||
| - 410, M_EXPIRED_ACTIVATION_TOKEN: if this token for this app_id is expired | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not convinced 410 is appropriate here. I'd just use 400 if there is no particular reason for 410. |
||
| - 400, M_UNKNOWN_ACTIVATION_TOKEN: if a pusher with this app_id exists, but the token is not known. An expired token may send this status too | ||
| - 200: if the pusher has been activated. The response body does not contain any parameters. | ||
|
|
||
| M_EXPIRED_ACTIVATION_TOKEN and M_UNKNOWN_ACTIVATION_TOKEN are new error codes. | ||
|
|
||
| Note: As specified by RFC8030, the homeserver deletes the registration if it receives a 404, 410 or 403 from the push server on push. | ||
|
|
||
| As many popular push servers require VAPID (Voluntary Application Server Identification, cf RFC8292), a server supporting this MSC MUST add a `m.webpush` capability to the `/capabilities` endpoint with this format: | ||
|
|
||
| The VAPID public key is in the uncompressed form, in URL-safe Base64 without padding. | ||
|
|
||
| ``` | ||
| "m.webpush": { | ||
| "enabled": true, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is |
||
| "vapid": "BNbXV88MfMI0fSxB7cDngopoviZRTbxIS0qSS-O7BZCtG04khMOn-PP2ueb_X7Aeci42n02kJ0-JJJ0uQ4ELRTs" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I slightly feel that it might be more appropriate to expose this on a separate endpoint, but it's not a strong opinion. |
||
| } | ||
| ``` | ||
|
|
||
| A client that supports this kind of pusher should use it if the server supports it too, and | ||
| not register another `http` pusher to avoid duplicate pushes. | ||
|
|
||
| ## Overview with webpush | ||
p1gp1g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| The current overview is here: <https://spec.matrix.org/v1.17/push-gateway-api/#overview> | ||
|
|
||
| It becomes: | ||
|
|
||
| ``` | ||
| +-------------------+ | ||
| Matrix HTTP | | | ||
| Notification Protocol | Device Vendor | | ||
| | | | ||
| +-------------------+ | +---------------+ | | ||
| | | | | | | | ||
| | Matrix homeserver +--> Web Push +----> Push Server | | | ||
| | | | | | | | ||
| +-^-----------------+ | +----+----------+ | | ||
| | | | | | ||
| Matrix | | | | | ||
| Client/Server API + | | | | ||
| | | +-------------------+ | ||
| | +--+-+ | | ||
| | | <--------------------------------+ | ||
| +---+ | | ||
| | | Provider Push Protocol | ||
| +----+ | ||
|
|
||
| Mobile Device or Client | ||
| ``` | ||
|
|
||
| ## Potential issues | ||
|
|
||
| Many libraries only implement [I-D.ietf-webpush-encryption-04](https://datatracker.ietf.org/doc/html/draft-ietf-webpush-encryption-04) from October 2016, rather than the final version of [RFC8291](https://datatracker.ietf.org/doc/html/rfc8291) from November 2017. Thus, some care needs to be taken during implementation. Checking the | ||
| Content-Encoding header is a good way to check for the correct version. If the value is `aes128gcm`, then it uses | ||
| the right specifications, in case of `aesgcm` it uses the draft version. | ||
p1gp1g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| The legacy version of web push (draft RFC8291 and draft RFC8292) MUST NOT be implemented. | ||
|
|
||
| ## Alternatives | ||
|
|
||
| `pushkey` could be a random ID, and we can add `p256dh` in the `PusherData`. But it would require client to store it, | ||
| while the public key already identifies that pusher. And the client already uses the PusherData that way. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't understand what this means.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hydrogen already uses pushkey for the p-256 pubkey |
||
|
|
||
| `vapid` parameter could be made optional considering it is officially not a requirement, however it seems | ||
| existing push servers from big players need it anyway to be able to subscribe, so it was decided to make it mandatory | ||
| to avoid issues with those. | ||
|
|
||
| ## Security considerations | ||
|
|
||
| Security considerations are listed by [RFC8030](https://www.rfc-editor.org/rfc/rfc8030#section-8), they are mainly resolved with [RFC8291](https://datatracker.ietf.org/doc/html/rfc8291) (Encryption) and | ||
| [RFC8292](https://datatracker.ietf.org/doc/html/rfc8292) (VAPID). | ||
|
|
||
| Like federation requests, there is a risk of SSRF. This risk is limited since the post data isn't | ||
| arbitrary (the content is encrypted), and a potential malicious actor doesn't have access to the response. | ||
| Nevertheless, it is recommended to not post to arbitrary private addresses but offer the option to | ||
| safelist a private IP. (Synapse already implements [`ip_range_whitelist`](https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#ip_range_whitelist)) | ||
| It is also recommended to not follow redirection, to avoid implementation issue where the destination is checked | ||
| before sending the request but not for redirection. | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does the sample implementation follow these recommendations? Do they work ok in practice (especially not following redirects)?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @MatMaul may answer that question |
||
| Like any other federation request, there is a risk of DOS amplification. One malicious actor can register many users | ||
| to a valid endpoint, then change the DNS record and target another server, then notify all these users. This | ||
| amplification is very limited since HTTPS is required and the TLS certificate of the target will be rejected. The | ||
dbkr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| request won't reach any functionality of the targeted application. The homeserver can reject pusher if the response | ||
| code is not one intended. | ||
|
|
||
| ## Unstable prefix | ||
|
|
||
| - Until this proposal is considered stable, implementations must use | ||
| `org.matrix.msc4174.webpush` instead of `m.webpush`. | ||
|
Comment on lines
+169
to
+170
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for the
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @MatMaul any opinion ? |
||
|
|
||
| ## Dependencies | ||
|
|
||
| - | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implementation requirements:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@turt2live These requirements are now met
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Client (Hydrogen) and Server (Synapse) implementations noted at #4174 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This MSC has changed since the implementations were made. I'm not sure what all the changes were. If they are primarily minor changes such as naming changes (e.g. renaming properties), or changes of that sort, then the implementations should still be fine as a demonstration that the MSC works. If there are any substantial changes, then the implementations may need to be updated.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The homeserver implementation for Synapse is actively being updated in element-hq/synapse#17987. It has yet to be merged.
The client implementation for Hydrogen has not been updated since Dec 2nd, 2024: element-hq/hydrogen-web#1201. The changes to the MSC since Dec 2nd, 2024 are https://github.com/matrix-org/matrix-spec-proposals/pull/4174/files/0346332a1f802425b579d2b4cdf98fe8a462d48b..f59cb97551216812d1ca492fafba05d057cc1298.
I've left a review on the Hydrogen PR with what appears to be missing from a cursory glance. As such, I think we're still missing an up-to-date client implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mscbot concern The client implementation has grown stale with respect to the MSC. See this PR review for the outstanding changes.