-
Notifications
You must be signed in to change notification settings - Fork 388
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
MSC2966: Usage of OAuth 2.0 Dynamic Client Registration in Matrix #2966
base: main
Are you sure you want to change the base?
MSC2966: Usage of OAuth 2.0 Dynamic Client Registration in Matrix #2966
Conversation
- makes some metadata optional - better explain how each metadata field is used - better explain what the restrictions on redirect_uris are - remove the signed metadata part - mention the client metadata JSON document alternative
Co-authored-by: Tonkku <[email protected]>
85d1958
to
0e2f0f1
Compare
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.
overall this looks good, though I think it needs implementations to be able to move to FCP. I'll document those requirements in a separate review.
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.
Implementation requirements:
- Client using
web
flow - Client using
native
flow - Server supporting both flows (may be two different servers)
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.
Known working clients with the web flow:
- Element Web
Known working clients with the native flow:
- Element Desktop
- Element X iOS
- Element X Android
Server supporting both:
- Synapse with Matrix Authentication Service
- Dendrite with Matrix Authentication Service (PR)
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.
I haven't dug too far to find all the PRs or original implementations, but did find these points of evidence:
- Allow specifying more OIDC client metadata for dynamic registration matrix-js-sdk#4070 (for Element Web via Improve client metadata used for OIDC dynamic registration matrix-react-sdk#12257 )
- Add support for io.element.desktop scheme for OIDC element-hq/element-desktop#1662
- feat(bindings): OIDC in AuthenticationService. matrix-rust-sdk#2412
- The dendrite PR above
MSCs proposed for Final Comment Period (FCP) should meet the requirements outlined in the checklist prior to being accepted into the spec. This checklist is a bit long, but aims to reduce the number of follow-on MSCs after a feature lands. SCT members: please check off things you check for, and raise a concern against FCP if the checklist is incomplete. If an item doesn't apply, prefer to check it rather than remove it. Unchecking items is encouraged where applicable. Checklist:
|
@mscbot fcp merge |
Team member @turt2live has proposed to merge this. The next step is review by the rest of the tagged people: Once at least 75% of reviewers approve (and there are no outstanding concerns), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for information about what commands tagged team members can give me. |
**Note**: in this example, the server has not registered the locale-specific values for `client_name`, `tos_uri`, and `policy_uri`, which is why they are not present in the response. The server also does not support the `urn:ietf:params:oauth:grant-type:token-exchange` grant type, which is why it is not present in the response. | ||
|
||
The client MUST store the `client_id` for future use. | ||
It SHOULD reuse the `client_id` for all future authorization requests done against the same homeserver. |
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.
So to be clear, the client should associate the client_id
to the server name of the homeserver? The issuer
field from RFC8414 is ignored, since it doesn't appear to be mentioned in any MSC?
Won't that complicate the migration of current implementations that should have associated their client_id
to the issuer
?
#### Localizable metadata | ||
|
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.
This section appears to be empty?
#### Redirect URI validation | ||
|
||
The redirect URI plays a critical role in validating the authenticity of the client. | ||
The client 'proves' its identity by demonstrating that it controls the redirect URI. | ||
This is why it is critical to have strict validation of the redirect URI. | ||
|
||
The `application_type` metadata is used to determine the type of client. | ||
It defaults to `web` if not present, and can be set to `native` to indicate that the client is a native application. | ||
|
||
In all cases, the redirect URI MUST not have a fragment component. | ||
|
||
#### Web clients | ||
|
||
`web` clients can use redirect URIs that: | ||
|
||
- MUST use the `https` scheme | ||
- MUST omit the port (to use the default port for https: 443) | ||
- MUST not use a user or password in the authority component of the URI | ||
- MUST use the client URI as a common base for the authority component | ||
|
||
Examples of valid redirect URIs (with `https://example.com/` as the client URI): | ||
|
||
- `https://example.com/callback` | ||
- `https://app.example.com/callback` | ||
- `https://example.com/?query=value` | ||
|
||
Examples of invalid redirect URIs (with `https://example.com/` as the client URI): | ||
|
||
- `https://example.com/callback#fragment` | ||
- `https://example.com:8080/callback` | ||
- `http://example.com/callback` | ||
- `http://localhost/` | ||
|
||
#### Native clients | ||
|
||
`native` clients can use three types of redirect URIs: | ||
|
||
1. Private-Use URI Scheme: | ||
- the scheme MUST be prefixed with the client URI hostname in reverse-DNS notation. For example, if the client URI is `https://example.com/`, then a valid custom URI scheme would be `com.example.app:/`. | ||
- the URI MUST not have an authority component. This means that it MUST have either a single slash or none immediately following the scheme, with no hostname, username, or port. | ||
2. "http" URIs on the loopback interface: | ||
- it MUST use the `http` scheme | ||
- the host part MUST be `localhost`, `127.0.0.1`, or `[::1]` | ||
- it MUST have no port registered. The homeserver MUST then accept any port number during the authorization flow. | ||
3. Claimed "https" Scheme URI: | ||
- some operating systems allow apps to claim "https" scheme URIs in the domains they control | ||
- when the browser encounters a claimed URI, instead of the page being loaded in the browser, the native app is launched with the URI supplied as a launch parameter | ||
- the same rules as for `web` clients apply | ||
|
||
These restrictions are the same as defined by [RFC8252 sec. 7](https://tools.ietf.org/html/rfc8252#section-7). | ||
|
||
Examples of valid redirect URIs (with `https://example.com/` as the client URI): | ||
|
||
- `com.example.app:/callback` | ||
- `com.example:/` | ||
- `com.example:callback` | ||
- `http://localhost/callback` | ||
- `http://127.0.0.1/callback` | ||
- `http://[::1]/callback` | ||
|
||
Examples of invalid redirect URIs (with `https://example.com/` as the client URI): | ||
|
||
- `example:/callback` | ||
- `com.example.app://callback` | ||
- `https://localhost/callback` | ||
- `http://localhost:1234/callback` |
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.
If these three sections are all about redirect URIs, it would be really helpful to say so. I was expecting a section called "Web clients" to be more general.
This proposal is part of the broader [MSC3861: Next-generation auth for Matrix, based on OAuth 2.0/OIDC](https://github.com/matrix-org/matrix-spec-proposals/pull/3861). | ||
|
||
This MSC specifies how Matrix clients SHOULD leverage the OAuth 2.0 Dynamic Client Registration Protocol ([RFC 7591](https://tools.ietf.org/html/rfc7591)) to register themselves before initiating an authorization flow. | ||
|
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.
I'd find it really helpful to give a one-line summary of how this MSC fits in with the others, and also explain, to those of us less familiar with RFC7591, how often we expect clients to hit this endpoint.
In brief, once a client has obtained the homeserver's OAuth 2.0 metadata per [MSC2965](https://github.com/matrix-org/matrix-spec-proposals/pull/2965), and before it can initiate an authorization request per [MSC2964](https://github.com/matrix-org/matrix-spec-proposals/pull/2964), the client must register itself with that homeserver to obtain a `client_id`. | |
It stores this `client_id` and uses it for all future authorization requests against the homeserver. | |
(There is something to this effect in the "client metadata" section, but it feels a bit lost in there, given that's just one of three h3-level sections in the proposal)
### Dynamic client registration | ||
|
||
Before initiating an authorization flow, the client MUST advertise its metadata to the homeserver to get back a `client_id`. | ||
|
||
This is done through the `registration_endpoint` as described by [RFC7591 sec. 3](https://tools.ietf.org/html/rfc7591#section-3). | ||
|
||
To register, the client sends an HTTP POST to the `registration_endpoint` with its metadata as JSON in the body. | ||
For example, the client could send the following registration request: | ||
|
||
```http | ||
POST /register HTTP/1.1 | ||
Content-Type: application/json | ||
Accept: application/json | ||
Server: auth.example.com | ||
``` | ||
|
||
```json | ||
{ | ||
"client_name": "My App", | ||
"client_name#fr": "Mon application", | ||
"client_uri": "https://example.com/", | ||
"logo_uri": "https://example.com/logo.png", | ||
"tos_uri": "https://example.com/tos.html", | ||
"tos_uri#fr": "https://example.com/fr/tos.html", | ||
"policy_uri": "https://example.com/policy.html", | ||
"policy_uri#fr": "https://example.com/fr/policy.html", | ||
"redirect_uris": ["https://app.example.com/callback"], | ||
"token_endpoint_auth_method": "none", | ||
"response_types": ["code"], | ||
"grant_types": [ | ||
"authorization_code", | ||
"refresh_token", | ||
"urn:ietf:params:oauth:grant-type:token-exchange" | ||
], | ||
"application_type": "web" | ||
} | ||
``` | ||
|
||
The server replies with a JSON object containing the `client_id` allocated, as well as all the metadata values that the server registered. | ||
It MUST ignore fields, `grant_types` and `response_types` that are not understood by the server. | ||
|
||
With the previous registration request, the server would reply with: | ||
|
||
```json | ||
{ | ||
"client_id": "s6BhdRkqt3", | ||
"client_name": "My App", | ||
"client_uri": "https://example.com/", | ||
"logo_uri": "https://example.com/logo.png", | ||
"tos_uri": "https://example.com/tos.html", | ||
"policy_uri": "https://example.com/policy.html", | ||
"redirect_uris": ["https://app.example.com/callback"], | ||
"response_types": ["code"], | ||
"grant_types": ["authorization_code", "refresh_token"], | ||
"application_type": "web" | ||
} | ||
``` | ||
|
||
**Note**: in this example, the server has not registered the locale-specific values for `client_name`, `tos_uri`, and `policy_uri`, which is why they are not present in the response. The server also does not support the `urn:ietf:params:oauth:grant-type:token-exchange` grant type, which is why it is not present in the response. | ||
|
||
The client MUST store the `client_id` for future use. | ||
It SHOULD reuse the `client_id` for all future authorization requests done against the same homeserver. |
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.
Personally, I'd find this proposal way easier to grok if the API was defined before the relevant metadata (with words like: "the client MUST advertise its metadata to the homeserver to get back a client_id
. The metadata is as descripted under 'Client metadata' below").
Otherwise I have to read all through the "metadata" section before I get an answer to "ok, but what is all this stuff actually for?"
Not insisting on a change here, but just thinking it might help future readers.
With the previous registration request, the server would reply with: | ||
|
||
```json | ||
{ | ||
"client_id": "s6BhdRkqt3", | ||
"client_name": "My App", | ||
"client_uri": "https://example.com/", | ||
"logo_uri": "https://example.com/logo.png", | ||
"tos_uri": "https://example.com/tos.html", | ||
"policy_uri": "https://example.com/policy.html", | ||
"redirect_uris": ["https://app.example.com/callback"], | ||
"response_types": ["code"], | ||
"grant_types": ["authorization_code", "refresh_token"], | ||
"application_type": "web" | ||
} | ||
``` |
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.
I notice that RFC7591 specifies client_secret
as an optional field here. Do we want to allow matrix homeservers to return a client_secret
and expect clients to know what to do with it? I guess the answer is yes, given RFC7591 says so, but it wouldn't hurt to be explicit.
#### `client_uri` and relationship with other URIs | ||
|
||
Per [RFC 7591](https://tools.ietf.org/html/rfc7591), the `client_uri` MUST be a valid URL that SHOULD give the user more information about the client. | ||
This URL SHOULD NOT require authentication to access. |
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.
is client_uri
required?
If not, some "If present" wording would be helpful, and explain how the constraints on the other URIs work when it is absent.
If it is required, call it out explicitly, and maybe say what should happen if it's not there?
The `application_type` metadata is used to determine the type of client. | ||
It defaults to `web` if not present, and can be set to `native` to indicate that the client is a native application. | ||
|
||
In all cases, the redirect URI MUST not have a fragment component. |
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.
In all cases, the redirect URI MUST not have a fragment component. | |
In all cases, the redirect URI MUST NOT have a fragment component. |
|
||
1. Private-Use URI Scheme: | ||
- the scheme MUST be prefixed with the client URI hostname in reverse-DNS notation. For example, if the client URI is `https://example.com/`, then a valid custom URI scheme would be `com.example.app:/`. | ||
- the URI MUST not have an authority component. This means that it MUST have either a single slash or none immediately following the scheme, with no hostname, username, or port. |
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.
- the URI MUST not have an authority component. This means that it MUST have either a single slash or none immediately following the scheme, with no hostname, username, or port. | |
- the URI MUST NOT have an authority component. This means that it MUST have either a single slash or none immediately following the scheme, with no hostname, username, or port. |
2. "http" URIs on the loopback interface: | ||
- it MUST use the `http` scheme | ||
- the host part MUST be `localhost`, `127.0.0.1`, or `[::1]` | ||
- it MUST have no port registered. The homeserver MUST then accept any port number during the authorization flow. |
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.
- it MUST have no port registered. The homeserver MUST then accept any port number during the authorization flow. | |
- it (the registered redirect URI) MUST NOT contain a port number. The homeserver MUST then accept any port number during the authorization flow. |
## Alternatives | ||
|
||
An alternative approach would be to have the client host a JSON file containing its metadata and use that URL as the `client_id`. | ||
This is what the following [*OAuth Client ID Metadata Document* draft](https://datatracker.ietf.org/doc/html/draft-parecki-oauth-client-id-metadata-document) proposes. |
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.
This is what the following [*OAuth Client ID Metadata Document* draft](https://datatracker.ietf.org/doc/html/draft-parecki-oauth-client-id-metadata-document) proposes. | |
This is what the [*OAuth Client ID Metadata Document* draft](https://datatracker.ietf.org/doc/html/draft-parecki-oauth-client-id-metadata-document) proposes. |
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.
or:
This is what the following [*OAuth Client ID Metadata Document* draft](https://datatracker.ietf.org/doc/html/draft-parecki-oauth-client-id-metadata-document) proposes. | |
This is what the following document proposes: [*OAuth Client ID Metadata Document* draft](https://datatracker.ietf.org/doc/html/draft-parecki-oauth-client-id-metadata-document). |
Rendered
Dependencies:
SCT stuff:
checklist
FCP tickyboxes