Compare commits

...

3 commits

Author SHA1 Message Date
Kévin Commaille 66d5a66b1f
Merge ed1b3d7d57 into 9244c84a32 2025-06-18 10:06:19 +00:00
Kévin Commaille ed1b3d7d57
Apply review suggestions
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2025-06-18 12:06:00 +02:00
Kévin Commaille 9244c84a32
Add OAuth 2.0 dynamic client registration (#2148)
Some checks failed
Spec / 🔎 Validate OpenAPI specifications (push) Has been cancelled
Spec / 🔎 Check Event schema examples (push) Has been cancelled
Spec / 🔎 Check OpenAPI definitions examples (push) Has been cancelled
Spec / 🔎 Check JSON Schemas inline examples (push) Has been cancelled
Spec / ⚙️ Calculate baseURL for later jobs (push) Has been cancelled
Spec / 📢 Run towncrier for changelog (push) Has been cancelled
Spell Check / Spell Check with Typos (push) Has been cancelled
Spec / 🐍 Build OpenAPI definitions (push) Has been cancelled
Spec / 📖 Build the spec (push) Has been cancelled
Spec / 🔎 Validate generated HTML (push) Has been cancelled
Spec / 📖 Build the historical backup spec (push) Has been cancelled
As per MSC2966

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2025-06-18 10:12:48 +01:00
3 changed files with 361 additions and 39 deletions

View file

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

View file

@ -292,7 +292,7 @@ and the two requests would be considered distinct because the two are
considered separate endpoints. Similarly, if a client logs out and back in
between two requests using the same transaction ID, the requests are distinct
because the act of logging in and out creates a new device (unless an existing
`device_id` is passed during the [login](#login) process). On the other hand, if
`device_id` is given during the [login](#login) process). On the other hand, if
a client re-uses a transaction ID for the same endpoint after
[refreshing](#refreshing-access-tokens) an access token, it will be assumed to
be a duplicate request and ignored. See also
@ -435,7 +435,7 @@ endpoints it supports.
## Client Authentication
{{% changed-in v="1.15" %}}
{{% changed-in v="1.15" %}} OAuth 2.0 API added to the specification.
Most API endpoints require the user to identify themselves by presenting
previously obtained credentials in the form of an access token.
@ -461,9 +461,9 @@ while the OAuth 2.0 API has been introduced to rely on a industry standard and
its experience rather than implementing a custom protocol that might not follow
the best practices.
A homeserver may support one of those two APIs, or both. Both APIs are
incompatible, which means that after logging in, clients MUST only use the API
that was used to obtain their current access token.
A homeserver may support one of those two APIs, or both. The two APIs are
mutually incompatible, which means that after logging in, clients MUST only use
the API that was used to obtain their current access token.
{{% boxes/note %}}
Currently the OAuth 2.0 API doesn't cover all the use cases of the legacy API,
@ -471,14 +471,6 @@ like ones that don't rely on a web browser for automation or for endpoints used
by application services that depend on the [UIA API](#user-interactive-authentication-api).
{{% /boxes/note %}}
{{% boxes/note %}}
[MSC3824](https://github.com/matrix-org/matrix-spec-proposals/pull/3824)
specifies a way for servers to implement the OAuth 2.0 API while staying
backwards-compatible with existing clients by using the [`m.login.sso`](#sso-client-loginauthentication)
method, and for clients to improve their compatibility with the OAuth 2.0 API
with minimal changes.
{{% /boxes/note %}}
### Authentication API discovery
To discover if a homeserver supports the legacy API, the [`GET /login`](#get_matrixclientv3login)
@ -509,8 +501,8 @@ endpoint.
With the OAuth 2.0 API, a client can obtain an access token by using one of the
[grant types](#grant-types) supported by the homeserver and authorizing the
proper [scope](#scope). To invalidate the access token the client must use
[token revocation](#token-revocation).
proper [scope](#scope), as demonstrated in the [login flow](#login-flow). To
invalidate the access token the client must use [token revocation](#token-revocation).
### Using access tokens
@ -557,8 +549,8 @@ used to generate a new access token and refresh token, the new access
and refresh tokens are now bound to the device associated with the
initial refresh token.
During login or registration, the access token should be associated to a
`device_id`. The legacy [Login](#legacy-login) and [Registration](#legacy-account-registration)
During login or registration, the generated access token should be associated
with a `device_id`. The legacy [Login](#legacy-login) and [Registration](#legacy-account-registration)
processes auto-generate a new `device_id`, but a client is also free to provide
its own `device_id`. With the OAuth 2.0 API, the `device_id` is always provided
by the client. The client can generate a new `device_id` or, provided the user
@ -603,8 +595,9 @@ anyways and rely on soft logout behaviour on clients that don't support
refreshing.
With the OAuth 2.0 API, refreshing access tokens is done with the [refresh token
grant type](#refresh-token-grant-type). Support for refreshing access tokens is
mandatory with this API.
grant type](#refresh-token-grant-type), as demonstrated in the [token refresh
flow](#token-refresh-flow). Support for refreshing access tokens is mandatory
with this API.
### Soft logout
@ -639,22 +632,13 @@ manage their account like [changing their password](#password-management),
With the OAuth 2.0 API, all account management is done via the homeserver's web
UI.
{{% boxes/note %}}
[MSC4191](https://github.com/matrix-org/matrix-spec-proposals/pull/4191)
provides a way for homeservers to provide the URL of their account management UI,
which can be used by clients to redirect the users to the relevant part of the
interface depending on the action that the user wishes to take.
{{% /boxes/note %}}
### Legacy API
{{% changed-in v="1.15" %}}
This is the first authentication API that was introduced since the first version
This is the original authentication API that was introduced in the first version
of the Client-Server specification and uses custom APIs. Contrary to the OAuth
2.0 API, account management is primarily done in the client's interface and as
such it might not require the end user to be redirected to a web UI in their
browser.
such it does not usually require the end user to be redirected to a web UI in
their browser.
#### User-Interactive Authentication API
@ -1596,26 +1580,223 @@ because they don't have access to the user's credentials anymore.
The [User-Interactive Authentication API](#user-interactive-authentication-api)
is not compatible with the OAuth 2.0 API, so the endpoints that depend on it for
authentication can't be used when an access token is obtained with this API.
Homeservers may provide alternatives to those endpoints in their web UI (like
[MSC4191](https://github.com/matrix-org/matrix-spec-proposals/pull/4191) for
account management), or disable UIA in certain circumstances (like
[MSC4190](https://github.com/matrix-org/matrix-spec-proposals/pull/4190) for
managing devices for application services).
{{% /boxes/warning %}}
**Sample flow**
1. [Discover the OAuth 2.0 server metadata](#server-metadata-discovery).
2. [Register the client with the homeserver](#client-registration).
3. Obtain an access token by authorizing a [scope](#scope) for the client with the [authorization code grant](#authorization-code-grant).
4. Refresh the access token with the [refresh token grant](#refresh-token-grant) when it expires.
3. [Obtain an access token](#login-flow) by authorizing a [scope](#scope) for the client with the [authorization code grant](#authorization-code-grant).
4. [Refresh the access token](#token-refresh-flow) with the [refresh token grant](#refresh-token-grant) when it expires.
5. [Revoke the tokens](#token-revocation) when the users wants to log out of the client.
#### Server metadata discovery
{{% 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

View file

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