Compare commits

...

5 commits

Author SHA1 Message Date
Kévin Commaille ef05ddcaf8
Merge bb54e7d303 into 9244c84a32 2025-06-19 14:50:46 +01: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
Kévin Commaille bb54e7d303
Add changelog
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2025-06-09 10:47:43 +02:00
Kévin Commaille f7f641aae7
Move and update layouts
A big change for template paths landed in Hugo 0.146.0.

In `layouts`, we:

- remove `_default` and move everything in it directly under `layouts`
- rename `partials` and `shortcodes` to `_partials` and `_shortcodes`
- adapt to Hugo and docsy changes about the render-heading hook.
  We don't need a copy of the heading self-link template now that it is
  defined as a partial.
- update `docs/baseof.html` to match a change upstream
- split `docs/changelog.html` because it doesn't work for the section
  page anymore. We create a `changelog-index` layout for this.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2025-06-09 10:44:27 +02:00
Kévin Commaille e5b9214bee
Bump docsy and Hugo
Docsy 0.12.0 requires at least Hugo 0.146.0 because of changes to
template paths.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2025-06-09 10:44:27 +02:00
60 changed files with 391 additions and 51 deletions

View file

@ -1,7 +1,7 @@
name: "Spec"
env:
HUGO_VERSION: 0.139.0
HUGO_VERSION: 0.147.8
PYTHON_VERSION: 3.13
on:

View file

@ -61,7 +61,7 @@ place after an MSC has been accepted, not as part of a proposal itself.
1. Install the extended version (often the OS default) of Hugo:
<https://gohugo.io/getting-started/installing>. Note that at least Hugo
v0.123.1 is required.
v0.146.0 is required.
Alternatively, use the Docker image at
https://hub.docker.com/r/klakegg/hugo/. (The "extended edition" is required

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

@ -0,0 +1 @@
Upgrade the docsy theme to version 0.12.0.

View file

@ -8,7 +8,7 @@ enableRobotsTXT = true
# We disable RSS, because (a) it's useless, (b) Hugo seems to generate broken
# links to it when used with a --baseURL (for example, https://spec.matrix.org/v1.4/
# contains `<link rel="alternate" type="application/rss&#43;xml" href="/v1.4/v1.4/index.xml">`).
disableKinds = ["taxonomy", "RSS"]
disableKinds = ["taxonomy", "rss"]
[languages]
[languages.en]
@ -134,7 +134,7 @@ sidebar_menu_compact = true
[module]
[module.hugoVersion]
extended = true
min = "0.123.1"
min = "0.146.0"
[[module.imports]]
path = "github.com/matrix-org/docsy"
disable = false

View file

@ -1,6 +1,7 @@
---
title: Changelog
type: docs
layout: changelog-index
weight: 1000
---

View file

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

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

2
go.mod
View file

@ -3,3 +3,5 @@ module github.com/matrix-org/matrix-spec
go 1.12
require github.com/matrix-org/docsy v0.0.0-20241106102557-ec7b98ee4014 // indirect
replace github.com/matrix-org/docsy => github.com/zecakeh/matrix-org-docsy v0.0.0-20250609083047-3b5e81464c1d

8
go.sum
View file

@ -1,4 +1,4 @@
github.com/FortAwesome/Font-Awesome v0.0.0-20240716171331-37eff7fa00de/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo=
github.com/matrix-org/docsy v0.0.0-20241106102557-ec7b98ee4014 h1:CNvxuuURuxkEjA0QN+lRKELc7PRDsX270e8v4GDF3II=
github.com/matrix-org/docsy v0.0.0-20241106102557-ec7b98ee4014/go.mod h1:4Ek1bcdbfU/j8hIatEjNhIs1Yua85FtQf3kLvoYZ0bQ=
github.com/twbs/bootstrap v5.3.3+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0=
github.com/FortAwesome/Font-Awesome v0.0.0-20241216213156-af620534bfc3/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo=
github.com/twbs/bootstrap v5.3.6+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0=
github.com/zecakeh/matrix-org-docsy v0.0.0-20250609083047-3b5e81464c1d h1:yO1JdmvpuDBWHxYVDhRZeKElcEWyzW5VmR1p6vcjQPA=
github.com/zecakeh/matrix-org-docsy v0.0.0-20250609083047-3b5e81464c1d/go.mod h1:4/t21g/nPraob/DVMm3jrk26k0CDL5I7Mxf+ar0IAgs=

View file

@ -1,11 +0,0 @@
{{- /*
A simplified copy of the inlined "_default/_markup/_td-heading-self-link.html"
template in Docsy's "_default/_markup/td-render-heading.html" template to be
able to reuse it when the heading has custom markup.
Takes a string which is the ID of the heading.
*/ -}}
<a class="td-heading-self-link" href="#{{ . | safeURL }}" aria-label="Heading self-link"></a>

View file

@ -8,4 +8,4 @@
heading.
*/ -}}
{{ template "_default/_markup/td-render-heading.html" . }}
{{ partial "td/render-heading.html" . }}

View file

@ -24,7 +24,7 @@
<h1 id="{{ $anchor }}">
{{ with .title }}{{ . | markdownify }}{{ else }}<code>{{ $event_name }}</code>{{ end }}
{{ template "_default/_markup/td-heading-self-link.html" $anchor }}
{{ partial "td/heading-self-link.html" (dict "Anchor" $anchor) }}
</h1>
</summary>

View file

@ -66,7 +66,7 @@
* If the input `schema` was itself an object that we should create a table for,
* this will be the same as the first entry in `objects`.
*/
{{ define "partials/resolve-additional-types-inner" }}
{{ define "_partials/resolve-additional-types-inner" }}
{{ $this_object := .schema }}
{{ $anchor_base := .anchor_base }}
{{ $name := .name }}
@ -222,7 +222,7 @@
* * `objects`: The array of object schema definitions.
* * `schema`: An updated copy of the `schema` input (eg, an `anchor` may be added).
*/
{{ define "partials/get-additional-objects" }}
{{ define "_partials/get-additional-objects" }}
/* .name is the name of the object for logging purposes */
{{ $name := .name }}
@ -246,6 +246,6 @@
* This is needed for uniqify to work - otherwise objects that are the same
* but with (for example) different examples will be considered different.
*/
{{ define "partials/clean-object" }}
{{ define "_partials/clean-object" }}
{{ return (dict "title" .title "properties" .properties "additionalProperties" .additionalProperties "patternProperties" .patternProperties "required" .required "enum" .enum "anchor" .anchor) }}
{{ end }}

View file

@ -80,7 +80,7 @@
</div>
</nav>
{{ define "partials/version-string" }}
{{ define "_partials/version-string" }}
{{ $ret := "unstable version"}}
{{ $status := .Site.Params.version.status }}

View file

@ -47,8 +47,8 @@
<tr>
<td><code>{{ $property_name }}</code></td>
<td><code>{{ partial "partials/property-type" $property | safeHTML }}</code></td>
<td>{{ partial "partials/property-description" (dict "property" $property "required" $required) }}</td>
<td><code>{{ partial "property-type" $property | safeHTML }}</code></td>
<td>{{ partial "property-description" (dict "property" $property "required" $required) }}</td>
</tr>
{{ end }}
@ -71,8 +71,8 @@
<tr>
<td>&lt;Other properties&gt;</td>
<td><code>{{ partial "partials/property-type" .additionalProperties | safeHTML }}</code></td>
<td>{{ partial "partials/property-description" (dict "property" .additionalProperties) }}</td>
<td><code>{{ partial "property-type" .additionalProperties | safeHTML }}</code></td>
<td>{{ partial "property-description" (dict "property" .additionalProperties) }}</td>
</tr>
{{ end }}
</table>
@ -101,8 +101,8 @@ resolve-additional-types.)
{{ $property := . }}
<tr>
<td><code>{{ partial "partials/property-type" $property | safeHTML }}</code></td>
<td>{{ partial "partials/property-description" (dict "property" $property) }}</td>
<td><code>{{ partial "property-type" $property | safeHTML }}</code></td>
<td>{{ partial "property-description" (dict "property" $property) }}</td>
</tr>
</table>
@ -138,7 +138,7 @@ resolve-additional-types.)
* `format`: optional string for the format of the type, used for strings.
*/}}
{{ define "partials/property-type" }}
{{ define "_partials/property-type" }}
{{ $type := "" }}
{{ if eq .type "object" }}
@ -215,7 +215,7 @@ resolve-additional-types.)
* `anchor`: optional HTML element id for the target type, which will be used to link to it.
*/}}
{{ define "partials/object-type-or-title" }}
{{ define "_partials/object-type-or-title" }}
{{ $type := "object" }}
{{ if .properties }}
{{/*
@ -306,7 +306,7 @@ resolve-additional-types.)
* `x-changedInMatrixVersion`: optional string indicating in which Matrix
spec version this property was last changed.
*/}}
{{ define "partials/property-description" -}}
{{ define "_partials/property-description" -}}
{{ $description := .property.description -}}
{{ if .required -}}
{{/*
@ -331,7 +331,7 @@ resolve-additional-types.)
Computes the type to display for a string format, given the identifier of
the format as a string.
*/}}
{{ define "partials/string-format" }}
{{ define "_partials/string-format" }}
{{ $stringFormat := "" }}
{{ with index site.Data "string-formats" . }}

View file

@ -37,7 +37,7 @@
<h1 id="{{ $anchor }}">
<span class="http-api-method">{{ $method }}</span>
<span class="endpoint{{ if $operation_data.deprecated }} deprecated-inline{{ end }}">{{ $endpoint }}</span>
{{ template "_default/_markup/td-heading-self-link.html" $anchor }}
{{ partial "td/heading-self-link.html" (dict "Anchor" $anchor) }}
</h1>
</summary>

View file

@ -25,7 +25,9 @@
</aside>
<main class="col-12 col-md-9 col-xl-8 ps-md-5" role="main">
{{ partial "version-banner.html" . }}
{{ if not .Site.Params.ui.breadcrumb_disable }}{{ partial "breadcrumb.html" . }}{{ end }}
{{ if not (.Param "ui.breadcrumb_disable") -}}
{{ partial "breadcrumb.html" . -}}
{{ end -}}
{{ block "main" . }}{{ end }}
</main>
</div>

View file

@ -0,0 +1,13 @@
{{- /*
Template to the `changelog` section page.
This redirects the page to the latest version's changelog page.
*/ -}}
{{ define "main" }}
{{ with index .RegularPages.ByDate.Reverse 0 -}}
<meta http-equiv="refresh" content="0; url={{ .RelPermalink }}">
{{ end -}}
{{ end }}

View file

@ -1,14 +1,8 @@
{{- /*
Template to render a page with a `changelog` layout or the `changelog`
section page. This conflation seems to be a limitation of Hugo currently, it
uses this template for both cases.
Template to render a page with a `changelog` layout.
For the `changelog` section page, this redirects the page to the latest
version's changelog page.
For a page with a `changelog` layout, this adds a table at the top of the
page with information about the release:
This adds a table at the top of the page with information about the release:
* A link to the matrix-spec repository at the time of the release, with the
version taken from the `linkTitle` in the frontmatter of the page, unless
@ -21,11 +15,6 @@
*/ -}}
{{ define "main" }}
{{ if .IsSection -}}
{{ with index .RegularPages.ByDate.Reverse 0 -}}
<meta http-equiv="refresh" content="0; url={{ .RelPermalink }}">
{{ end -}}
{{ else -}}
{{ $version := lower .LinkTitle -}}
<div class="td-content">
<h1>{{ .Title }}</h1>
@ -50,5 +39,4 @@
{{ .Content }}
</div>
{{ end -}}
{{ end }}