-
Notifications
You must be signed in to change notification settings - Fork 401
MSC2966: Usage of OAuth 2.0 Dynamic Client Registration in Matrix #2966
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
Merged
turt2live
merged 35 commits into
matrix-org:main
from
sandhose:msc/sandhose/oauth2-dynamic-registration
Apr 5, 2025
Merged
Changes from 15 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
1b1bcdd
OAuth 2.0 Dynamic Registration MSC
sandhose d923a45
contacts is required non-empty
hughns 2bdcc4a
Make client_uri mandatory
hughns d4caa5a
Merge branch 'matrix-org:main' into msc/sandhose/oauth2-dynamic-regis…
sandhose 32c9ead
Rework MSC
sandhose 1ba6ce9
Mention the `token_endpoint_auth_method` client metadata
sandhose 6eb5fdf
Update proposals/2966-oauth2-dynamic-registration.md
sandhose a67a2e8
State that the homeserver should display the tos_uri and policy_uri
sandhose d9fd175
Make the wording for the refresh token clearer
sandhose a7ddac2
Clarify that native callbacks with no slashes are allowed
sandhose 4deb3c8
Give an example where the server ignores an unsupported grant type
sandhose 0e2f0f1
Add security considerations
sandhose 2a20e6d
must -> MUST, should -> SHOULD, may -> MAY
sandhose c7e55ec
Clarify the client should store the client_id
sandhose fe4ef69
Simplify definition of client_uri, already covered by the RFC
sandhose 24d0ab8
Explain the point of the MSC earlier
sandhose fae9c29
Remove empty section
sandhose f3d30e9
Explicitly state that the client_uri is required
sandhose a65bd6b
Apply suggestions from code review
sandhose 1768d54
Fix the web/native client sub-sub-sub sections
sandhose a98b2ba
Clarify the localhost port-less redirect URIs
sandhose 6e32a71
The server should return a 201 on successful registration
sandhose 8b2f387
Explain better the restrictions on URIs
sandhose 03ba6f2
Allow custom ports in the redirect URI
sandhose 6b03a53
Client regs won't grow exponentially
sandhose b6abea5
Explain how to mitigate the problem of client registrations growing o…
sandhose 650a449
Add missing metadata in the dynamic registration response
sandhose 15a3c87
Make 'metadata localization' its own sub-sub-sub-sub-section
sandhose 4d7ff6c
Server may still deduplicate registrations
sandhose a0e8b37
Suggest different strategies to mitigate the growing number of client…
sandhose 1f489ee
Let the server delete client registrations that have no active sessions
sandhose ee36393
Really, shoud MUST do a new client reg
sandhose 55433db
Make sure the summary doesn't sound authoritative
sandhose 70f017c
Put the links at the end of the file
sandhose dbb6163
Explain what is Matrix-specific, what is not
sandhose File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
# MSC2966: Usage of OAuth 2.0 Dynamic Client Registration in Matrix | ||
|
||
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. | ||
clokep 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. Not to be a stickler, but I think having this 'should' in formal capitals technically implies the whole MSC is optional, which is probably not what you want. |
||
|
||
sandhose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
## Proposal | ||
|
||
### Prerequisites | ||
|
||
This proposal requires the client to know the following authorization server metadata about the homeserver: | ||
|
||
- `registration_endpoint`: the URL where the client is able to register itself. | ||
|
||
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). | ||
turt2live marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### Client metadata | ||
|
||
In OAuth 2.0, clients have a set of metadata values associated with their client identifier at an authorization server. | ||
These values are used to describe the client to the user and define how the client interacts with the authorization server. | ||
|
||
This MSC specifies what metadata values are required by the Matrix specification and how a client can register itself with a Matrix homeserver to get a client identifier. | ||
|
||
The metadata names are registered in the IANA [OAuth Dynamic Client Registration Metadata](https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#client-metadata) registry, and normative definitions of them are available in their respective RFCs in the registry. | ||
|
||
#### Localizable metadata | ||
|
||
erikjohnston marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#### `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. | ||
erikjohnston marked this conversation as resolved.
Show resolved
Hide resolved
|
||
This URL SHOULD NOT require authentication to access. | ||
richvdh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
This URI is a common base for all the other URIs in the metadata: those MUST be either on the same host or on a subdomain of the host of the `client_uri`. | ||
For example, if the `client_uri` is `https://example.com/`, then one of the `redirect_uris` can be `https://example.com/callback` or `https://app.example.com/callback`, but not `https://app.com/callback`. | ||
erikjohnston marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### User-visible metadata values | ||
|
||
The following metadata values SHOULD be used by clients to help users identify the client: | ||
|
||
- `client_name`: Human-readable name of the client to be presented to the user | ||
- `logo_uri`: URL that references a logo for the client | ||
- `tos_uri`: URL that points to a human-readable terms of service document for the client | ||
- `policy_uri`: URL that points to a human-readable policy document for the client | ||
turt2live marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
All the URIs MUST use the `https` scheme and use the `client_uri` as a common base. | ||
|
||
If provided by the client, the homeserver SHOULD show or link to the `tos_uri` and `policy_uri` to the user. | ||
sandhose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
All of these metadata values are optional. | ||
|
||
As per [RFC 7591 sec. 2.2](https://tools.ietf.org/html/rfc7591#section-2.2), these metadata values MAY be localized. | ||
sandhose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
For example: | ||
|
||
```json | ||
{ | ||
"client_name": "Digital mailbox", | ||
"client_name#en-US": "Digital mailbox", | ||
"client_name#en-GB": "Digital postbox", | ||
"client_name#fr": "Boîte aux lettres numérique", | ||
"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" | ||
} | ||
``` | ||
|
||
#### Metadata values required by the OAuth 2.0 authorization grant flow | ||
|
||
The following metadata values are required to be present to use the OAuth 2.0 authorization code grant and refresh token grant, as described in [MSC2964]: | ||
|
||
- `redirect_uris`: Array of redirection URIs for use in redirect-based flows | ||
- `response_types`: Array of the OAuth 2.0 response types that the client may use | ||
- `grant_types`: Array of OAuth 2.0 grant types that the client may use | ||
- `token_endpoint_auth_method`: String indicator of the requested authentication method for the token endpoint | ||
|
||
The homeserver MUST support the `none` value for the `token_endpoint_auth_method`, as most Matrix clients are client-side only, do not have a server component, and therefore are public clients. | ||
|
||
To use this grant: | ||
|
||
- the `redirect_uris` MUST have at least one value | ||
- the `response_types` MUST include `code` | ||
- the `grant_types` MUST include `authorization_code` and `refresh_token` | ||
|
||
#### 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. | ||
sandhose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### 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) | ||
sandhose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- 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. | ||
sandhose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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. | ||
sandhose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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` | ||
sandhose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### 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. | ||
sandhose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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" | ||
} | ||
``` | ||
richvdh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
**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. | ||
sandhose marked this conversation as resolved.
Show resolved
Hide resolved
richvdh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Potential issues | ||
|
||
Because each client on each user device will do its own registration, they will all have different `client_id`s. | ||
sandhose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
This means that the number of client registrations will grow exponentially. | ||
sandhose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
A subsequent MSC could be proposed to identify multiple instances of the same client using signed client metadata. | ||
sandhose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## 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. | ||
sandhose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
This approach has the advantage of being able to use the same `client_id` for different instances of the same client, but it has the disadvantage of requiring the client to host a JSON file on its own domain, as well as difficulties in handling updates to the metadata. | ||
erikjohnston marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Security considerations | ||
|
||
The restrictions on the metadata values laid out in this MSC are a best effort to prevent client impersonation, but they are not flawless. | ||
|
||
For web clients, it relies on the client's ability to prove ownership of the redirect URI, which can be guaranteed to some extent by sane DNS management and its use of TLS. | ||
If a client-related domain name hosts an open redirector, it could be used to impersonate the client. | ||
|
||
For native clients, because they can use private-use URI schemes and localhost redirectors, it relies more on the underlying operating system's security model and their application distribution model. | ||
A good example of this is if a mobile client distributed through an app store registers the `app.acme.corp:` scheme in an effort to impersonate "ACME Corp's" app, then "ACME Corp" would have a valid case to take down the malicious app from the app store. | ||
erikjohnston marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
In both cases, it is crucial for the server to strictly enforce these restrictions and to show as much information about the client as possible to the user so they can make an informed decision. | ||
|
||
## Unstable prefix | ||
|
||
None relevant. | ||
|
||
[RFC7591]: https://tools.ietf.org/html/rfc7591 | ||
[MSC2964]: https://github.com/matrix-org/matrix-spec-proposals/pull/2964 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
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:
web
flownative
flowThere 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:
Known working clients with the native flow:
Server supporting both:
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: