Skip to content

Commit 5ce58e2

Browse files
committed
Add OAuth 2.0 dynamic client registration
As per MSC2966 Signed-off-by: Kévin Commaille <[email protected]>
1 parent eea7089 commit 5ce58e2

File tree

2 files changed

+342
-0
lines changed

2 files changed

+342
-0
lines changed

content/client-server-api/_index.md

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1481,6 +1481,208 @@ MAY reject weak passwords with an error code `M_WEAK_PASSWORD`.
14811481

14821482
### OAuth 2.0 API
14831483

1484+
#### Client registration
1485+
1486+
Before being able to use the authorization flow to obtain an access token, a
1487+
client needs to obtain a `client_id` by registering itself with the server.
1488+
1489+
One way to do that is to leverage OAuth 2.0 Dynamic Client Registration as
1490+
defined in [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591).
1491+
1492+
##### Client metadata
1493+
1494+
In OAuth 2.0, clients register a set of metadata values with the authorization
1495+
server which associates it to a newly generated `client_id`. These values are
1496+
used to describe the client to the user and define how the client interacts with
1497+
the server.
1498+
1499+
{{% json-schema name="oauth2-client-metadata" %}}
1500+
1501+
###### Metadata localization
1502+
1503+
As per [RFC 7591 section 2.2](https://tools.ietf.org/html/rfc7591#section-2.2),
1504+
all the human-readable metadata values MAY be localized.
1505+
1506+
The human-readable values include:
1507+
- `client_name`
1508+
- `logo_uri`
1509+
- `tos_uri`
1510+
- `policy-uri`
1511+
1512+
For example:
1513+
1514+
```json
1515+
{
1516+
"client_name": "Digital mailbox",
1517+
"client_name#en-US": "Digital mailbox",
1518+
"client_name#en-GB": "Digital postbox",
1519+
"client_name#fr": "Boîte aux lettres numérique",
1520+
"tos_uri": "https://example.com/tos.html",
1521+
"tos_uri#fr": "https://example.com/fr/tos.html",
1522+
"policy_uri": "https://example.com/policy.html",
1523+
"policy_uri#fr": "https://example.com/fr/policy.html"
1524+
}
1525+
```
1526+
1527+
###### Redirect URI validation
1528+
1529+
The redirect URI plays a critical role in validating the authenticity of the
1530+
client. The client "proves" its identity by demonstrating that it controls the
1531+
redirect URI. This is why it is critical to have strict validation of the
1532+
redirect URI.
1533+
1534+
The `application_type` metadata is used to determine the type of client.
1535+
1536+
In all cases, the redirect URI MUST NOT have a fragment component.
1537+
1538+
**Web clients**
1539+
1540+
`web` clients can use redirect URIs that:
1541+
1542+
- MUST use the `https` scheme
1543+
- MUST NOT use a user or password in the authority component of the URI
1544+
- MUST use the client URI as a common base for the authority component, as
1545+
defined previously
1546+
- MAY include an `application/x-www-form-urlencoded` formatted query component
1547+
1548+
For example, with `https://example.com/` as the client URI,
1549+
1550+
These are valid redirect URIs:
1551+
- `https://example.com/callback`
1552+
- `https://app.example.com/callback`
1553+
- `https://example.com:5173/?query=value`
1554+
1555+
These are invalid redirect URIs:
1556+
- `https://example.com/callback#fragment`
1557+
- `http://example.com/callback`
1558+
- `http://localhost/`
1559+
1560+
**Native clients**
1561+
1562+
`native` clients can use three types of redirect URIs:
1563+
1564+
1. Private-Use URI Scheme:
1565+
- the scheme MUST be prefixed with the client URI hostname in reverse-DNS
1566+
notation. For example, if the client URI is `https://example.com/`, then a
1567+
valid custom URI scheme would be `com.example.app:/`.
1568+
- the URI MUST NOT have an authority component. This means that it MUST have
1569+
either a single slash or none immediately following the scheme, with no
1570+
hostname, username, or port.
1571+
2. `http` URI on the loopback interface:
1572+
- it MUST use the `http` scheme
1573+
- the host part MUST be `localhost`, `127.0.0.1`, or `[::1]`
1574+
- it MUST have no port. The homeserver MUST then accept any port number during
1575+
the authorization flow.
1576+
3. Claimed `https` Scheme URI: some operating systems allow apps to claim
1577+
`https` scheme URIs in the domains they control. When the browser encounters a
1578+
claimed URI, instead of the page being loaded in the browser, the native app
1579+
is launched with the URI supplied as a launch parameter. The same rules as for
1580+
`web` clients apply.
1581+
1582+
These restrictions are the same as defined by [RFC 8252 section 7](https://tools.ietf.org/html/rfc8252#section-7).
1583+
1584+
For example, with `https://example.com/` as the client URI,
1585+
1586+
These are valid redirect URIs:
1587+
- `com.example.app:/callback`
1588+
- `com.example:/`
1589+
- `com.example:callback`
1590+
- `http://localhost/callback`
1591+
- `http://127.0.0.1/callback`
1592+
- `http://[::1]/callback`
1593+
1594+
These are invalid redirect URIs:
1595+
- `example:/callback`
1596+
- `com.example.app://callback`
1597+
- `https://localhost/callback`
1598+
- `http://localhost:1234/callback`
1599+
1600+
##### Dynamic client registration flow
1601+
1602+
To register, the client sends an HTTP `POST` request to the
1603+
`registration_endpoint`, which can be found in the server metadata. The body of
1604+
the request is the JSON client metadata.
1605+
1606+
For example, the client could send the following registration request:
1607+
1608+
```http
1609+
POST /register HTTP/1.1
1610+
Content-Type: application/json
1611+
Accept: application/json
1612+
Server: auth.example.com
1613+
```
1614+
1615+
```json
1616+
{
1617+
"client_name": "My App",
1618+
"client_name#fr": "Mon application",
1619+
"client_uri": "https://example.com/",
1620+
"logo_uri": "https://example.com/logo.png",
1621+
"tos_uri": "https://example.com/tos.html",
1622+
"tos_uri#fr": "https://example.com/fr/tos.html",
1623+
"policy_uri": "https://example.com/policy.html",
1624+
"policy_uri#fr": "https://example.com/fr/policy.html",
1625+
"redirect_uris": ["https://app.example.com/callback"],
1626+
"token_endpoint_auth_method": "none",
1627+
"response_types": ["code"],
1628+
"grant_types": [
1629+
"authorization_code",
1630+
"refresh_token",
1631+
"urn:ietf:params:oauth:grant-type:token-exchange"
1632+
],
1633+
"application_type": "web"
1634+
}
1635+
```
1636+
1637+
Upon successful registration, the server replies with an `HTTP 201 Created`
1638+
response, with a JSON object containing the allocated `client_id` and all the
1639+
registered metadata values.
1640+
1641+
With the previous registration request, the server would reply with:
1642+
1643+
```json
1644+
{
1645+
"client_id": "s6BhdRkqt3",
1646+
"client_name": "My App",
1647+
"client_uri": "https://example.com/",
1648+
"logo_uri": "https://example.com/logo.png",
1649+
"tos_uri": "https://example.com/tos.html",
1650+
"policy_uri": "https://example.com/policy.html",
1651+
"redirect_uris": ["https://app.example.com/callback"],
1652+
"token_endpoint_auth_method": "none",
1653+
"response_types": ["code"],
1654+
"grant_types": ["authorization_code", "refresh_token"],
1655+
"application_type": "web"
1656+
}
1657+
```
1658+
1659+
In this example, the server has not registered the locale-specific values for
1660+
`client_name`, `tos_uri`, and `policy_uri`, which is why they are not present in
1661+
the response. The server also does not support the
1662+
`urn:ietf:params:oauth:grant-type:token-exchange` grant type, which is why it is
1663+
not present in the response.
1664+
1665+
The client MUST store the `client_id` for future use.
1666+
1667+
To avoid the number of client registrations growing over time, the server MAY
1668+
choose to delete client registrations that don't have an active session. The
1669+
server MUST NOT delete client registrations that have an active session.
1670+
1671+
Clients MUST perform a new client registration at the start of each
1672+
authorization flow.
1673+
1674+
{{% boxes/note %}}
1675+
Because each client on each user device will do its own registration, they may
1676+
all have different `client_id`s. This means that the server may store the same
1677+
client registration multiple times, which could lead to a large number of client
1678+
registrations.
1679+
1680+
This can be mitigated by de-duplicating client registrations that have identical
1681+
metadata. By doing so, different users on different devices using the same
1682+
client can share a single `client_id`, reducing the overall number of
1683+
registrations.
1684+
{{% /boxes/note %}}
1685+
14841686
### Account moderation
14851687

14861688
#### Account locking
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Copyright 2025 The Matrix.org Foundation C.I.C.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
title: OAuth 2.0 Client Server Metadata
15+
type: object
16+
description: |-
17+
This definition of the metadata specifies only the fields that are meaningful
18+
in the context of the Matrix specification. All the possible values are
19+
registered in the [OAuth Dynamic Client Registration Metadata registry](https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#client-metadata),
20+
and normative definitions of them are available in their respective RFCs.
21+
properties:
22+
client_uri:
23+
type: string
24+
format: uri
25+
description: |-
26+
A URL to a valid web page that SHOULD give the user more information about
27+
the client.
28+
29+
This URL MUST use the `https` scheme and SHOULD NOT require authentication
30+
to access. It MUST NOT use a user or password in the authority component
31+
of the URI.
32+
33+
The server MAY reject client registrations if this field is invalid or
34+
missing.
35+
36+
This URI is a common base for all the other URIs in the metadata: those
37+
MUST be either on the same host or on a subdomain of the host of the
38+
`client_uri`. The port number, path and query components MAY be different.
39+
40+
For example, if the `client_uri` is `https://example.com/`, then one of
41+
the `redirect_uris` can be `https://example.com/callback` or
42+
`https://app.example.com/callback`, but not `https://app.com/callback`.
43+
client_name:
44+
type: string
45+
description: |-
46+
Human-readable name of the client to be presented to the user.
47+
48+
This field can be [localized](/client-server-api/#metadata-localization).
49+
logo_uri:
50+
type: string
51+
format: uri
52+
description: |-
53+
URL that references a logo for the client.
54+
55+
This URL MUST use the `https` scheme.
56+
57+
This field can be [localized](/client-server-api/#metadata-localization).
58+
tos_uri:
59+
type: string
60+
format: uri
61+
description: |-
62+
URL that points to a human-readable terms of service document for the
63+
client.
64+
65+
This URL MUST use the `https` scheme and SHOULD NOT require authentication
66+
to access. It MUST NOT use a user or password in the authority component
67+
of the URI.
68+
69+
If this field is set, the server SHOULD show or link to this URL.
70+
71+
This field can be [localized](/client-server-api/#metadata-localization).
72+
policy_uri:
73+
type: string
74+
format: uri
75+
description: |-
76+
URL that points to a human-readable policy document for the client.
77+
78+
This URL MUST use the `https` scheme and SHOULD NOT require authentication
79+
to access. It MUST NOT use a user or password in the authority component
80+
of the URI.
81+
82+
If this field is set, the server SHOULD show or link to this URL.
83+
84+
This field can be [localized](/client-server-api/#metadata-localization).
85+
redirect_uris:
86+
type: array
87+
description: |-
88+
Array of redirection URIs for use in redirect-based flows.
89+
90+
At least one URI is required to use the authorization code grant.
91+
92+
The server MUST perform [validation on redirect URIs](/client-server-api/#redirect-uri-validation).
93+
items:
94+
type: string
95+
format: uri
96+
description: A redirection URI.
97+
response_types:
98+
type: array
99+
description: |-
100+
Array of the OAuth 2.0 response types that the client may use.
101+
102+
This MUST include the `code` value to use the authorization code grant.
103+
104+
The server MUST ignore values that it does not understand.
105+
items:
106+
type: string
107+
description: A response type that the client may use.
108+
grant_types:
109+
type: array
110+
description: |-
111+
Array of the OAuth 2.0 grant types that the client may use.
112+
113+
This MUST include:
114+
- the `authorization_code` value to use the authorization code grant,
115+
- the `refresh_token` value to use the refresh token grant.
116+
117+
The server MUST ignore values that it does not understand.
118+
items:
119+
type: string
120+
description: A grant type that the client may use.
121+
token_endpoint_auth_method:
122+
type: string
123+
description: |-
124+
String indicator of the requested authentication method for the token
125+
endpoint.
126+
127+
The homeserver MUST support the `none` value, as most Matrix clients are
128+
client-side only, do not have a server component, and therefore are public
129+
clients.
130+
application_type:
131+
type: string
132+
description: |-
133+
Kind of the application.
134+
135+
The homeserver MUST support the `web` and `native` values to be able to
136+
perform [redirect URI validation](/client-server-api/#redirect-uri-validation).
137+
138+
Defaults to `web` if omitted.
139+
required:
140+
- client_uri

0 commit comments

Comments
 (0)