|
| 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