Skip to content

Commit 5610436

Browse files
authored
Merge pull request #1960 from matrix-org/travis/msc/integrations/openid
MSC1960: OpenID information exchange with widgets
2 parents 81c7893 + c9e8326 commit 5610436

File tree

1 file changed

+192
-0
lines changed

1 file changed

+192
-0
lines changed
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# MSC1960: OpenID Connect information exchange for widgets
2+
3+
Widgets are currently left with no options to verify the user's ID, making it hard for
4+
personalized and authenticated widgets to exist. The spec says the `$matrix_user_id`
5+
template variable cannot be relied upon due to how easy it is to faslify, which is true.
6+
7+
This MSC aims to solve the problem with verifiably accurate OpenID Connect credentials.
8+
9+
As of writing, the best resource to learn more about the widgets spec is the following
10+
spec PR: https://github.com/matrix-org/matrix-doc/pull/2764
11+
12+
## Proposal
13+
14+
Typically widgets which need to accurately verify the user's identity will also have a
15+
backend service of some kind. This backend service likely already uses the integration
16+
manager authentication APIs introduced by [MSC1961](https://github.com/matrix-org/matrix-doc/pull/1961).
17+
18+
Through using the same concepts from MSC1961, the widget can verify the user's identity
19+
by requesting a fresh OpenID Connect credential object to pass along to its backend, like
20+
the integration manager which might be running it.
21+
22+
The protocol sequence defined here is based upon the previous discussion in the Element Web
23+
issue tracker: https://github.com/vector-im/element-web/issues/7153
24+
25+
It is proposed that after the capabilities negotation, the widget can ask the client for
26+
an OpenID Connect credential object so it can pass it along to its backend for validation.
27+
The request SHOULD result in the user being prompted to confirm that the widget can have
28+
their information. Because of this user interaction, it's not always possible for the user
29+
to complete the approval within the 10 second suggested timeout by the widget spec. As
30+
such, the initial request by the widget can have one of three states:
31+
32+
1. The client indicates that the user is being prompted (to be followed up on).
33+
2. The client sends over credentials for the widget to verify.
34+
3. The client indicates the request was blocked/denied.
35+
36+
The initial request from the widget looks as follows:
37+
38+
```json
39+
{
40+
"api": "fromWidget",
41+
"action": "get_openid",
42+
"requestId": "AAABBB",
43+
"widgetId": "CCCDDD",
44+
"data": {}
45+
}
46+
```
47+
48+
Which then receives a response which has a `state` field alongside potentially the credentials
49+
to be verified. Matching the order of possible responses above, here are examples:
50+
51+
```json
52+
{
53+
"api": "fromWidget",
54+
"action": "get_openid",
55+
"requestId": "AAABBB",
56+
"widgetId": "CCCDDD",
57+
"data": {},
58+
"response": {
59+
"state": "request"
60+
}
61+
}
62+
```
63+
64+
```json
65+
{
66+
"api": "fromWidget",
67+
"action": "get_openid",
68+
"requestId": "AAABBB",
69+
"widgetId": "CCCDDD",
70+
"data": {},
71+
"response": {
72+
"state": "allowed",
73+
"access_token": "s3cr3t",
74+
"token_type": "Bearer",
75+
"matrix_server_name": "example.org",
76+
"expires_in": 3600
77+
}
78+
}
79+
```
80+
81+
```json
82+
{
83+
"api": "fromWidget",
84+
"action": "get_openid",
85+
"requestId": "AAABBB",
86+
"widgetId": "CCCDDD",
87+
"data": {},
88+
"response": {
89+
"state": "blocked"
90+
}
91+
}
92+
```
93+
94+
The credential information is directly copied from the `/_matrix/client/r0/user/:userId/openid/request_token`
95+
response.
96+
97+
In the case of `state: "request"`, the user is being asked to approve the widget's attempt to
98+
verify their identity. To ensure that future requests are quicker, clients are encouraged to
99+
include a "remember this widget" option to make use of the immediate `state: "allowed"` or
100+
`state: "blocked"` responses above.
101+
102+
There is no timeout associated with the user making their selection. Once a user does make
103+
a selection (allow or deny the request), the client sends a `toWidget` request to indicate the
104+
result, using a very similar structure to the above immediate responses:
105+
106+
```json
107+
{
108+
"api": "toWidget",
109+
"action": "openid_credentials",
110+
"requestId": "EEEFFF",
111+
"widgetId": "CCCDDD",
112+
"data": {
113+
"state": "allowed",
114+
"original_request_id": "AAABBB",
115+
"access_token": "s3cr3t",
116+
"token_type": "Bearer",
117+
"matrix_server_name": "example.org",
118+
"expires_in": 3600
119+
}
120+
}
121+
```
122+
123+
```json
124+
{
125+
"api": "toWidget",
126+
"action": "openid_credentials",
127+
"requestId": "EEEFFF",
128+
"widgetId": "CCCDDD",
129+
"data": {
130+
"state": "blocked",
131+
"original_request_id": "AAABBB"
132+
}
133+
}
134+
```
135+
136+
`original_request_id` is the `requestId` of the `get_openid` request which started the prompt,
137+
for the widget's reference.
138+
139+
The widget acknowledges receipt of the credentials with an empty `response` object.
140+
141+
A typical sequence diagram for this flow is as follows:
142+
143+
```
144+
+-------+ +---------+ +---------+
145+
| User | | Client | | Widget |
146+
+-------+ +---------+ +---------+
147+
| | |
148+
| | Capabilities negotiation |
149+
| |----------------------------------------->|
150+
| | |
151+
| | Capabilities negotiation |
152+
| |<-----------------------------------------|
153+
| | |
154+
| | fromWidget get_openid request |
155+
| |<-----------------------------------------|
156+
| | |
157+
| | ack with state "request" |
158+
| |----------------------------------------->|
159+
| | |
160+
| Ask if the widget can have information | |
161+
|<--------------------------------------------| |
162+
| | |
163+
| Approve | |
164+
|-------------------------------------------->| |
165+
| | |
166+
| | toWidget openid_credentials request |
167+
| |----------------------------------------->|
168+
| | |
169+
| | acknowledge request (empty response) |
170+
| |<-----------------------------------------|
171+
```
172+
173+
Prior to this proposal, widgets could use an undocumented `scalar_token` parameter if the client chose to
174+
send it to the widget. Clients typically chose to send it if the widget's URL matched a whitelist for URLs
175+
the client trusts. With the widget specification as written, widgets cannot rely on this behaviour.
176+
177+
Widgets may wish to look into cookies and other storage techniques to avoid continously requesting
178+
credentials. Widgets should also look into [MSC1961](https://github.com/matrix-org/matrix-doc/pull/1961)
179+
for information on how to properly verify the OpenID Connect credentials it will be receiving. The
180+
widget is ultimately responsible for how it deals with the credentials, though the author recommends
181+
handing it off to an integration manager's `/register` endpoint to acquire a single token string
182+
instead.
183+
184+
An implementation of this proposal's early draft is here: https://github.com/matrix-org/matrix-react-sdk/pull/2781
185+
186+
## Security considerations
187+
188+
The user is explicitly kept in the loop to avoid automatic and silent harvesting of private information.
189+
Clients must ask the user for permission to send OpenID Connect information to a widget, but may optionally allow
190+
the user to always allow/deny the widget access. Clients are encouraged to notify the user when future
191+
requests are automatically handled due to the user's prior selection (eg: an unobtrusive popup saying
192+
"hey, your sticker picker asked for your information. [Block future requests]").

0 commit comments

Comments
 (0)