Skip to content

Commit 52db4c6

Browse files
sandhosehughnsdhenneketonkku107richvdh
authored
MSC2964: Usage of OAuth 2.0 authorization code grant and refresh token grant (#2964)
* OAuth 2.0 profile MSC * Refer to OP rather than AS to avoid clash with Application Service * Title update and intro about architectural change * Add section on endpoints that would now be outside of scope and so removed * Spelling * Section on proposed endpoints that would no longer be relevant * Consistency with MSC3861 and cleanup * Standardise terminology on OpenID Provider = OP * Update proposals/2964-oauth2-profile.md Co-authored-by: Dominik Henneke <dominik.henneke@nordeck.net> * Notes on QR and browserless * OpenID id_token endpoint is still needed * Notes about confusion with existing OIDC and OpenID capabilities * Additional endpoints to be removed * Add 3pid endpoints that would be removed * Changes to GET /account/3pid * Alternative proposal for 3PID handling * Add section on removing UIA * Refer to UIA as API * We now have proposal for 3PID and guest access * Logout semantics * Remove TBDs that are done * More done items * Remove dependency loop * Rework proposal to only cover the authorization code flow * Fix a bunch of todos * Fix typos * Fix the response_mode being an authorization request parameter * Apply suggestions from code review Co-authored-by: Tonkku <tonkku.kallio3@gmail.com> * Remove unused images * Expand the security considerations section * Clarify that using PKCE with *S256* is mandatory * Apply suggestions from code review Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * All Matrix clients are public clients, no need to be too specific * Add PAR/RAR in 'alternatives' section * Replace horizontal rules with subsections * Clarify how the client should handle access token refresh failures * Explain why clients should use the fragment response_mode better * Explain the scope better in the example * Clarify that `code_verifier` should be cryptographically random Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> * Typo Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> --------- Co-authored-by: Hugh Nimmo-Smith <hughns@users.noreply.github.com> Co-authored-by: Hugh Nimmo-Smith <hughns@element.io> Co-authored-by: Dominik Henneke <dominik.henneke@nordeck.net> Co-authored-by: Tonkku <tonkku.kallio3@gmail.com> Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
1 parent 2f670ca commit 52db4c6

File tree

1 file changed

+283
-0
lines changed

1 file changed

+283
-0
lines changed

proposals/2964-oauth2-profile.md

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
# MSC2964: Usage of OAuth 2.0 authorization code grant and refresh token grant
2+
3+
This proposal is part of the broader [MSC3861: Next-generation auth for Matrix, based on OAuth 2.0/OIDC][MSC3861].
4+
5+
This MSC in particular defines how clients can leverage the OAuth 2.0 authorization code grant to gain access to the Matrix Client-to-Server API.
6+
7+
## Proposal
8+
9+
### Prerequisites
10+
11+
This proposal requires the client to know the following authorization server metadata about the homeserver:
12+
13+
- `authorization_endpoint`: the URL where the user should be sent to initiate the login flow
14+
- `token_endpoint`: the URL where the client is able to exchange the authorization code for an access token
15+
- `response_types_supported`: a JSON array of response types supported by the authorization endpoint
16+
- `grant_types_supported`: a JSON array of grant types supported by the authorization endpoint defined in [RFC8414] and used in [RFC6749]
17+
- `response_mode_supported`: a JSON array of response modes supported by the authorization endpoint
18+
19+
All of those metadata values are well-defined in [RFC8414] and used in various RFCs like [RFC6749].
20+
21+
The discovery of the above metadata is out of scope for this MSC, and is currently covered by [MSC2965](https://github.com/matrix-org/matrix-doc/pull/2965).
22+
23+
The client must also have a `client_id` to use with this flow.
24+
How the client obtains this is out of scope for this MSC, and is currently covered by [MSC2966](https://github.com/matrix-org/matrix-doc/pull/2966).
25+
26+
### Authorization code grant
27+
28+
As per [RFC6749], the authorization code grant lets the client obtain an access token through a browser redirect.
29+
30+
Because this flow has various parameters and security improvements added by other specifications, this describes what is enforced and required to support by the client and the homeserver.
31+
32+
Homeservers and clients must:
33+
34+
- support PKCE using the `S256` code challenge method as per [RFC7636]
35+
- support the auth code flow as per [RFC6749] section 4.1
36+
- support the refresh token grant as per [RFC6749] section 6
37+
- use pre-registered, strict redirect URIs
38+
- use the `fragment` response mode as per [OAuth 2.0 Multiple Response Type Encoding Practices] for clients with an HTTPS redirect URI
39+
40+
### Refresh token grant
41+
42+
When authorization is granted to a client, the homeserver must issue a refresh token to the client in addition to the access token.
43+
44+
The access token must be short-lived and should be refreshed using the `refresh_token` when expired, as described in [RFC6749] section 6.
45+
46+
The homeserver should issue a new refresh token each time one is used, and invalidate the old one.
47+
It should do this only if it can guarantee that in case a response with a new refresh token is not received and stored by the client, retrying the request with the old refresh token will succeed.
48+
49+
The homeserver should consider that the session is compromised if an old, invalidated refresh token is being used, and should revoke the session.
50+
51+
The client must handle access token refresh failures as follows:
52+
53+
- If the refresh fails due to network issues or a `5xx` HTTP status code from the server, the client should retry the request with the old refresh token later.
54+
- If the refresh fails due to a `4xx` HTTP status code from the server, the client should consider the session logged out.
55+
56+
### Sample flow
57+
58+
#### Flow parameters
59+
60+
The client must know the following parameters, through ways described in [MSC2965], [MSC2966] and [MSC2967]:
61+
62+
- `authorization_endpoint`: the URL where the user is able to access the authorization endpoint to initiate the login flow
63+
- `token_endpoint`: the URL where the user is able to access the token endpoint to exchange the authorization code for an access token
64+
- `client_id`: the unique identifier allocated for the client
65+
- `redirect_uri`: the URI where the user is redirected after the authorization flow used by this client
66+
- `scope`: the scope of the access token to request
67+
- `response_mode`: the response mode to use, either `fragment` or `query`. It must be `fragment` if the `redirect_uri` is an HTTPS URI, and can be `query` otherwise
68+
69+
It needs to generate the following values:
70+
71+
- a random value for the `state`
72+
- a cryptographically random value for the `code_verifier`
73+
74+
#### Authorization request
75+
76+
It then constructs the authorization request URL using the `authorization_endpoint` value, with the following query parameters:
77+
78+
- The `response_type` value set to `code`
79+
- The `client_id` value
80+
- The `redirect_uri` value
81+
- The `scope` value
82+
- The `state` value
83+
- The `response_mode` value
84+
- The `code_challenge` computed from the `code_verifier` value using the SHA-256 algorithm, as described in [RFC7636]
85+
- The `code_challenge_method` set to `S256`
86+
87+
This authorization request URL must be opened in the user's browser:
88+
89+
- For web-based clients, this can be done through a redirection or by opening the URL in a new tab
90+
- For native clients, this can be done by opening the URL:
91+
- using the system browser
92+
- through platform-specific APIs when available, such as [`ASWebAuthenticationSession`](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession) on iOS or [Android Custom Tabs](https://developer.chrome.com/docs/android/custom-tabs) on Android
93+
94+
The rationale for using the system browser is explained in [MSC3861], under "Motivation" → "Benefits of authenticating end-users through the system browser".
95+
96+
##### Sample authorization request
97+
98+
Sample authorization request (broken down into multiple lines for readability), with the following values:
99+
100+
- `authorization_endpoint` set to `https://account.example.com/oauth2/auth`, obtained through [MSC2965]
101+
- `client_id` set to `s6BhdRkqt3`, obtained through [MSC2966]
102+
- `redirect_uri` set to `https://app.example.com/oauth2-callback`
103+
- `state` set to `ewubooN9weezeewah9fol4oothohroh3`
104+
- `response_mode` set to `fragment`
105+
- `code_verifier` set to `ogie4iVaeteeKeeLaid0aizuimairaCh`
106+
- `code_challenge` computed as `72xySjpngTcCxgbPfFmkPHjMvVDl2jW1aWP7-J6rmwU`
107+
- `scope` set to `urn:matrix:client:api:* urn:matrix:client:device:AAABBBCCCDDD` (full access to the C-S API, using the `AAABBBCCCDDD` device ID, as per [MSC2967])
108+
109+
```
110+
https://account.example.com/oauth2/auth?
111+
client_id = s6BhdRkqt3 &
112+
response_type = code &
113+
response_mode = fragment &
114+
redirect_uri = https://app.example.com/oauth2-callback &
115+
scope = urn:matrix:client:api:* urn:matrix:client:device:AAABBBCCCDDD &
116+
state = ewubooN9weezeewah9fol4oothohroh3 &
117+
code_challenge = 72xySjpngTcCxgbPfFmkPHjMvVDl2jW1aWP7-J6rmwU &
118+
code_challenge_method = S256
119+
```
120+
121+
#### Callback
122+
123+
Once completed, the user is redirected to the `redirect_uri`, with either a successful or failed authorization in the URL fragment or query parameters.
124+
Whether the parameters are in the URL fragment or query parameters is determined by the `response_mode` value:
125+
126+
- if set to `fragment`, the parameters will be placed in the URL fragment, like `https://example.com/callback#param1=value1&param2=value2`
127+
- if set to `query`, the parameters will be in placed the query string, like `com.example.app:/callback?param1=value1&param2=value2`
128+
129+
To avoid disclosing the parameters to the web server hosting the `redirect_uri`, clients should use the `fragment` response mode if the `redirect_uri` is an HTTP/HTTPS URI with a remote host.
130+
131+
In both success and failure cases, the parameters will have the `state` value used in the authorization request.
132+
133+
##### Successful authorization callback
134+
135+
Successful authorization will have a `code` value.
136+
137+
Sample successful authorization:
138+
139+
```
140+
https://app.example.com/oauth2-callback#state=ewubooN9weezeewah9fol4oothohroh3&code=iuB7Eiz9heengah1joh2ioy9ahChuP6R
141+
```
142+
143+
##### Failed authorization callback
144+
145+
Failed authorization will have the following values:
146+
147+
- `error`: the error code
148+
- `error_description`: the error description (optional)
149+
- `error_uri`: the URI where the user can find more information about the error (optional)
150+
151+
Sample failed authorization:
152+
153+
```
154+
https://app.example.com/oauth2-callback#state=ewubooN9weezeewah9fol4oothohroh3&error=access_denied&error_description=The+resource+owner+or+authorization+server+denied+the+request.&error_uri=https%3A%2F%2Ferrors.example.com%2F
155+
```
156+
157+
#### Token request
158+
159+
The client then exchanges the authorization code to obtain an access token using the token endpoint.
160+
161+
This is done by making a POST request to the `token_endpoint` with the following parameters, encoded as `application/x-www-form-urlencoded` in the body:
162+
163+
- The `grant_type` set to `authorization_code`
164+
- The `code` obtained from the callback
165+
- The `redirect_uri` used in the authorization request
166+
- The `client_id` value
167+
- The `code_verifier` value generated at the start of the authorization flow
168+
169+
The server replies with a JSON object containing the access token, the token type, the expiration time, and the refresh token.
170+
171+
The access token must be short-lived and should be refreshed using the `refresh_token` when expired.
172+
173+
##### Sample token request
174+
175+
```
176+
POST /oauth2/token HTTP/1.1
177+
Host: account.example.com
178+
Content-Type: application/x-www-form-urlencoded
179+
Accept: application/json
180+
181+
grant_type=authorization_code
182+
&code=iuB7Eiz9heengah1joh2ioy9ahChuP6R
183+
&redirect_uri=https://app.example.com/oauth2-callback
184+
&client_id=s6BhdRkqt3
185+
&code_verifier=ogie4iVaeteeKeeLaid0aizuimairaCh
186+
```
187+
188+
```json
189+
{
190+
"access_token": "2YotnFZFEjr1zCsicMWpAA",
191+
"token_type": "Bearer",
192+
"expires_in": 299,
193+
"refresh_token": "tGz3JOkF0XG5Qx2TlKWIA",
194+
"scope": "urn:matrix:client:api:* urn:matrix:client:device:AAABBBCCCDDD"
195+
}
196+
```
197+
198+
#### Token refresh
199+
200+
When the access token expires, the client must refresh it by making a POST request to the `token_endpoint` with the following parameters, encoded as `application/x-www-form-urlencoded` in the body:
201+
202+
- The `grant_type` set to `refresh_token`
203+
- The `refresh_token` obtained from the token response
204+
- The `client_id` value
205+
206+
The server replies with a JSON object containing the new access token, the token type, the expiration time, and a new refresh token.
207+
The old refresh token is no longer valid and should be discarded.
208+
209+
##### Sample token refresh
210+
211+
```
212+
POST /oauth2/token HTTP/1.1
213+
Host: account.example.com
214+
Content-Type: application/x-www-form-urlencoded
215+
Accept: application/json
216+
217+
grant_type=refresh_token
218+
&refresh_token=tGz3JOkF0XG5Qx2TlKWIA
219+
&client_id=s6BhdRkqt3
220+
```
221+
222+
```json
223+
{
224+
"access_token": "2YotnFZFEjr1zCsicMWpAA",
225+
"token_type": "Bearer",
226+
"expires_in": 299,
227+
"refresh_token": "tGz3JOkF0XG5Qx2TlKWIA",
228+
"scope": "urn:matrix:client:api:* urn:matrix:client:device:AAABBBCCCDDD"
229+
}
230+
```
231+
232+
### User registration
233+
234+
Users can register themselves by initiating an authorization code flow with the `prompt=create` parameter as defined in [Initiating User Registration via OpenID Connect 1.0](https://openid.net/specs/openid-connect-prompt-create-1_0.html).
235+
236+
Whether the homeserver supports this parameter is advertised by the `prompt_values_supported` authorization server metadata.
237+
238+
## Potential issues
239+
240+
For a discussion on potential issues please see [MSC3861]
241+
242+
## Alternatives
243+
244+
The authorization flow could make use of [RFC9126: OAuth 2.0 Pushed Authorization Request][RFC9126] as a way to future-proof the flow.
245+
This could help with granting very specific permissions to the client in combination with [RFC9396: OAuth 2.0 Rich Authorization Requests][RFC9396].
246+
247+
As Matrix clients are 'public clients' in the sense of [RFC6749] section 2.1, this proposal would not benefit from the security aspects of [RFC9126].
248+
It could, although, give better feedback to clients when they are trying to start an invalid or unauthorized flow.
249+
250+
Other alternatives for the global proposal are discussed in [MSC3861].
251+
252+
## Security considerations
253+
254+
Since this touches one of the most sensitive parts of the API, there are a lot of security considerations to keep in mind.
255+
256+
The [OAuth 2.0 Security Best Practice](https://tools.ietf.org/html/draft-ietf-oauth-security-topics-16) IETF draft outlines many potential attack scenarios. Many of these scenarios are mitigated by the choices enforced in the client profiles outlined in this MSC.
257+
It motivates the following decisions in this profile:
258+
259+
- Using strict redirect URIs validation helps mitigate the risk of open redirection attacks.
260+
- Using the `code` response mode, alongside PKCE mitigates the risk in cases of redirection hijacking.
261+
- Usage of short-lived access tokens, along with rotation of refresh tokens mitigates the impact of leaked tokens.
262+
- Using the system browser to authenticate users lowers the risk of credentials exfiltration by the client.
263+
264+
## Unstable prefix
265+
266+
None as part of this MSC.
267+
268+
## Dependencies
269+
270+
- [MSC2965]
271+
- [MSC2966]
272+
- [MSC2967]
273+
274+
[RFC6749]: https://tools.ietf.org/html/rfc6749
275+
[RFC7636]: https://tools.ietf.org/html/rfc7636
276+
[RFC8414]: https://tools.ietf.org/html/rfc8414
277+
[RFC9126]: https://tools.ietf.org/html/rfc9126
278+
[RFC9396]: https://tools.ietf.org/html/rfc9396
279+
[MSC2965]: https://github.com/matrix-org/matrix-spec-proposals/pull/2965
280+
[MSC2966]: https://github.com/matrix-org/matrix-spec-proposals/pull/2966
281+
[MSC2967]: https://github.com/matrix-org/matrix-spec-proposals/pull/2967
282+
[MSC3861]: https://github.com/matrix-org/matrix-spec-proposals/pull/3861
283+
[OAuth 2.0 Multiple Response Type Encoding Practices]: https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html

0 commit comments

Comments
 (0)