Merge 474b2fbc82 into e9f0f31d27
commit
abb31361cd
@ -0,0 +1,182 @@
|
||||
# MSC3846: Allowing widgets to access TURN servers
|
||||
|
||||
*The following is adapted from [MSC3819](https://github.com/matrix-org/matrix-spec-proposals/pull/3819).*
|
||||
|
||||
Widgets (embedded HTML applications in Matrix) currently have a relatively large surface area they can use for
|
||||
interacting with their attached client, primarily in the context of a room. They can send/receive
|
||||
[events with MSC2762](https://github.com/matrix-org/matrix-spec-proposals/pull/2762) and
|
||||
[to-device events with MSC3819](https://github.com/matrix-org/matrix-spec-proposals/pull/3819), allowing them to
|
||||
simulate an entire Matrix client in application code for specific, limited use cases.
|
||||
|
||||
This MSC forms part of a larger, ongoing, question about how to embed other Matrix clients into another client or room
|
||||
for access. An increasingly more popular client development option is to build out an entirely new Matrix client and
|
||||
want to embed that within another client (as a widget) to avoid the user needing to switch apps. To support this, we
|
||||
need to consider both long term and short term impact of the changes we propose. This MSC aims closer to the short term.
|
||||
|
||||
A longer term solution to the problem of clients wanting to be embedded in other clients might still be widgets, though
|
||||
with a system like [MSC3008](https://github.com/matrix-org/matrix-spec-proposals/pull/3008) to restrict access to the
|
||||
client-server API more effectively. For this MSC's purpose though, we're aiming to cover a specific functionality of the
|
||||
client-server API: providing access TURN servers.
|
||||
|
||||
While we could expose the entire client-server API over `postMessage` (or similar) for embedded clients to access, the
|
||||
permissions model gets hairy and difficult to secure on the client side. Instead, we're exploring what it would look
|
||||
like to special case what is needed for specific applications, as needed.
|
||||
|
||||
The specific goal of exposing TURN servers is to make it possible for widget-ized Matrix clients to implement
|
||||
[MSC3401 - Native group VoIP](https://github.com/matrix-org/matrix-spec-proposals/pull/3401), when combined with the
|
||||
abilities of [MSC2762](https://github.com/matrix-org/matrix-spec-proposals/pull/3819) and
|
||||
[MSC3819](https://github.com/matrix-org/matrix-spec-proposals/pull/3819).
|
||||
|
||||
## Proposal
|
||||
|
||||
To set up calls with minimal latency and support the full-mesh group call mode of
|
||||
[MSC3401](https://github.com/matrix-org/matrix-spec-proposals/pull/3401), widgets need to be able to continuously
|
||||
receive updates about the latest valid TURN server credentials, rather than requesting them once at start-up or
|
||||
on-demand. Otherwise, call setup with new participants may start failing midway through a call, or suffer the latency
|
||||
penalty of making a widget API call for every new participant.
|
||||
|
||||
To this end, we introduce a new capability, `m.turn_servers`, used for accessing TURN server credentials. When approved,
|
||||
it opens up access to the following actions:
|
||||
|
||||
**`fromWidget` action of `watch_turn_servers`**
|
||||
|
||||
```json5
|
||||
{
|
||||
// This is a standardized widget API request.
|
||||
"api": "fromWidget",
|
||||
"widgetId": "20220712_WidgetExample",
|
||||
"requestid": "generated-id-1234",
|
||||
"action": "watch_turn_servers", // value defined by this proposal
|
||||
"data": {} // nothing
|
||||
}
|
||||
```
|
||||
|
||||
Upon receipt, the client begins polling for TURN servers over the client-server API (if it wasn't already). It should
|
||||
queue an `update_turn_servers` action as soon as possible, and then continue to update the widget with new TURN server
|
||||
data whenever the previous data expires, using further `update_turn_servers` actions.
|
||||
|
||||
Both the `data` and `response` fields for this action are empty:
|
||||
|
||||
```json5
|
||||
{
|
||||
// This is a standardized widget API request.
|
||||
"api": "fromWidget",
|
||||
"widgetId": "20220712_WidgetExample",
|
||||
"requestid": "generated-id-1234",
|
||||
"action": "watch_turn_servers",
|
||||
"data": {},
|
||||
"response": {} // nothing
|
||||
}
|
||||
```
|
||||
|
||||
If the widget was already watching, this action has no effect. If the client is for whatever reason unable to start
|
||||
polling (for example if TURN access for the account is 403'd), the client sends back an error response.
|
||||
|
||||
**`fromWidget` action of `unwatch_turn_servers`**
|
||||
|
||||
```json5
|
||||
{
|
||||
// This is a standardized widget API request.
|
||||
"api": "fromWidget",
|
||||
"widgetId": "20220712_WidgetExample",
|
||||
"requestid": "generated-id-1234",
|
||||
"action": "unwatch_turn_servers", // value defined by this proposal
|
||||
"data": {} // nothing
|
||||
}
|
||||
```
|
||||
|
||||
This action tells the client to stop sending the widget TURN server updates. As with `watch_turn_servers`, the `data`
|
||||
and `response` fields for this action are empty. If the widget was not already watching for TURN servers, the action has
|
||||
no effect.
|
||||
|
||||
```json5
|
||||
{
|
||||
// This is a standardized widget API request.
|
||||
"api": "fromWidget",
|
||||
"widgetId": "20220712_WidgetExample",
|
||||
"requestid": "generated-id-1234",
|
||||
"action": "unwatch_turn_servers",
|
||||
"data": {},
|
||||
"response": {} // nothing
|
||||
}
|
||||
```
|
||||
|
||||
**`toWidget` action of `update_turn_servers`**
|
||||
|
||||
```json5
|
||||
{
|
||||
// This is a standardized widget API request.
|
||||
"api": "toWidget",
|
||||
"widgetId": "20220712_WidgetExample",
|
||||
"requestid": "generated-id-1234",
|
||||
"action": "update_turn_servers", // value defined by this proposal
|
||||
"data": {
|
||||
"uris": [
|
||||
"turn:turn.example.com:3478?transport=udp",
|
||||
"turn:10.20.30.40:3478?transport=tcp",
|
||||
"turns:10.20.30.40:443?transport=tcp"
|
||||
],
|
||||
"username": "1443779631:@user:example.com",
|
||||
"password": "JlKfBy1QwLrO20385QyAtEyIv0="
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This action informs the widget of the current TURN server URIs and credentials provided by the homeserver. As shown, the
|
||||
`data` for this action contains a list of TURN URIs, along with the username and password to use with them.
|
||||
|
||||
The widget acknowledges the request with an empty response object:
|
||||
|
||||
```json5
|
||||
{
|
||||
// This is a standardized widget API request.
|
||||
"api": "toWidget",
|
||||
"widgetId": "20220712_WidgetExample",
|
||||
"requestid": "generated-id-1234",
|
||||
"action": "update_turn_servers",
|
||||
"data": {
|
||||
"uris": [
|
||||
"turn:turn.example.com:3478?transport=udp",
|
||||
"turn:10.20.30.40:3478?transport=tcp",
|
||||
"turns:10.20.30.40:443?transport=tcp"
|
||||
],
|
||||
"username": "1443779631:@user:example.com",
|
||||
"password": "JlKfBy1QwLrO20385QyAtEyIv0="
|
||||
},
|
||||
"response": {} // nothing
|
||||
}
|
||||
```
|
||||
|
||||
## Potential issues
|
||||
|
||||
For simplicity, this proposal gives the widget no way to know *when* TURN server data will expire. It is instead
|
||||
intended that the client will handle the TTL returned by the homeserver opaquely, for the sole purpose of refreshing the
|
||||
TURN server data when it expires. It also gives no way to inform the widget when network errors or rate limits occur
|
||||
during polling, leaving it up to the client to either wait for the error to be resolved on its own, or inform the widget
|
||||
in the meantime that no TURN servers are available, by sending an empty `uris` list.
|
||||
|
||||
## Alternatives
|
||||
|
||||
A simpler approach would be to expose a single from-widget action to fetch the TURN data along with a TTL, just like
|
||||
what would be returned by the `/_matrix/client/v3/voip/turnServer` endpoint. However, in practice this would not make
|
||||
the implementation any simpler, but instead just shift the burden of refreshing the data and handling transient errors
|
||||
onto to the widget. Clients wishing to support the `m.turn_servers` capability will likely already be doing VoIP in some
|
||||
form, so by placing the burden on clients to handle the polling opaquely, we enable them to reuse their existing TURN
|
||||
server logic.
|
||||
|
||||
## Security considerations
|
||||
|
||||
By granting a widget access to TURN credentials, the client is entrusting the widget with the same responsibility to not
|
||||
misuse the TURN server that the homeserver is entrusting to the client. So, as with other widget capabilities, the
|
||||
client must either prompt the user for permission, or take appropriate measures to verify that the capability can be
|
||||
safely auto-approved, such as if the widget is running code from a specific, trusted domain.
|
||||
|
||||
## Unstable prefix
|
||||
|
||||
While this MSC is not yet included in the spec, implementations should use `town.robin.msc3846.turn_servers` in place of
|
||||
the `m.turn_servers` capability identifier, and only call/support the actions if a widget API version of
|
||||
`town.robin.msc3846` is advertised.
|
||||
|
||||
## Dependencies
|
||||
|
||||
None, though in practice, widgets should probably be formally included in the spec before this MSC gets included.
|
||||
Loading…
Reference in New Issue