-
Notifications
You must be signed in to change notification settings - Fork 386
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
[WIP] MSC1956: Integrations API (base) #1956
base: old_master
Are you sure you want to change the base?
Changes from all commits
907eb14
3e2d060
b754a26
90e4614
805a2ca
e4ba7a2
72cdfa8
fcfed3e
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,260 @@ | ||
# MSC1956: Integrations API | ||
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. TODO: Add a ready state to widget postMessage API so that widgets don't have to rely on the capabilities request taking an amount of time. |
||
|
||
"Integration managers" are applications that help clients bring integrations (bridges, bots, widgets, etc) | ||
to users. This can be for things like setting up an IRC bridge or adding a Giphy bot to the room. Integration | ||
managers have been popularized by Riot, however other clients also have support for concept as well although | ||
to a lesser extent. | ||
|
||
Integration managers are important to the Matrix ecosystem as they help abstract away the underlying integration | ||
and allow providers of the integratiosn to manage access to them. For example, there are multiple XMPP bridges | ||
available in the Matrix ecosystem and an integration manager is in the best position to handle the different APIs | ||
each exposes, generalizing the API exposed to clients. Similarly, the integration manager could charge for access | ||
to specific integrations such as sticker packs or per-user access to a bridge. | ||
|
||
This proposal introduces a new Matrix API: the "Integrations API" (or "Integrations Specification") which covers | ||
how integration managers and integrations themselves cooperate with clients. Typically these kinds of extensions | ||
to Matrix would be put into the Client-Server API, however the integration manager is not typically the same as | ||
the user's homeserver and is therefore ideal to be specified indepdently. | ||
|
||
|
||
## Glossary | ||
|
||
Due to the introduction of a new API, some common terminology is to be established: | ||
|
||
An **Integration Manager** (or *Integrations Manager*, or simply *Manager*) is an application which assists users | ||
and/or clients in setting up *Integrations*. | ||
|
||
**Integrations** are something the *Integration Manager* exposes, such as *Bots*, *Bridges*, and *Widgets*. | ||
|
||
A **Bot** is typically a Matrix user which provides a utility service, such as Github Notifications or reaction | ||
GIFs. | ||
|
||
A **Bridge** is typically an Application Service which proxies events between Matrix and a 3rd party platform, | ||
such as IRC. Bridges may also operate in a single direction and have other features like *Puppeting*, which are | ||
not covered here. For information on the various types of bridging, please see the | ||
[reference guide](https://matrix.org/docs/guides/types-of-bridging.html). | ||
|
||
**Widgets** are embedded web applications in a Matrix client. These are split into two types: **Account Widgets** | ||
and **Room Widgets**. *Account Widgets* are specific to a particular Matrix user and not shared with other users | ||
whereas *Room Widgets* are shared with the members of the room they reside in. | ||
|
||
A **Trusted Integration Manager** is an *Integration Manager* which the client has deemed trustworthy by its own | ||
criteria. For example, this may be defined as being known as an account widget, discovered by .well-known, or | ||
being set in the client's configuration. By proxy, an **Untrusted Integration Manager** is the opposite. | ||
|
||
The **Integration Manager API** is the set of HTTP endpoints which clients can use to interact with a given | ||
*Integration Manager*. | ||
|
||
The **Widget API** is the set of `postMessage` APIs which are used for clients and *Widgets* to communicate with | ||
each other when embedded. | ||
|
||
The **Integrations API** is the set of both the *Integration Manager API* and *Widget API* as well as any other | ||
details which affect *Widgets* or *Integration Managers* and their interactions with clients. | ||
|
||
|
||
## Proposal | ||
|
||
A new Integrations API be introduced into Matrix which consists of the components listed in this proposal. | ||
|
||
**Components**: | ||
turt2live marked this conversation as resolved.
Show resolved
Hide resolved
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. Include #2192 (if it stays relevant) 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. add #2315 |
||
|
||
* API Standards (see later section of this proposal) | ||
* Discovery - [MSC1957](https://github.com/matrix-org/matrix-doc/pull/1957) | ||
* Widgets | ||
* Includes [MSC1236](https://github.com/matrix-org/matrix-doc/issues/1236) | ||
* Includes [MSC1958](https://github.com/matrix-org/matrix-doc/pull/1958) | ||
* Includes [MSC1960](https://github.com/matrix-org/matrix-doc/pull/1960) | ||
* MSC for extensions and alterations not yet defined | ||
* ***TODO: MSC for each or group them by subject?*** | ||
* The first version of the Widget API is to be `0.1.0`, with the existing `0.0.x` versions being flagged as | ||
development versions which are otherwise unspecified. | ||
* Sticker picker and custom emoji | ||
* Includes [MSC1951](https://github.com/matrix-org/matrix-doc/pull/1951) | ||
* Includes [MSC1959](https://github.com/matrix-org/matrix-doc/pull/1959) | ||
* Authentication in integration managers - [MSC1961](https://github.com/matrix-org/matrix-doc/pull/1961) | ||
* Terms of service / policies - [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140) | ||
* Integration manager specific APIs | ||
* MSC and scope not yet defined | ||
|
||
***TODO: Define where paid integrations, OAuth, etc all fit if at all.*** | ||
***TODO: Finalize what goes into this spec.*** | ||
|
||
|
||
## Proposed API standards | ||
|
||
All HTTP APIs in this specification must consume and produce JSON unless otherwise indicated. Errors emitted by | ||
these APIs should follow the standard error responses defined by other APIs in Matrix. Some common error responses | ||
implementations may encounter are: | ||
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. From reading the code of https://github.com/matrix-org/matrix-widget-api, it seems like these errors have only a message field that contains human-readable text. This could be problematic since the error message is not defined in the spec, so it's impossible for a widget to know what the error is. For example, the string It may be helpful to define standard error types (like the error codes in the C2S API) to categorize these errors. Ex, |
||
* `403` `{"errcode":"M_UNKNOWN_TOKEN","error":"Invalid token"}` | ||
* `400` `{"errcode":"M_MISSING_PARAM","error":"Missing 'matrix_server_name'"}` | ||
* `400` `{"errcode":"M_INVALID_PARAM","error":"'matrix_server_name' must be a string"}` | ||
* `400` `{"errcode":"M_NOT_FOUND","error":"The sharable URL did not resolve to a pack"}` | ||
|
||
|
||
## Tradeoffs | ||
|
||
Specifying a whole new set of APIs means introducing another complex system into Matrix, potentially causing | ||
confusion or concerns about the architecture of Matrix or its implementations. It is believed by the author that | ||
having a dedicated set of APIs for this system within Matrix is important to reduce the burden on homeserver | ||
authors and to support client authors in their ambitions to grow Matrix through bridges, bots, etc. | ||
|
||
It is also questionable if integration managers should be a concept within Matrix as the APIs which clients would | ||
be interacting with can be generically exposed by the integrations themselves without the need for a manager. The | ||
manager's role in this specification is to provide clients with the opportunity to quickly get themselves connected | ||
with the larger Matrix ecossytem without adding the burden of designing their own interface. The majority of the | ||
APIs are defined such that a client is able to put their own UI on top of the manager instead of embedding that | ||
manager if desired by the client. | ||
|
||
|
||
## Potential issues | ||
|
||
Clients which support today's ecosystem of integration managers have a lot of work to do to support this proposal | ||
in all its parts. In addition, integration managers themselves have a decent amount of work in order to become | ||
compliant. The exercise of this proposal is not to make development harder for the projects in the ecosystem, but | ||
to standardize on a set of APIs which bridge the gap between managers while also patching inadequacies in the current | ||
systems. | ||
|
||
|
||
## Security considerations | ||
|
||
Each proposal which branches off this proposal has its own set of security considerations. As a whole, clients are | ||
expected to make decisions on which integration managers are trusted, and integration managers are encouraged to do | ||
the same with clients and integrations. | ||
|
||
---- | ||
|
||
## How all of this is meant to work (versus before) | ||
|
||
This portion of the proposal is not required reading - it is supplemental information to help explain how integration | ||
managers work in a world with this API and prior. This is also meant to be moderately high level and doesn't go into | ||
the specific API endpoints one would call. | ||
|
||
### Integration managers today (pre-API) | ||
|
||
Integration managers are very much a Riot concept that has lead to enough utility to make it worth putting the idea | ||
in the spec. An integration manager is typically just a UI for interacting with bridges/bots/widgets instead of having | ||
to do all the bits manually or through a command line-like interface. Riot defines an integration manager as a set | ||
of URLs: one for the UI (`ui_url`) and one for its API (`rest_url`). There are other URLs that also take place, but | ||
they're covered in more detail later on in this text. | ||
|
||
Before an integration manager can even be shown to the user, an authentication dance must take place. This is to | ||
acquire an authentication token (wrongly named a `scalar_token` in Riot) - this token is then provided to the integration | ||
manager where applicable via a `scalar_token` query string parameter. The auth dance involves getting an OpenID token | ||
from the homeserver for the user, passing that to the integration manager via its API URL, and getting back a token | ||
it can then use for future requests. The integration manager internally takes the OpenID token and verifies it by | ||
doing a federated request to the homeserver - this is how it claims the token and gets a user ID back. This is also | ||
why homeservers generally need working federation in order to use integrations, at least until recently when Synapse | ||
could support exposing the OpenID claim endpoint without the rest of federation (Modular and similar deployments use | ||
this). | ||
|
||
The entire auth dance is done in the background, so the user doesn't see this happening. If any of the steps go wrong, | ||
the user is warned that the integration manager is offline/inaccessible. After the auth dance, Riot does a terms of | ||
service check to ensure the user has accepted all applicable policies. It does this by using the API URL to get the | ||
authentication token owner's information (`/account`) which can return an error if there are unaccepted policies. If | ||
there are policies to accept, Riot prompts before continuing. Provided the user accepts all the policies, Riot moves | ||
on to rendering the integration manager window using the UI URL as a base. Depending on what the user clicked depends | ||
on how the URL is constructed. Typically users would click the 4/9 squares in the context of the room, so a generic | ||
URL referencing the "homepage" of the manager and the room ID is supplied to the manager. The user's authentication | ||
token for the manager is also included in the URL. | ||
|
||
The integration manager then takes all the information from the URL and starts to render the UI. In this case, it would | ||
be rendering the homepage so it asks its backend for information about integrations it supports and starts trying to | ||
figure out which ones are feasible. In the process, it queries Riot itself for some information (bot membership, room | ||
publicity, room member count, etc) - this is done over a special `postMessage` API named `ScalarMessaging` (which is | ||
not really Scalar-specific). Note that this API is different from the Widget `postMessage` API - more on that in a bit. | ||
|
||
The ScalarMessaging API acts in the background and allows the integration manager to check the state of things as well | ||
as perform actions. For example, when the user wants to add a bot to the room the manager will use the ScalarMessaging | ||
API to determine if the bot is already in the room, and if it isn't it will use ScalarMessaging to cause the user to | ||
invite the bot to the room. The expectation is generally that the bot will auto-accept invites without needing the | ||
integration manager or Riot to interfere. When the bot is being removed from the room, it is not kicked through the API. | ||
Instead, the integration manager calls its backend which impersonates the bot to leave the room. | ||
|
||
The ScalarMessaging API is locked down enough to ensure only the integration manager can interact with it. | ||
|
||
When the user finally wants to close the integration manager, they can click outside of it to have Riot dismiss the dialog | ||
or they can click a close button within the integration manager which uses the ScalarMessaging API to close itself. | ||
|
||
The other thing the ScalarMessaging class lets integration managers do is add widgets into the room and onto the user's | ||
account. Widgets added by an integration manager are almost always wrapped by the integration manager itself. Widgets | ||
are simply URLs that are rendered in iframes, so when the integration manager wraps a widget it means there's the top | ||
level iframe supplied by Riot and another iframe supplied by the integration manager's wrapper. The wrapper usually | ||
provides some features like being able to talk the Widget `postMessage` API (here on out known as the Widget API) | ||
and a fullscreen button. Dimension, Scalar's opensource "competitor", doesn't require any authentication in order to | ||
load this widget wrapper however Scalar does. Riot solves this by having a "widget URL whitelist" in the configuration | ||
for when to provide an authentication token to the widget through its URL. | ||
|
||
Scalar has two methods of checking for an authentication token, both of which are used to try and avoid the dreaded | ||
"Forbidden" error page. The first is simply using the authentication token it was provided by the client. The second | ||
involves cookies that are scoped to the Scalar domain. Typically the cookie is set after a token has been successfully | ||
used (as provided by the client). The cookies helps avoid the forbidden error page when Riot decides it can't send | ||
the authentication token to the widget/manager (typically when people change to Dimension or another integration manager). | ||
|
||
Dimension uses a similar scheme for ensuring it has a token available, though it uses localstorage in place of cookies. | ||
When both the provided and stored token fail, Dimension uses Riot's OpenID exchange API to acquire a new token by asking | ||
the user for permission to share their identity. This is supposed to be used as a last resort, given Dimension only needs | ||
a token to determine the user's stickerpacks and nothing more (for widgets). | ||
|
||
It is worth noting that widgets are designed such that the integration manager gets no special treatment, however in | ||
practice integration manager widgets are very difficult to manage from a client perspective, primarily for authentication | ||
reasons. Widgets are simply supposed to be iframes with no dealing of tokens from a spec perspective. | ||
|
||
The sticker picker is a particularly interesting widget in that it's at the user's account level and not in a room. By | ||
nature of being a widget, it is supposed to be independent of the integration manager however in practice users find it | ||
confusing when their integration manager changes and their sticker picker is the "old" one. This is something that can | ||
usually be mitigated by the client (removing/replacing the sticker picker widget when the user asks to change their | ||
integration manager), but so far no client has opted to do so. | ||
|
||
The sticker picker widget uses a capabilities subsystem in the Widget API to get permission to send events (stickers) | ||
on behalf of the user. Sticker pickers are one of the widgets which require authentication so the widget can show the | ||
user the sticker packs they have enabled. Riot currently has the capabilities exchange happen in the background, though | ||
it could expose it as a dialog to the user. | ||
|
||
The final aspect of integration managers in the current ecosystem is conference calls: Riot uses a Jitsi widget to make | ||
a conference call. Because different integration managers have different opinions on how the Jitsi widget should be declared, | ||
Riot supports a configuration option to change the base URL of the Jitsi widget before it gets added to the room state. | ||
The Jitsi URL in the config almost always points to an integration manager which wraps the widget. | ||
|
||
### Integration managers in a post-API world | ||
|
||
This proposal defines an API for how integration managers are supposed to work, taking into consideration user expectations, | ||
technical limitations, and other issues with the current state of affairs. Most critically, this new API brings forth a | ||
new authentication scheme for integration managers (and therefore widgets) by making somewhat radical changes to the way | ||
things work. | ||
|
||
The first major change is that integration managers become widgets. This is confusing at first, but if we run the thought | ||
experiment for a bit it starts to make some sense. Widgets already have a well-specified API they can use to talk to the | ||
client, and with some modifications (proposed here) the same API can be used to run an integration manager. This proposal | ||
builds on the capabilities system to add additional actions needed by integration managers, such as adding readonly views | ||
of the room membership and ability to invite users. This does away with the existing API which is not as well specified. | ||
|
||
Widgets under this proposal also lose their authentication tokens, which means integration managers also lose an authentication | ||
token. Authentication tokens are not completely gone though: the APIs to get them still exist for reasons described a bit | ||
later on, and widgets can use the now-specified OpenID exchange API to acquire a token if it needs one. There's additionally | ||
a provision within the proposal to allow clients to skip the prompt under some circumstances, such as when dealing with a | ||
sticker picker or integration manager - these kinds of widgets are likely to need/request OpenID information for good reason | ||
so the client can do so quietly in the background without nagging the user. Other widgets, like custom widgets, are expected | ||
to prompt for confirmation from the user. | ||
|
||
With this new authentication system, clients no longer need to track a whitelist of widget URLs to append tokens to and | ||
generally don't need to track the tokens for themselves. Terms of service acceptance is also expected to be done by the | ||
widgets (including integration manager) themselves, though smarter clients like Riot can try and optimize for it by still | ||
doing the auth&check dance. | ||
|
||
This proposal also defines a set of APIs for clients to interact with the integration manager directly, avoiding the use | ||
of iframes where applicable. The theory is that clients wanting to control the integrations experience can do so by invoking | ||
the integration manager's API on top of the client's native UI/UX. An example of where this might be worthwhile for Riot is | ||
the sticker picker: instead of rendering an iframe and having to deal with a lot of complicated APIs, Riot could instead | ||
just do the auth dance and get all the information it needs from the widget. A similar approach could be taken for adding | ||
an IRC bridge to a room: if the client were driving the manager's API, it could offer a button instead of rendering a whole | ||
iframe. | ||
|
||
To handle the Jitsi use case, this proposal introduces a "make me a widget" API. The API exists on integration managers to | ||
construct a widget for the client, doing away with Riot's Jitsi URL in the config. | ||
|
||
Integration managers like Scalar can still require an authentication token to render the actual widget by having a small | ||
wrapper which uses the OpenID exchange API. The wrapper should also persist the token somewhere (cookies/localstorage) so | ||
it can load the next time unobtrusively. | ||
|
||
Fundamentally the power an integration manager has is unchanged by this proposal, though how they perform actions is vastly | ||
different, hopefully for the better. |
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.
note to self: reference the following diagrams (if needed):
Also, the dances need better names. Currently we have: