Skip to content

Add OAuth 2.0 dynamic client registration #2148

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
merged 8 commits into from
Jun 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelogs/client_server/newsfragments/2148.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the OAuth 2.0 based authentication API, as per [MSC3861](https://github.com/matrix-org/matrix-spec-proposals/pull/3861) and its sub-proposals.
203 changes: 203 additions & 0 deletions content/client-server-api/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1485,6 +1485,209 @@ MAY reject weak passwords with an error code `M_WEAK_PASSWORD`.

{{% http-api spec="client-server" api="oauth_server_metadata" %}}

#### Client registration

Before being able to use the authorization flow to obtain an access token, a
client needs to obtain a `client_id` by registering itself with the server.

This should be done via OAuth 2.0 Dynamic Client Registration as defined in
[RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591).

##### Client metadata

In OAuth 2.0, clients register a set of metadata values with the authorization
server, which associates it with a newly generated `client_id`. These values are
used to describe the client to the user and define how the client interacts with
the server.

{{% definition path="schemas/oauth2-client-metadata" %}}

###### Metadata localization

As per [RFC 7591 section 2.2](https://tools.ietf.org/html/rfc7591#section-2.2),
all the human-readable metadata values MAY be localized.

The human-readable values include:
- `client_name`
- `logo_uri`
- `tos_uri`
- `policy-uri`

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"
}
```

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

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 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, as
defined previously.
- MAY include an `application/x-www-form-urlencoded` formatted query component.

For example, with `https://example.com/` as the client URI, the following are
valid redirect URIs:
- `https://example.com/callback`
- `https://app.example.com/callback`
- `https://example.com:5173/?query=value`

With the same client URI, the following are invalid redirect URIs:
- `https://example.com/callback#fragment`
- `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:/`.
- There MUST NOT be an authority component. This means that the URI MUST have
either a single slash or none immediately following the scheme, with no
hostname, username, or port.
2. **`http` URI on the loopback interface**
- The scheme MUST be `http`.
- The host part MUST be `localhost`, `127.0.0.1`, or `[::1]`.
- There MUST NOT be a port. 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 [RFC 8252 section 7](https://tools.ietf.org/html/rfc8252#section-7).

For example, with `https://example.com/` as the client URI,

These are valid redirect URIs:
- `com.example.app:/callback`
- `com.example:/`
- `com.example:callback`
- `http://localhost/callback`
- `http://127.0.0.1/callback`
- `http://[::1]/callback`

These are invalid redirect URIs:
- `example:/callback`
- `com.example.app://callback`
- `https://localhost/callback`
- `http://localhost:1234/callback`

##### Dynamic client registration flow

To register, the client sends an HTTP `POST` request to the
`registration_endpoint`, which can be found in the [server metadata](#server-metadata-discovery).
The body of the request is the JSON-encoded [`OAuthClientMetadata`](#client-metadata).

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"
}
```

Upon successful registration, the server replies with an `HTTP 201 Created`
response, with a JSON object containing the allocated `client_id` and all the
registered metadata values.

With the registration request above, the server might 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"],
"token_endpoint_auth_method": "none",
"response_types": ["code"],
"grant_types": ["authorization_code", "refresh_token"],
"application_type": "web"
}
```

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.

To avoid the number of client registrations growing over time, the server MAY
choose to delete client registrations that don't have an active session. The
server MUST NOT delete client registrations that have an active session.

Clients MUST perform a new client registration at the start of each
authorization flow.

{{% boxes/note %}}
Because each client on each user device will do its own registration, they may
all have different `client_id`s. This means that the server may store the same
client registration multiple times, which could lead to a large number of client
registrations.

This can be mitigated by de-duplicating client registrations that have identical
metadata. By doing so, different users on different devices using the same
client can share a single `client_id`, reducing the overall number of
registrations.
{{% /boxes/note %}}

#### Scope

The client requests a scope in the OAuth 2.0 authorization flow, which is then
Expand Down
140 changes: 140 additions & 0 deletions data/schemas/oauth2-client-metadata.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Copyright 2025 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
title: OAuthClientMetadata
type: object
description: |-
This definition of the metadata specifies only the fields that are meaningful
in the context of the Matrix specification. All the possible values are
registered in the [OAuth Dynamic Client Registration Metadata registry](https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#client-metadata),
and normative definitions of them are available in their respective RFCs.
properties:
client_uri:
type: string
format: uri
description: |-
A URL to a valid web page that SHOULD give the user more information about
the client.

This URL MUST use the `https` scheme and SHOULD NOT require authentication
to access. It MUST NOT use a user or password in the authority component
of the URI.

The server MAY reject client registrations if this field is invalid or
missing.

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`. The port number, path and query components MAY be different.

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`.
client_name:
type: string
description: |-
Human-readable name of the client to be presented to the user.

This field can be [localized](/client-server-api/#metadata-localization).
logo_uri:
type: string
format: uri
description: |-
URL that references a logo for the client.

This URL MUST use the `https` scheme.

This field can be [localized](/client-server-api/#metadata-localization).
tos_uri:
type: string
format: uri
description: |-
URL that points to a human-readable terms of service document for the
client.

This URL MUST use the `https` scheme and SHOULD NOT require authentication
to access. It MUST NOT use a user or password in the authority component
of the URI.

If this field is set, the server SHOULD show or link to this URL.

This field can be [localized](/client-server-api/#metadata-localization).
policy_uri:
type: string
format: uri
description: |-
URL that points to a human-readable policy document for the client.

This URL MUST use the `https` scheme and SHOULD NOT require authentication
to access. It MUST NOT use a user or password in the authority component
of the URI.

If this field is set, the server SHOULD show or link to this URL.

This field can be [localized](/client-server-api/#metadata-localization).
redirect_uris:
type: array
description: |-
Array of redirection URIs for use in redirect-based flows.

At least one URI is required to use the authorization code grant.

The server MUST perform [validation on redirect URIs](/client-server-api/#redirect-uri-validation).
items:
type: string
format: uri
description: A redirection URI.
response_types:
type: array
description: |-
Array of the OAuth 2.0 response types that the client may use.

This MUST include the `code` value to use the authorization code grant.

The server MUST ignore values that it does not understand.
items:
type: string
description: A response type that the client may use.
grant_types:
type: array
description: |-
Array of the OAuth 2.0 grant types that the client may use.

This MUST include:
- the `authorization_code` value to use the authorization code grant,
- the `refresh_token` value to use the refresh token grant.

The server MUST ignore values that it does not understand.
items:
type: string
description: A grant type that the client may use.
token_endpoint_auth_method:
type: string
description: |-
String indicator of the requested authentication method for the token
endpoint.

The homeserver MUST support the `none` value, as most Matrix clients are
client-side only, do not have a server component, and therefore are public
clients.
application_type:
type: string
description: |-
Kind of the application.

The homeserver MUST support the `web` and `native` values to be able to
perform [redirect URI validation](/client-server-api/#redirect-uri-validation).

Defaults to `web` if omitted.
required:
- client_uri