diff --git a/proposals/4140-delayed-events-futures.md b/proposals/4140-delayed-events-futures.md index 559727bde..fcede0a88 100644 --- a/proposals/4140-delayed-events-futures.md +++ b/proposals/4140-delayed-events-futures.md @@ -83,6 +83,8 @@ Content-Type: application/json ``` The `delay_id` is an [opaque identifier](https://spec.matrix.org/v1.16/appendices/#opaque-identifiers) generated by the server. +It MUST be globally unique and SHOULD be as difficult to guess/craft as an access token, so that knowledge of a `delay_id` may +securely grant control of it via [the management endpoint](#managing-delayed-events). The server MAY round the delay up to a maximum of 30 seconds away from the request. @@ -120,29 +122,30 @@ Content-Type: application/json ### Managing delayed events -A new authenticated client-server API endpoint at `POST /_matrix/client/v1/delayed_events/{delay_id}` allows scheduled events +A set of new unauthenticated client-server API endpoints at `POST /_matrix/client/v1/delayed_events/{delay_id}/[send|cancel|restart]` allows scheduled events to be managed. -The body of the request is a JSON object containing the following fields: +The final path component of these endpoints specifies the action to take on the delayed event: -- `action` - The action to take on the delayed event.\ -Must be one of: - - `send` - Send the delayed event immediately instead of waiting for the delay time to be reached. - - `cancel` - Cancel the delayed event so that it is never sent. - - `restart` - Reset the send time to be `now + original_delay`. +- `send` - Send the delayed event immediately instead of waiting for the delay time to be reached. +- `cancel` - Cancel the delayed event so that it is never sent. +- `restart` - Reset the send time to be `now + original_delay`. For example, the following would send the delayed event with delay ID `1234567890` immediately: ```http -POST /_matrix/client/v1/delayed_events/1234567890 +POST /_matrix/client/v1/delayed_events/1234567890/send Content-Type: application/json { - "action": "send" } ``` -Where the `action` is `send`, the homeserver **should** apply rate limiting to provide mitigation against the +These endpoints are unauthenticated so that control over a particular delayed event may be +[delegated to an external service](#delegating-delayed-events) +by sharing the target delayed event's `delay_id` with the service. + +Where the action is `send`, the homeserver **should** apply rate limiting to provide mitigation against the [High Volume of Messages](https://spec.matrix.org/v1.16/appendices/#threat-high-volume-of-messages) threat. The server will respond with HTTP 404 and an `M_NOT_FOUND` error if the `delay_id` is not recognized or was already cancelled. @@ -159,6 +162,27 @@ that happened while sending the event (e.g. HTTP 403 and `M_FORBIDDEN` if the us The primary point of rate limiting is event sending when the delay times out or the event is sent using the `send` action. However, servers can choose to rate limit the management endpoints themselves as well if necessary. +#### Delegating delayed events + +It is useful for external services to also interact with delayed events. If a client disconnects, an external service can +be the best source to send the delayed event/"last will". + +To permit this, the `delay_id` that uniquely identifies a delayed event also behaves as a scoped access token +that only allows to interact with the `POST /delayed_events` endpoints on that specific `delay_id`. + +With this, an SFU that tracks the current client connection state could be given the power to control the delayed event. +The client would share the `delay_id` and the required details, so that the SFU can call the +`POST /delayed_events/{delay_id}/refresh` endpoint while a user is connected +and can call the `POST /delayed_events/{delay_id}/send` endpoint once the user disconnects. +This way, the SFU can be used as the source of truth for the call membership events without knowing anything about +the Matrix call. + +Since the SFU has a much lower chance of running into a network issue, +`POST /delayed_events/{delay_id}/restart` calls may be sent much more infrequently. +Instead of calling that endpoint every couple of seconds, a delayed event's +timeout can be set to be long (e.g. 6 hours), as the SFU can be expected to not forget sending the `POST /delayed_events/{delay_id}/send` requests +when it detects a disconnecting client. + ### Getting delayed events The new authenticated client-server API endpoint `GET /_matrix/client/v1/delayed_events` allows clients to get a list of @@ -391,11 +415,10 @@ the call membership "alive". For example it could make the request every 5 seconds (or some other period less than the `delay`): ```http -POST /_matrix/client/v1/delayed_events/1234567890 +POST /_matrix/client/v1/delayed_events/1234567890/restart Content-Type: application/json { - "action": "restart" } ``` @@ -462,28 +485,20 @@ it would make it transparent to clients, when an event was scheduled and when it ## Alternatives -### Delegating delayed events - -It is useful for external services to also interact with delayed events. If a client disconnects, an external service can -be the best source to send the delayed event/"last will". +### OAuth 2.0 scope for management endpoints -This is not covered in this MSC but could be realized with scoped access tokens. -A scoped token that only allows to interact with the `delayed_events` endpoint and only with a subset of `delay_id`s -would be used. +Instead of the [delayed event management endpoints](#managing-delayed-events) being unauthenticated +to permit [delegation to an external service](#delegating-delayed-events), +those endpoints could be given an OAuth 2.0 scope and be restricted to sessions that have requested it. +The scope would be within the existing `urn:matrix:client:api:*` scope, +so that access to the entirety the Client-Server API would include access to these endpoints as well. -With this, an SFU that tracks the current client connection state could be given the power to control the delayed event. -The client would share the scoped token and the required details, so that the SFU can call the -`refresh` endpoint while a user is connected -and can call the delayed event `send` request once the user disconnects -(using a `{"action": "restart"}` and a `{"action": "send"}` `/delayed_events` request.). -This way, the SFU can be used as the source of truth for the call membership events without knowing anything about -the Matrix call. +Using OAuth 2.0 to restrict access on these endpoints has many benefits over using a path parameter (`delay_id`) as an access token, +such as more fine-grained revocability on access to the endpoints, +and better identification of what entity is requesting these endpoints, which can be used to apply per-entity ratelimits. -Since the SFU has a much lower chance of running into a network issue, -`{"action": "restart"}` calls may be sent much more infrequently. -Instead of calling the `/delayed_events` endpoint every couple of seconds, a delayed event's -timeout can be set to be long (e.g. 6 hours), as the SFU can be expected to not forget sending the `{"action": "send"}` action -when it detects a disconnecting client. +The downsides of this approach are its requirement on the homeserver supporting the OAuth 2.0 login API, +and the additional network/configuration overhead for external services to request access to this scope. ### Batch sending @@ -539,7 +554,7 @@ an indicator to determine if the event is expired, instead of letting the SFU inform about the call termination or using the call app ping/refresh loop as proposed earlier in this MSC. The advantage is that this does not require introducing a new ping system -(as is proposed here by using the `delayed_events` restart action). +(as is proposed here by using the `delayed_events/{delay_id}/restart` action). Though with cryptographic identities, the client needs to create the leave event. The timeout for syncs are much slower than what would be desirable (30s vs 5s).