111 lines
6.2 KiB
Markdown
111 lines
6.2 KiB
Markdown
# MSC3009: Websocket transport for client <--> widget communications
|
|
|
|
If a widget wishes to speak with a client (or a client with a widget) then the implementation must
|
|
support postmessage as a capability, however not all scenarios for widgets make this possible. For
|
|
example, mobile apps and thinly wrapped widgets may not have enough JavaScript available to handle
|
|
the postmessage API.
|
|
|
|
*Note*: A thinly wrapped widget is one which is in a theoretical web view which disconnects the widget
|
|
from the client, for clients which cannot reasonably support a webview or similar. This is primarly
|
|
a solution for desktop clients, which puts the actual client and widget in different processes (the
|
|
desktop app running on its own with the widget running in a web browser after being 'popped out').
|
|
|
|
## Proposal
|
|
|
|
A websocket transport is defined in addition to the existing postmessage API support. This alternative
|
|
transport is opt-in by the client for the widget to handle. The API over postmessage is quite simply
|
|
the same as the postmessage API but with the added implicit behaviour that `waitForIframeLoad` is `false`
|
|
(this causes the client to wait for the widget to reach out to the client instead of the client
|
|
assuming the widget has loaded).
|
|
|
|
Because there would be two transports available, clients need to know which ones are available on
|
|
the widget, and the widget should not be required to implement them all. This proposal opens up an
|
|
opportunity for custom transports through a namespaced array of transports being listed on the standardized
|
|
widget definition:
|
|
|
|
```json5
|
|
{
|
|
"type": "m.custom",
|
|
"url": "http://localhost:8082#?widgetId=$matrix_widget_id&transport=$matrix_widget_transport",
|
|
"name": "Widget Debugger",
|
|
"avatar_url": "mxc://t2bot.io/c977fc5396241194e426e6eb9da64f025f813f1b",
|
|
"data": {
|
|
"title": "Widget testing"
|
|
},
|
|
"creatorUserId": "@travis:localhost",
|
|
"id": "debugger_test",
|
|
|
|
// This is the only added field.
|
|
"transports": [
|
|
"m.postmessage",
|
|
"m.websockets"
|
|
]
|
|
}
|
|
```
|
|
|
|
If the `transports` array is empty, undefined, or the wrong data type then the client will assume that
|
|
the widget definition meant `["m.postmessage"]` for a value. If the array contains no known transports
|
|
to communicate with the widget, the client will assume the widget supports `m.postmessage` as a baseline.
|
|
This means `m.postmessage` remains the bare minimum every widget must support.
|
|
|
|
Prior to rendering the widget, the client picks the transport and spins up any applicable servers (websocket
|
|
receivers, etc) for the widget to communicate with. The client communicates the transport it chose with
|
|
an available `$matrix_widget_transport` template variable.
|
|
|
|
When the `m.websockets` transport is chosen, a `$matrix_websocket_uri` template variable is made available
|
|
by the client containing the `ws://` URI for the widget to connect to. Because the widget is expected to
|
|
make first contact, the client should already be listening and not try to connect to the widget.
|
|
|
|
## Rationale for websockets
|
|
|
|
Websockets are bidirectional and about as easy as HTTP to set up and deploy in modern applications. Most
|
|
use cases will see local clients spinning up servers and therefore the complexities of corporate firewalls
|
|
should not apply.
|
|
|
|
However, using websockets means that the widget cannot be contacted and is assumed to reach out to the client
|
|
first. This could potentially leave the client in an unknown state where it is waiting for the widget to reach
|
|
out, but the widget died or refuses to do so. Reconnection attempts by the widget may also be confusing to
|
|
the client (client restarts but widget doesn't, or local network issues) - **this has undefined behaviour
|
|
under this MSC currently.**
|
|
|
|
## Potential issues
|
|
|
|
Web clients (and widgets) will not be able to create websocket servers to listen for requests from widgets,
|
|
making this MSC targeted specifically at desktop/mobile clients which are typically more able to do so.
|
|
Web clients in particular are likely to need their own alternative transport for cases when the widget
|
|
is popped out of the client's context, such as in a new tab.
|
|
|
|
This MSC also introduced capabilities for arbitrary alternative transports. This isn't necessarily seen
|
|
as a bad thing, though could lead to fragmentation among the widget authoring community. This MSC attempts
|
|
to mitigate that fragmentation by maintaining a baseline transport.
|
|
|
|
There is also a question if this is even needed: if [MSC3008 - Scoped access](https://github.com/matrix-org/matrix-doc/pull/3008)
|
|
were to be accepted, the only remaining functionality would be the capabilities API (for which few capabilities
|
|
are useful in this use case) and other smaller client manipulation MSCs. Given the widget is separated from
|
|
the client, it may not be desirable to support [MSC2931 - matrix.to navigation](https://github.com/matrix-org/matrix-doc/pull/2931)
|
|
over this alternative transport, for example. Similarly, there is nothing to pin to the screen and therefore
|
|
the widget's attempts to request an "always on screen" capability would be for naught.
|
|
|
|
## Alternatives
|
|
|
|
Regular HTTP might also work, as would Server-Sent Events, though these are inherently one-way or require
|
|
excessive resources to make two-way. Other efficient transports are not easily available on all platforms,
|
|
such as lower level TCP/UDP-based transports.
|
|
|
|
## Security considerations
|
|
|
|
There is a high likelihood that when a client spins up a websocket server that it'll be bound to localhost
|
|
without TLS. This could be problematic if widgets are transferring sensitive information over the API
|
|
which could be intercepted by local processes. This is akin to a malicious browser extension listening for
|
|
postmessage requests on `*`, however.
|
|
|
|
## Unstable prefix
|
|
|
|
The `transports` array becomes `org.matrix.msc3009.transports` while this MSC is not considered stable. The
|
|
template variables `$matrix_widget_transport` and `$matrix_websocket_uri` become `$org.matrix.msc3009.widget_transport`
|
|
and `$org.matrix.msc3009.websocket_uri` respectively.
|
|
|
|
The transports become `org.matrix.msc3009.postmessage` and `org.matrix.msc3009.websockets`. Communication
|
|
over either transport is not required to be namespaced in any particular way, however any unstable actions
|
|
over the widget API must be appropriately prefixed.
|